corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006-2014, openmetaverse.org
3 * All rights reserved.
4 *
5 * - Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26  
27 using System;
28 using System.Collections.Generic;
29 using System.Text;
30 using OpenMetaverse.Packets;
31 using System.IO;
32 using System.Reflection;
33 using OpenMetaverse.StructuredData;
34 using ComponentAce.Compression.Libs.zlib;
35  
36 namespace OpenMetaverse
37 {
38 /// <summary>
39 /// Static helper functions and global variables
40 /// </summary>
41 public static class Helpers
42 {
43 /// <summary>This header flag signals that ACKs are appended to the packet</summary>
44 public const byte MSG_APPENDED_ACKS = 0x10;
45 /// <summary>This header flag signals that this packet has been sent before</summary>
46 public const byte MSG_RESENT = 0x20;
47 /// <summary>This header flags signals that an ACK is expected for this packet</summary>
48 public const byte MSG_RELIABLE = 0x40;
49 /// <summary>This header flag signals that the message is compressed using zerocoding</summary>
50 public const byte MSG_ZEROCODED = 0x80;
51  
52 /// <summary>
53 /// Passed to Logger.Log() to identify the severity of a log entry
54 /// </summary>
55 public enum LogLevel
56 {
57 /// <summary>No logging information will be output</summary>
58 None,
59 /// <summary>Non-noisy useful information, may be helpful in
60 /// debugging a problem</summary>
61 Info,
62 /// <summary>A non-critical error occurred. A warning will not
63 /// prevent the rest of the library from operating as usual,
64 /// although it may be indicative of an underlying issue</summary>
65 Warning,
66 /// <summary>A critical error has occurred. Generally this will
67 /// be followed by the network layer shutting down, although the
68 /// stability of the library after an error is uncertain</summary>
69 Error,
70 /// <summary>Used for internal testing, this logging level can
71 /// generate very noisy (long and/or repetitive) messages. Don't
72 /// pass this to the Log() function, use DebugLog() instead.
73 /// </summary>
74 Debug
75 };
76  
77 /// <summary>
78 ///
79 /// </summary>
80 /// <param name="offset"></param>
81 /// <returns></returns>
82 public static short TEOffsetShort(float offset)
83 {
84 offset = Utils.Clamp(offset, -1.0f, 1.0f);
85 offset *= 32767.0f;
86 return (short)Math.Round(offset);
87 }
88  
89 /// <summary>
90 ///
91 /// </summary>
92 /// <param name="bytes"></param>
93 /// <param name="pos"></param>
94 /// <returns></returns>
95 public static float TEOffsetFloat(byte[] bytes, int pos)
96 {
97 float offset = (float)BitConverter.ToInt16(bytes, pos);
98 return offset / 32767.0f;
99 }
100  
101 /// <summary>
102 ///
103 /// </summary>
104 /// <param name="rotation"></param>
105 /// <returns></returns>
106 public static short TERotationShort(float rotation)
107 {
108 const float TWO_PI = 6.283185307179586476925286766559f;
109 return (short)Math.Round(((Math.IEEERemainder(rotation, TWO_PI) / TWO_PI) * 32768.0f) + 0.5f);
110 }
111  
112 /// <summary>
113 ///
114 /// </summary>
115 /// <param name="bytes"></param>
116 /// <param name="pos"></param>
117 /// <returns></returns>
118 public static float TERotationFloat(byte[] bytes, int pos)
119 {
120 const float TWO_PI = 6.283185307179586476925286766559f;
121 return ((float)(bytes[pos] | (bytes[pos + 1] << 8)) / 32768.0f) * TWO_PI;
122 }
123  
124 public static byte TEGlowByte(float glow)
125 {
126 return (byte)(glow * 255.0f);
127 }
128  
129 public static float TEGlowFloat(byte[] bytes, int pos)
130 {
131 return (float)bytes[pos] / 255.0f;
132 }
133  
134 /// <summary>
135 /// Given an X/Y location in absolute (grid-relative) terms, a region
136 /// handle is returned along with the local X/Y location in that region
137 /// </summary>
138 /// <param name="globalX">The absolute X location, a number such as
139 /// 255360.35</param>
140 /// <param name="globalY">The absolute Y location, a number such as
141 /// 255360.35</param>
142 /// <param name="localX">The sim-local X position of the global X
143 /// position, a value from 0.0 to 256.0</param>
144 /// <param name="localY">The sim-local Y position of the global Y
145 /// position, a value from 0.0 to 256.0</param>
146 /// <returns>A 64-bit region handle that can be used to teleport to</returns>
147 public static ulong GlobalPosToRegionHandle(float globalX, float globalY, out float localX, out float localY)
148 {
149 uint x = ((uint)globalX / 256) * 256;
150 uint y = ((uint)globalY / 256) * 256;
151 localX = globalX - (float)x;
152 localY = globalY - (float)y;
153 return Utils.UIntsToLong(x, y);
154 }
155  
156 /// <summary>
157 /// Converts a floating point number to a terse string format used for
158 /// transmitting numbers in wearable asset files
159 /// </summary>
160 /// <param name="val">Floating point number to convert to a string</param>
161 /// <returns>A terse string representation of the input number</returns>
162 public static string FloatToTerseString(float val)
163 {
164 string s = string.Format(Utils.EnUsCulture, "{0:.00}", val);
165  
166 if (val == 0)
167 return ".00";
168  
169 // Trim trailing zeroes
170 while (s[s.Length - 1] == '0')
171 s = s.Remove(s.Length - 1, 1);
172  
173 // Remove superfluous decimal places after the trim
174 if (s[s.Length - 1] == '.')
175 s = s.Remove(s.Length - 1, 1);
176 // Remove leading zeroes after a negative sign
177 else if (s[0] == '-' && s[1] == '0')
178 s = s.Remove(1, 1);
179 // Remove leading zeroes in positive numbers
180 else if (s[0] == '0')
181 s = s.Remove(0, 1);
182  
183 return s;
184 }
185  
186 /// <summary>
187 /// Convert a variable length field (byte array) to a string, with a
188 /// field name prepended to each line of the output
189 /// </summary>
190 /// <remarks>If the byte array has unprintable characters in it, a
191 /// hex dump will be written instead</remarks>
192 /// <param name="output">The StringBuilder object to write to</param>
193 /// <param name="bytes">The byte array to convert to a string</param>
194 /// <param name="fieldName">A field name to prepend to each line of output</param>
195 internal static void FieldToString(StringBuilder output, byte[] bytes, string fieldName)
196 {
197 // Check for a common case
198 if (bytes.Length == 0) return;
199  
200 bool printable = true;
201  
202 for (int i = 0; i < bytes.Length; ++i)
203 {
204 // Check if there are any unprintable characters in the array
205 if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09
206 && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00)
207 {
208 printable = false;
209 break;
210 }
211 }
212  
213 if (printable)
214 {
215 if (fieldName.Length > 0)
216 {
217 output.Append(fieldName);
218 output.Append(": ");
219 }
220  
221 if (bytes[bytes.Length - 1] == 0x00)
222 output.Append(UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length - 1));
223 else
224 output.Append(UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length));
225 }
226 else
227 {
228 for (int i = 0; i < bytes.Length; i += 16)
229 {
230 if (i != 0)
231 output.Append('\n');
232 if (fieldName.Length > 0)
233 {
234 output.Append(fieldName);
235 output.Append(": ");
236 }
237  
238 for (int j = 0; j < 16; j++)
239 {
240 if ((i + j) < bytes.Length)
241 output.Append(String.Format("{0:X2} ", bytes[i + j]));
242 else
243 output.Append(" ");
244 }
245 }
246 }
247 }
248  
249 /// <summary>
250 /// Decode a zerocoded byte array, used to decompress packets marked
251 /// with the zerocoded flag
252 /// </summary>
253 /// <remarks>Any time a zero is encountered, the next byte is a count
254 /// of how many zeroes to expand. One zero is encoded with 0x00 0x01,
255 /// two zeroes is 0x00 0x02, three zeroes is 0x00 0x03, etc. The
256 /// first four bytes are copied directly to the output buffer.
257 /// </remarks>
258 /// <param name="src">The byte array to decode</param>
259 /// <param name="srclen">The length of the byte array to decode. This
260 /// would be the length of the packet up to (but not including) any
261 /// appended ACKs</param>
262 /// <param name="dest">The output byte array to decode to</param>
263 /// <returns>The length of the output buffer</returns>
264 public static int ZeroDecode(byte[] src, int srclen, byte[] dest)
265 {
266 if (srclen > src.Length)
267 throw new ArgumentException("srclen cannot be greater than src.Length");
268  
269 uint zerolen = 0;
270 int bodylen = 0;
271 uint i = 0;
272  
273 try
274 {
275 Buffer.BlockCopy(src, 0, dest, 0, 6);
276 zerolen = 6;
277 bodylen = srclen;
278  
279 for (i = zerolen; i < bodylen; i++)
280 {
281 if (src[i] == 0x00)
282 {
283 for (byte j = 0; j < src[i + 1]; j++)
284 {
285 dest[zerolen++] = 0x00;
286 }
287  
288 i++;
289 }
290 else
291 {
292 dest[zerolen++] = src[i];
293 }
294 }
295  
296 // Copy appended ACKs
297 for (; i < srclen; i++)
298 {
299 dest[zerolen++] = src[i];
300 }
301  
302 return (int)zerolen;
303 }
304 catch (Exception ex)
305 {
306 Logger.Log(String.Format("Zerodecoding error: i={0}, srclen={1}, bodylen={2}, zerolen={3}\n{4}\n{5}",
307 i, srclen, bodylen, zerolen, Utils.BytesToHexString(src, srclen, null), ex), LogLevel.Error);
308  
309 throw new IndexOutOfRangeException(String.Format("Zerodecoding error: i={0}, srclen={1}, bodylen={2}, zerolen={3}\n{4}\n{5}",
310 i, srclen, bodylen, zerolen, Utils.BytesToHexString(src, srclen, null), ex.InnerException));
311 }
312 }
313  
314 /// <summary>
315 /// Encode a byte array with zerocoding. Used to compress packets marked
316 /// with the zerocoded flag. Any zeroes in the array are compressed down
317 /// to a single zero byte followed by a count of how many zeroes to expand
318 /// out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02,
319 /// three zeroes becomes 0x00 0x03, etc. The first four bytes are copied
320 /// directly to the output buffer.
321 /// </summary>
322 /// <param name="src">The byte array to encode</param>
323 /// <param name="srclen">The length of the byte array to encode</param>
324 /// <param name="dest">The output byte array to encode to</param>
325 /// <returns>The length of the output buffer</returns>
326 public static int ZeroEncode(byte[] src, int srclen, byte[] dest)
327 {
328 uint zerolen = 0;
329 byte zerocount = 0;
330  
331 Buffer.BlockCopy(src, 0, dest, 0, 6);
332 zerolen += 6;
333  
334 int bodylen;
335 if ((src[0] & MSG_APPENDED_ACKS) == 0)
336 {
337 bodylen = srclen;
338 }
339 else
340 {
341 bodylen = srclen - src[srclen - 1] * 4 - 1;
342 }
343  
344 uint i;
345 for (i = zerolen; i < bodylen; i++)
346 {
347 if (src[i] == 0x00)
348 {
349 zerocount++;
350  
351 if (zerocount == 0)
352 {
353 dest[zerolen++] = 0x00;
354 dest[zerolen++] = 0xff;
355 zerocount++;
356 }
357 }
358 else
359 {
360 if (zerocount != 0)
361 {
362 dest[zerolen++] = 0x00;
363 dest[zerolen++] = (byte)zerocount;
364 zerocount = 0;
365 }
366  
367 dest[zerolen++] = src[i];
368 }
369 }
370  
371 if (zerocount != 0)
372 {
373 dest[zerolen++] = 0x00;
374 dest[zerolen++] = (byte)zerocount;
375 }
376  
377 // copy appended ACKs
378 for (; i < srclen; i++)
379 {
380 dest[zerolen++] = src[i];
381 }
382  
383 return (int)zerolen;
384 }
385  
386 /// <summary>
387 /// Calculates the CRC (cyclic redundancy check) needed to upload inventory.
388 /// </summary>
389 /// <param name="creationDate">Creation date</param>
390 /// <param name="saleType">Sale type</param>
391 /// <param name="invType">Inventory type</param>
392 /// <param name="type">Type</param>
393 /// <param name="assetID">Asset ID</param>
394 /// <param name="groupID">Group ID</param>
395 /// <param name="salePrice">Sale price</param>
396 /// <param name="ownerID">Owner ID</param>
397 /// <param name="creatorID">Creator ID</param>
398 /// <param name="itemID">Item ID</param>
399 /// <param name="folderID">Folder ID</param>
400 /// <param name="everyoneMask">Everyone mask (permissions)</param>
401 /// <param name="flags">Flags</param>
402 /// <param name="nextOwnerMask">Next owner mask (permissions)</param>
403 /// <param name="groupMask">Group mask (permissions)</param>
404 /// <param name="ownerMask">Owner mask (permissions)</param>
405 /// <returns>The calculated CRC</returns>
406 public static uint InventoryCRC(int creationDate, byte saleType, sbyte invType, sbyte type,
407 UUID assetID, UUID groupID, int salePrice, UUID ownerID, UUID creatorID,
408 UUID itemID, UUID folderID, uint everyoneMask, uint flags, uint nextOwnerMask,
409 uint groupMask, uint ownerMask)
410 {
411 uint CRC = 0;
412  
413 // IDs
414 CRC += assetID.CRC(); // AssetID
415 CRC += folderID.CRC(); // FolderID
416 CRC += itemID.CRC(); // ItemID
417  
418 // Permission stuff
419 CRC += creatorID.CRC(); // CreatorID
420 CRC += ownerID.CRC(); // OwnerID
421 CRC += groupID.CRC(); // GroupID
422  
423 // CRC += another 4 words which always seem to be zero -- unclear if this is a UUID or what
424 CRC += ownerMask;
425 CRC += nextOwnerMask;
426 CRC += everyoneMask;
427 CRC += groupMask;
428  
429 // The rest of the CRC fields
430 CRC += flags; // Flags
431 CRC += (uint)invType; // InvType
432 CRC += (uint)type; // Type
433 CRC += (uint)creationDate; // CreationDate
434 CRC += (uint)salePrice; // SalePrice
435 CRC += (uint)((uint)saleType * 0x07073096); // SaleType
436  
437 return CRC;
438 }
439  
440 /// <summary>
441 /// Attempts to load a file embedded in the assembly
442 /// </summary>
443 /// <param name="resourceName">The filename of the resource to load</param>
444 /// <returns>A Stream for the requested file, or null if the resource
445 /// was not successfully loaded</returns>
446 public static System.IO.Stream GetResourceStream(string resourceName)
447 {
448 return GetResourceStream(resourceName, "openmetaverse_data");
449 }
450  
451 /// <summary>
452 /// Attempts to load a file either embedded in the assembly or found in
453 /// a given search path
454 /// </summary>
455 /// <param name="resourceName">The filename of the resource to load</param>
456 /// <param name="searchPath">An optional path that will be searched if
457 /// the asset is not found embedded in the assembly</param>
458 /// <returns>A Stream for the requested file, or null if the resource
459 /// was not successfully loaded</returns>
460 public static System.IO.Stream GetResourceStream(string resourceName, string searchPath)
461 {
462 if (searchPath != null)
463 {
464 Assembly gea = Assembly.GetEntryAssembly();
465 if (gea == null) gea = typeof(Helpers).Assembly;
466 string dirname = ".";
467 if (gea != null && gea.Location != null)
468 {
469 dirname = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(gea.Location), searchPath);
470 }
471  
472 string filename = System.IO.Path.Combine(dirname, resourceName);
473 try
474 {
475 return new System.IO.FileStream(
476 filename,
477 System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
478 }
479 catch (Exception ex)
480 {
481 Logger.Log(string.Format("Failed opening resource from file {0}: {1}", filename, ex.Message), LogLevel.Error);
482 }
483 }
484 else
485 {
486 try
487 {
488 System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly();
489 System.IO.Stream s = a.GetManifestResourceStream("OpenMetaverse.Resources." + resourceName);
490 if (s != null) return s;
491 }
492 catch (Exception ex)
493 {
494 Logger.Log(string.Format("Failed opening resource stream: {0}", ex.Message), LogLevel.Error);
495 }
496 }
497  
498 return null;
499 }
500 /// <summary>
501 /// Converts a list of primitives to an object that can be serialized
502 /// with the LLSD system
503 /// </summary>
504 /// <param name="prims">Primitives to convert to a serializable object</param>
505 /// <returns>An object that can be serialized with LLSD</returns>
506 public static StructuredData.OSD PrimListToOSD(List<Primitive> prims)
507 {
508 StructuredData.OSDMap map = new OpenMetaverse.StructuredData.OSDMap(prims.Count);
509  
510 for (int i = 0; i < prims.Count; i++)
511 map.Add(prims[i].LocalID.ToString(), prims[i].GetOSD());
512  
513 return map;
514 }
515  
516 /// <summary>
517 /// Deserializes OSD in to a list of primitives
518 /// </summary>
519 /// <param name="osd">Structure holding the serialized primitive list,
520 /// must be of the SDMap type</param>
521 /// <returns>A list of deserialized primitives</returns>
522 public static List<Primitive> OSDToPrimList(StructuredData.OSD osd)
523 {
524 if (osd.Type != StructuredData.OSDType.Map)
525 throw new ArgumentException("LLSD must be in the Map structure");
526  
527 StructuredData.OSDMap map = (StructuredData.OSDMap)osd;
528 List<Primitive> prims = new List<Primitive>(map.Count);
529  
530 foreach (KeyValuePair<string, StructuredData.OSD> kvp in map)
531 {
532 Primitive prim = Primitive.FromOSD(kvp.Value);
533 prim.LocalID = UInt32.Parse(kvp.Key);
534 prims.Add(prim);
535 }
536  
537 return prims;
538 }
539  
540 /// <summary>
541 /// Converts a struct or class object containing fields only into a key value separated string
542 /// </summary>
543 /// <param name="t">The struct object</param>
544 /// <returns>A string containing the struct fields as the keys, and the field value as the value separated</returns>
545 /// <example>
546 /// <code>
547 /// // Add the following code to any struct or class containing only fields to override the ToString()
548 /// // method to display the values of the passed object
549 ///
550 /// /// <summary>Print the struct data as a string</summary>
551 /// ///<returns>A string containing the field name, and field value</returns>
552 ///public override string ToString()
553 ///{
554 /// return Helpers.StructToString(this);
555 ///}
556 /// </code>
557 /// </example>
558 public static string StructToString(object t)
559 {
560 StringBuilder result = new StringBuilder();
561 Type structType = t.GetType();
562 FieldInfo[] fields = structType.GetFields();
563  
564 foreach (FieldInfo field in fields)
565 {
566 result.Append(field.Name + ": " + field.GetValue(t) + " ");
567 }
568 result.AppendLine();
569 return result.ToString().TrimEnd();
570 }
571  
572 public static void CopyStream(Stream input, Stream output)
573 {
574 byte[] buffer = new byte[4096];
575 int read;
576 while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
577 {
578 output.Write(buffer, 0, read);
579 }
580 }
581  
582 public static byte[] ZCompressOSD(OSD data)
583 {
584 byte[] ret = null;
585  
586 using (MemoryStream outMemoryStream = new MemoryStream())
587 using (ZOutputStream outZStream = new ZOutputStream(outMemoryStream, zlibConst.Z_BEST_COMPRESSION))
588 using (Stream inMemoryStream = new MemoryStream(OSDParser.SerializeLLSDBinary(data, false)))
589 {
590 CopyStream(inMemoryStream, outZStream);
591 outZStream.finish();
592 ret = outMemoryStream.ToArray();
593 }
594  
595 return ret;
596 }
597  
598 public static OSD ZDecompressOSD(byte[] data)
599 {
600 OSD ret;
601  
602 using (MemoryStream input = new MemoryStream(data))
603 using (MemoryStream output = new MemoryStream())
604 using (ZOutputStream zout = new ZOutputStream(output))
605 {
606 CopyStream(input, zout);
607 zout.finish();
608 output.Seek(0, SeekOrigin.Begin);
609 ret = OSDParser.DeserializeLLSDBinary(output);
610 }
611  
612 return ret;
613 }
614 }
615 }