corrade-vassal – Blame information for rev 1
?pathlinks?
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 | } |