diff --git a/libsecondlife-cs/Helpers.cs b/libsecondlife-cs/Helpers.cs
index 67eff058..3cda5b85 100644
--- a/libsecondlife-cs/Helpers.cs
+++ b/libsecondlife-cs/Helpers.cs
@@ -25,489 +25,506 @@
*/
using System;
+using System.Collections.Generic;
+using System.Xml;
+using System.Xml.Serialization;
using System.Text;
+using libsecondlife.Packets;
namespace libsecondlife
{
-///
-/// Static helper functions and global variables
-///
-public class Helpers
-{
- /// The version of libsecondlife (not the SL protocol itself)
- public readonly static string VERSION = "libsecondlife 0.0.9";
- /// This header flag signals that ACKs are appended to the packet
- public const byte MSG_APPENDED_ACKS = 0x10;
- /// This header flag signals that this packet has been sent before
- public const byte MSG_RESENT = 0x20;
- /// This header flags signals that an ACK is expected for this packet
- public const byte MSG_RELIABLE = 0x40;
- /// This header flag signals that the message is compressed using zerocoding
- public const byte MSG_ZEROCODED = 0x80;
-
///
- /// Passed to SecondLife.Log() to identify the severity of a log entry
+ /// Static helper functions and global variables
///
- public enum LogLevel
+ public class Helpers
{
- /// Non-noisy useful information, may be helpful in
- /// debugging a problem
- Info,
- /// A non-critical error occurred. A warning will not
- /// prevent the rest of libsecondlife from operating as usual,
- /// although it may be indicative of an underlying issue
- Warning,
- /// A critical error has occurred. Generally this will
- /// be followed by the network layer shutting down, although the
- /// stability of libsecondlife after an error is uncertain
- Error,
- /// Used for internal testing, this logging level can
- /// generate very noisy (long and/or repetitive) messages. Don't
- /// pass this to the Log() function, use DebugLog() instead.
+ /// The version of libsecondlife (not the SL protocol itself)
+ public readonly static string VERSION = "libsecondlife 0.0.9";
+ /// This header flag signals that ACKs are appended to the packet
+ public const byte MSG_APPENDED_ACKS = 0x10;
+ /// This header flag signals that this packet has been sent before
+ public const byte MSG_RESENT = 0x20;
+ /// This header flags signals that an ACK is expected for this packet
+ public const byte MSG_RELIABLE = 0x40;
+ /// This header flag signals that the message is compressed using zerocoding
+ public const byte MSG_ZEROCODED = 0x80;
+
+ ///
+ /// Passed to SecondLife.Log() to identify the severity of a log entry
///
- Debug
- };
-
- ///
- ///
- ///
- [Flags]
- public enum PermissionWho
- {
- ///
- Group = 4,
- ///
- Everyone = 8,
- ///
- NextOwner = 16
- }
-
- ///
- ///
- ///
- [Flags]
- public enum PermissionType
- {
- ///
- Copy = 0x00008000,
- ///
- Modify = 0x00004000,
- ///
- Move = 0x00080000,
- ///
- Transfer = 0x00002000
- }
-
- ///
- /// Packs to 32-bit unsigned integers in to a 64-bit unsigned integer
- ///
- /// The left-hand (or X) value
- /// The right-hand (or Y) value
- /// A 64-bit integer containing the two 32-bit input values
- public static ulong UIntsToLong(uint a, uint b)
- {
- return (ulong)(((ulong)a << 32) + (ulong)b);
- }
-
- ///
- /// Unpacks two 32-bit unsigned integers from a 64-bit unsigned integer
- ///
- /// The 64-bit input integer
- /// The left-hand (or X) output value
- /// The right-hand (or Y) output value
- public static void LongToUInts(ulong a, out uint b, out uint c)
- {
- b = (uint)(a >> 32);
- c = (uint)(a & 0x00000000FFFFFFFF);
- }
-
- ///
- /// Convert a variable length field (byte array) to a string.
- ///
- /// If the byte array has unprintable characters in it, a
- /// hex dump will be put in the string instead
- /// The byte array to convert to a string
- /// A UTF8 string, minus the null terminator
- public static string FieldToString(byte[] bytes)
- {
- return FieldToString(bytes, "");
- }
-
- ///
- /// Convert a variable length field (byte array) to a string, with a
- /// field name prepended to each line of the output.
- ///
- /// If the byte array has unprintable characters in it, a
- /// hex dump will be put in the string instead
- /// The byte array to convert to a string
- /// A field name to prepend to each line of output
- /// A UTF8 string, minus the null terminator
- public static string FieldToString(byte[] bytes, string fieldName)
- {
- string output = "";
- bool printable = true;
-
- for (int i = 0; i < bytes.Length; ++i)
+ public enum LogLevel
{
- // Check if there are any unprintable characters in the array
- if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09
- && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00)
- {
- printable = false;
- break;
- }
+ /// Non-noisy useful information, may be helpful in
+ /// debugging a problem
+ Info,
+ /// A non-critical error occurred. A warning will not
+ /// prevent the rest of libsecondlife from operating as usual,
+ /// although it may be indicative of an underlying issue
+ Warning,
+ /// A critical error has occurred. Generally this will
+ /// be followed by the network layer shutting down, although the
+ /// stability of libsecondlife after an error is uncertain
+ Error,
+ /// Used for internal testing, this logging level can
+ /// generate very noisy (long and/or repetitive) messages. Don't
+ /// pass this to the Log() function, use DebugLog() instead.
+ ///
+ Debug
+ };
+
+ ///
+ ///
+ ///
+ [Flags]
+ public enum PermissionWho
+ {
+ ///
+ Group = 4,
+ ///
+ Everyone = 8,
+ ///
+ NextOwner = 16
}
- if (printable)
+ ///
+ ///
+ ///
+ [Flags]
+ public enum PermissionType
{
- if (fieldName.Length > 0)
- {
- output += fieldName + ": ";
- }
-
- output += System.Text.Encoding.UTF8.GetString(bytes).Replace("\0", "");
+ ///
+ Copy = 0x00008000,
+ ///
+ Modify = 0x00004000,
+ ///
+ Move = 0x00080000,
+ ///
+ Transfer = 0x00002000
}
- else
- {
- for (int i = 0; i < bytes.Length; i += 16)
- {
- if (i != 0) { output += "\n"; }
- if (fieldName != "") { output += fieldName + ": "; }
- for (int j = 0; j < 16; j++)
+ ///
+ /// Packs to 32-bit unsigned integers in to a 64-bit unsigned integer
+ ///
+ /// The left-hand (or X) value
+ /// The right-hand (or Y) value
+ /// A 64-bit integer containing the two 32-bit input values
+ public static ulong UIntsToLong(uint a, uint b)
+ {
+ return (ulong)(((ulong)a << 32) + (ulong)b);
+ }
+
+ ///
+ /// Unpacks two 32-bit unsigned integers from a 64-bit unsigned integer
+ ///
+ /// The 64-bit input integer
+ /// The left-hand (or X) output value
+ /// The right-hand (or Y) output value
+ public static void LongToUInts(ulong a, out uint b, out uint c)
+ {
+ b = (uint)(a >> 32);
+ c = (uint)(a & 0x00000000FFFFFFFF);
+ }
+
+ ///
+ /// Convert a variable length field (byte array) to a string.
+ ///
+ /// If the byte array has unprintable characters in it, a
+ /// hex dump will be put in the string instead
+ /// The byte array to convert to a string
+ /// A UTF8 string, minus the null terminator
+ public static string FieldToString(byte[] bytes)
+ {
+ return FieldToString(bytes, "");
+ }
+
+ ///
+ /// Convert a variable length field (byte array) to a string, with a
+ /// field name prepended to each line of the output.
+ ///
+ /// If the byte array has unprintable characters in it, a
+ /// hex dump will be put in the string instead
+ /// The byte array to convert to a string
+ /// A field name to prepend to each line of output
+ /// A UTF8 string, minus the null terminator
+ public static string FieldToString(byte[] bytes, string fieldName)
+ {
+ string output = "";
+ bool printable = true;
+
+ for (int i = 0; i < bytes.Length; ++i)
+ {
+ // Check if there are any unprintable characters in the array
+ if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09
+ && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00)
{
- if ((i + j) < bytes.Length)
+ printable = false;
+ break;
+ }
+ }
+
+ if (printable)
+ {
+ if (fieldName.Length > 0)
+ {
+ output += fieldName + ": ";
+ }
+
+ output += System.Text.Encoding.UTF8.GetString(bytes).Replace("\0", "");
+ }
+ else
+ {
+ for (int i = 0; i < bytes.Length; i += 16)
+ {
+ if (i != 0) { output += "\n"; }
+ if (fieldName != "") { output += fieldName + ": "; }
+
+ for (int j = 0; j < 16; j++)
{
- string s = String.Format("{0:X} ", bytes[i + j]);
- if (s.Length == 2)
+ if ((i + j) < bytes.Length)
{
- s = "0" + s;
+ string s = String.Format("{0:X} ", bytes[i + j]);
+ if (s.Length == 2)
+ {
+ s = "0" + s;
+ }
+
+ output += s;
}
+ else
+ {
+ output += " ";
+ }
+ }
- output += s;
- }
- else
+ for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
{
- output += " ";
- }
- }
-
- for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
- {
- if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
- {
- output += (char)bytes[i + j];
- }
- else
- {
- output += ".";
+ if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
+ {
+ output += (char)bytes[i + j];
+ }
+ else
+ {
+ output += ".";
+ }
}
}
}
+
+ return output;
}
- return output;
- }
-
- ///
- /// Convert a UTF8 string to a byte array
- ///
- /// The string to convert to a byte array
- /// A null-terminated byte array
- public static byte[] StringToField(string str)
- {
- if (!str.EndsWith("\0")) { str += "\0"; }
- return System.Text.UTF8Encoding.UTF8.GetBytes(str);
- }
-
- public static uint GetUnixTime()
- {
- return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
- }
-
- ///
- /// Calculates the distance between two vectors
- ///
- public static float VecDist(LLVector3 pointA, LLVector3 pointB)
- {
- float xd = pointB.X - pointA.X;
- float yd = pointB.Y - pointA.Y;
- float zd = pointB.Z - pointA.Z;
- return (float)Math.Sqrt(xd * xd + yd * yd + zd * zd);
- }
-
- ///
- /// Calculate the magnitude of the supplied vector
- ///
- public static float VecMag(LLVector3 vector)
- {
- return (float)Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z);
- }
-
- ///
- /// Return the supplied vector in normalized form
- ///
- public static LLVector3 VecNorm(LLVector3 vector)
- {
- float mag = VecMag(vector);
- return new LLVector3(vector.X/mag,vector.Y/mag,vector.Z/mag);
- }
-
- ///
- /// Calculate the rotation between two vectors
- ///
- /// Directional vector, such as 1,0,0 for the forward face
- /// Target vector - normalize first with VecNorm
- public static LLQuaternion RotBetween(LLVector3 a, LLVector3 b)
- {
- //A and B should both be normalized
- //dotProduct is 0 if a and b are perpendicular. I think that's normal?
- float dotProduct = (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z);
-
- LLVector3 crossProduct = new LLVector3();
- crossProduct.X = a.Y * b.Z - a.Z * b.Y;
- crossProduct.Y = a.Z * b.X - a.X * b.Z;
- crossProduct.Z = a.X * b.Y - a.Y * b.X;
-
- //float scalarProduct = (a.X * b.Y) + (a.Y * b.Z) + (a.Z * b.X); //not used?
- float magProduct = VecMag(a) * VecMag(b);
- double angle = Math.Acos(dotProduct / magProduct);
-
- LLVector3 axis = VecNorm(crossProduct);
- float s = (float)Math.Sin(angle / 2);
- return new LLQuaternion(axis.X * s, axis.Y * s, axis.Z * s, (float)Math.Cos(angle / 2));
- }
-
- ///
- /// Decode a zerocoded byte array, used to decompress packets marked
- /// with the zerocoded flag
- ///
- /// Any time a zero is encountered, the next byte is a count
- /// of how many zeroes to expand. One zero is encoded with 0x00 0x01,
- /// two zeroes is 0x00 0x02, three zeroes is 0x00 0x03, etc. The
- /// first four bytes are copied directly to the output buffer.
- ///
- /// The byte array to decode
- /// The length of the byte array to decode
- /// The output byte array to decode to
- /// The length of the output buffer
- public static int ZeroDecode(byte[] src, int srclen, byte[] dest)
- {
- uint zerolen = 0;
-
- Array.Copy(src, 0, dest, 0, 4);
- zerolen += 4;
-
- //int bodylen;
- //if ((src[0] & MSG_APPENDED_ACKS) == 0)
- //{
- // bodylen = srclen;
- //}
- //else
- //{
- // bodylen = srclen - src[srclen - 1] * 4 - 1;
- //}
- int bodylen = srclen;
-
- uint i;
- for (i = zerolen; i < bodylen; i++)
+ ///
+ /// Convert a UTF8 string to a byte array
+ ///
+ /// The string to convert to a byte array
+ /// A null-terminated byte array
+ public static byte[] StringToField(string str)
{
- if (src[i] == 0x00)
- {
- for (byte j = 0; j < src[i + 1]; j++)
- {
- dest[zerolen++] = 0x00;
- }
+ if (!str.EndsWith("\0")) { str += "\0"; }
+ return System.Text.UTF8Encoding.UTF8.GetBytes(str);
+ }
- i++;
+ public static uint GetUnixTime()
+ {
+ return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
+ }
+
+ ///
+ /// Calculates the distance between two vectors
+ ///
+ public static float VecDist(LLVector3 pointA, LLVector3 pointB)
+ {
+ float xd = pointB.X - pointA.X;
+ float yd = pointB.Y - pointA.Y;
+ float zd = pointB.Z - pointA.Z;
+ return (float)Math.Sqrt(xd * xd + yd * yd + zd * zd);
+ }
+
+ ///
+ /// Calculate the magnitude of the supplied vector
+ ///
+ public static float VecMag(LLVector3 vector)
+ {
+ return (float)Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z);
+ }
+
+ ///
+ /// Return the supplied vector in normalized form
+ ///
+ public static LLVector3 VecNorm(LLVector3 vector)
+ {
+ float mag = VecMag(vector);
+ return new LLVector3(vector.X / mag, vector.Y / mag, vector.Z / mag);
+ }
+
+ ///
+ /// Calculate the rotation between two vectors
+ ///
+ /// Directional vector, such as 1,0,0 for the forward face
+ /// Target vector - normalize first with VecNorm
+ public static LLQuaternion RotBetween(LLVector3 a, LLVector3 b)
+ {
+ //A and B should both be normalized
+ //dotProduct is 0 if a and b are perpendicular. I think that's normal?
+ float dotProduct = (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z);
+
+ LLVector3 crossProduct = new LLVector3();
+ crossProduct.X = a.Y * b.Z - a.Z * b.Y;
+ crossProduct.Y = a.Z * b.X - a.X * b.Z;
+ crossProduct.Z = a.X * b.Y - a.Y * b.X;
+
+ //float scalarProduct = (a.X * b.Y) + (a.Y * b.Z) + (a.Z * b.X); //not used?
+ float magProduct = VecMag(a) * VecMag(b);
+ double angle = Math.Acos(dotProduct / magProduct);
+
+ LLVector3 axis = VecNorm(crossProduct);
+ float s = (float)Math.Sin(angle / 2);
+ return new LLQuaternion(axis.X * s, axis.Y * s, axis.Z * s, (float)Math.Cos(angle / 2));
+ }
+
+ ///
+ /// Decode a zerocoded byte array, used to decompress packets marked
+ /// with the zerocoded flag
+ ///
+ /// Any time a zero is encountered, the next byte is a count
+ /// of how many zeroes to expand. One zero is encoded with 0x00 0x01,
+ /// two zeroes is 0x00 0x02, three zeroes is 0x00 0x03, etc. The
+ /// first four bytes are copied directly to the output buffer.
+ ///
+ /// The byte array to decode
+ /// The length of the byte array to decode
+ /// The output byte array to decode to
+ /// The length of the output buffer
+ public static int ZeroDecode(byte[] src, int srclen, byte[] dest)
+ {
+ uint zerolen = 0;
+
+ Array.Copy(src, 0, dest, 0, 4);
+ zerolen += 4;
+
+ //int bodylen;
+ //if ((src[0] & MSG_APPENDED_ACKS) == 0)
+ //{
+ // bodylen = srclen;
+ //}
+ //else
+ //{
+ // bodylen = srclen - src[srclen - 1] * 4 - 1;
+ //}
+ int bodylen = srclen;
+
+ uint i;
+ for (i = zerolen; i < bodylen; i++)
+ {
+ if (src[i] == 0x00)
+ {
+ for (byte j = 0; j < src[i + 1]; j++)
+ {
+ dest[zerolen++] = 0x00;
+ }
+
+ i++;
+ }
+ else
+ {
+ dest[zerolen++] = src[i];
+ }
}
- else
+
+ // HACK: Fix truncated zerocoded messages
+ for (uint j = zerolen; j < zerolen + 16; j++)
+ {
+ dest[j] = 0;
+ }
+ zerolen += 16;
+
+ // copy appended ACKs
+ for (; i < srclen; i++)
{
dest[zerolen++] = src[i];
}
+
+ return (int)zerolen;
}
- // HACK: Fix truncated zerocoded messages
- for (uint j = zerolen; j < zerolen + 16; j++)
+ ///
+ /// Decode enough of a byte array to get the packet ID. Data before and
+ /// after the packet ID is undefined.
+ ///
+ /// The byte array to decode
+ /// The output byte array to encode to
+ public static void ZeroDecodeCommand(byte[] src, byte[] dest)
{
- dest[j] = 0;
- }
- zerolen += 16;
-
- // copy appended ACKs
- for (; i < srclen; i++)
- {
- dest[zerolen++] = src[i];
- }
-
- return (int)zerolen;
- }
-
- ///
- /// Decode enough of a byte array to get the packet ID. Data before and
- /// after the packet ID is undefined.
- ///
- /// The byte array to decode
- /// The output byte array to encode to
- public static void ZeroDecodeCommand(byte[] src, byte[] dest)
- {
- for (int srcPos = 4, destPos = 4; destPos < 8; ++srcPos)
- {
- if (src[srcPos] == 0x00)
+ for (int srcPos = 4, destPos = 4; destPos < 8; ++srcPos)
{
- for (byte j = 0; j < src[srcPos + 1]; ++j)
+ if (src[srcPos] == 0x00)
{
- dest[destPos++] = 0x00;
- }
+ for (byte j = 0; j < src[srcPos + 1]; ++j)
+ {
+ dest[destPos++] = 0x00;
+ }
- ++srcPos;
+ ++srcPos;
+ }
+ else
+ {
+ dest[destPos++] = src[srcPos];
+ }
+ }
+ }
+
+ ///
+ /// Encode a byte array with zerocoding. Used to compress packets marked
+ /// with the zerocoded flag. Any zeroes in the array are compressed down
+ /// to a single zero byte followed by a count of how many zeroes to expand
+ /// out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02,
+ /// three zeroes becomes 0x00 0x03, etc. The first four bytes are copied
+ /// directly to the output buffer.
+ ///
+ /// The byte array to encode
+ /// The length of the byte array to encode
+ /// The output byte array to encode to
+ /// The length of the output buffer
+ public static int ZeroEncode(byte[] src, int srclen, byte[] dest)
+ {
+ uint zerolen = 0;
+ byte zerocount = 0;
+
+ Array.Copy(src, 0, dest, 0, 4);
+ zerolen += 4;
+
+ int bodylen;
+ if ((src[0] & MSG_APPENDED_ACKS) == 0)
+ {
+ bodylen = srclen;
}
else
{
- dest[destPos++] = src[srcPos];
+ bodylen = srclen - src[srclen - 1] * 4 - 1;
}
- }
- }
- ///
- /// Encode a byte array with zerocoding. Used to compress packets marked
- /// with the zerocoded flag. Any zeroes in the array are compressed down
- /// to a single zero byte followed by a count of how many zeroes to expand
- /// out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02,
- /// three zeroes becomes 0x00 0x03, etc. The first four bytes are copied
- /// directly to the output buffer.
- ///
- /// The byte array to encode
- /// The length of the byte array to encode
- /// The output byte array to encode to
- /// The length of the output buffer
- public static int ZeroEncode(byte[] src, int srclen, byte[] dest)
- {
- uint zerolen = 0;
- byte zerocount = 0;
-
- Array.Copy(src, 0, dest, 0, 4);
- zerolen += 4;
-
- int bodylen;
- if ((src[0] & MSG_APPENDED_ACKS) == 0)
- {
- bodylen = srclen;
- }
- else
- {
- bodylen = srclen - src[srclen - 1] * 4 - 1;
- }
-
- uint i;
- for (i = zerolen; i < bodylen; i++)
- {
- if (src[i] == 0x00)
+ uint i;
+ for (i = zerolen; i < bodylen; i++)
{
- zerocount++;
-
- if (zerocount == 0)
+ if (src[i] == 0x00)
{
- dest[zerolen++] = 0x00;
- dest[zerolen++] = 0xff;
zerocount++;
+
+ if (zerocount == 0)
+ {
+ dest[zerolen++] = 0x00;
+ dest[zerolen++] = 0xff;
+ zerocount++;
+ }
+ }
+ else
+ {
+ if (zerocount != 0)
+ {
+ dest[zerolen++] = 0x00;
+ dest[zerolen++] = (byte)zerocount;
+ zerocount = 0;
+ }
+
+ dest[zerolen++] = src[i];
}
}
- else
- {
- if (zerocount != 0)
- {
- dest[zerolen++] = 0x00;
- dest[zerolen++] = (byte)zerocount;
- zerocount = 0;
- }
+ if (zerocount != 0)
+ {
+ dest[zerolen++] = 0x00;
+ dest[zerolen++] = (byte)zerocount;
+ }
+
+ // copy appended ACKs
+ for (; i < srclen; i++)
+ {
dest[zerolen++] = src[i];
}
+
+ return (int)zerolen;
}
- if (zerocount != 0)
+ ///
+ /// Calculates the CRC (cyclic redundancy check) needed to upload inventory.
+ ///
+ /// Creation date
+ /// Sale type
+ /// Inventory type
+ /// Type
+ /// Asset ID
+ /// Group ID
+ /// Sale price
+ /// Owner ID
+ /// Creator ID
+ /// Item ID
+ /// Folder ID
+ /// Everyone mask (permissions)
+ /// Flags
+ /// Next owner mask (permissions)
+ /// Group mask (permissions)
+ /// Owner mask (permisions)
+ /// The calculated CRC
+ public static uint InventoryCRC(int creationDate, byte saleType, sbyte invType, sbyte type,
+ LLUUID assetID, LLUUID groupID, int salePrice, LLUUID ownerID, LLUUID creatorID,
+ LLUUID itemID, LLUUID folderID, uint everyoneMask, uint flags, uint nextOwnerMask,
+ uint groupMask, uint ownerMask)
{
- dest[zerolen++] = 0x00;
- dest[zerolen++] = (byte)zerocount;
+ uint CRC = 0;
+
+ // IDs
+ CRC += assetID.CRC(); // AssetID
+ CRC += folderID.CRC(); // FolderID
+ CRC += itemID.CRC(); // ItemID
+
+ // Permission stuff
+ CRC += creatorID.CRC(); // CreatorID
+ CRC += ownerID.CRC(); // OwnerID
+ CRC += groupID.CRC(); // GroupID
+
+ // CRC += another 4 words which always seem to be zero -- unclear if this is a LLUUID or what
+ CRC += ownerMask;
+ CRC += nextOwnerMask;
+ CRC += everyoneMask;
+ CRC += groupMask;
+
+ // The rest of the CRC fields
+ CRC += flags; // Flags
+ CRC += (uint)invType; // InvType
+ CRC += (uint)type; // Type
+ CRC += (uint)creationDate; // CreationDate
+ CRC += (uint)salePrice; // SalePrice
+ CRC += (uint)((uint)saleType * 0x07073096); // SaleType
+
+ return CRC;
}
- // copy appended ACKs
- for (; i < srclen; i++)
+ ///
+ /// Calculate the MD5 hash of a given string
+ ///
+ /// The password to hash
+ /// An MD5 hash in string format, with $1$ prepended
+ public static string MD5(string password)
{
- dest[zerolen++] = src[i];
+ StringBuilder digest = new StringBuilder();
+ System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
+ byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(password));
+
+ // Convert the hash to a hex string
+ foreach (byte b in hash)
+ {
+ digest.AppendFormat("{0:x2}", b);
+ }
+
+ return "$1$" + digest.ToString();
}
- return (int)zerolen;
- }
-
- ///
- /// Calculates the CRC (cyclic redundancy check) needed to upload inventory.
- ///
- /// Creation date
- /// Sale type
- /// Inventory type
- /// Type
- /// Asset ID
- /// Group ID
- /// Sale price
- /// Owner ID
- /// Creator ID
- /// Item ID
- /// Folder ID
- /// Everyone mask (permissions)
- /// Flags
- /// Next owner mask (permissions)
- /// Group mask (permissions)
- /// Owner mask (permisions)
- /// The calculated CRC
- public static uint InventoryCRC(int creationDate, byte saleType, sbyte invType, sbyte type,
- LLUUID assetID, LLUUID groupID, int salePrice, LLUUID ownerID, LLUUID creatorID,
- LLUUID itemID, LLUUID folderID, uint everyoneMask, uint flags, uint nextOwnerMask,
- uint groupMask, uint ownerMask)
- {
- uint CRC = 0;
-
- // IDs
- CRC += assetID.CRC(); // AssetID
- CRC += folderID.CRC(); // FolderID
- CRC += itemID.CRC(); // ItemID
-
- // Permission stuff
- CRC += creatorID.CRC(); // CreatorID
- CRC += ownerID.CRC(); // OwnerID
- CRC += groupID.CRC(); // GroupID
-
- // CRC += another 4 words which always seem to be zero -- unclear if this is a LLUUID or what
- CRC += ownerMask;
- CRC += nextOwnerMask;
- CRC += everyoneMask;
- CRC += groupMask;
-
- // The rest of the CRC fields
- CRC += flags; // Flags
- CRC += (uint)invType; // InvType
- CRC += (uint)type; // Type
- CRC += (uint)creationDate; // CreationDate
- CRC += (uint)salePrice; // SalePrice
- CRC += (uint)((uint)saleType * 0x07073096); // SaleType
-
- return CRC;
- }
-
- public static string MD5(string password)
- {
- StringBuilder digest = new StringBuilder();
- System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
- byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(password));
-
- // Convert the hash to a hex string
- foreach (byte b in hash)
+ public static void PacketListToXml(List packets, XmlWriter xmlWriter)
{
- digest.AppendFormat("{0:x2}", b);
+ XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
+ ns.Add("", "");
+ XmlSerializer serializer = new XmlSerializer(typeof(List));
+ serializer.Serialize(xmlWriter, packets, ns);
}
-
- return "$1$" + digest.ToString();
}
}
-}
\ No newline at end of file
diff --git a/libsecondlife-cs/examples/TestClient/Commands/PacketLogCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/PacketLogCommand.cs
index cd68ad36..384db2e8 100644
--- a/libsecondlife-cs/examples/TestClient/Commands/PacketLogCommand.cs
+++ b/libsecondlife-cs/examples/TestClient/Commands/PacketLogCommand.cs
@@ -8,7 +8,7 @@ namespace libsecondlife.TestClient
{
public class PacketLogCommand : Command
{
- XmlWriter Writer;
+ List Packets = new List();
bool Done = false;
int Count = 0;
int Total = 0;
@@ -24,21 +24,23 @@ namespace libsecondlife.TestClient
if (args.Length != 2)
return "Usage: packetlog 10 tenpackets.xml";
+ XmlWriter writer;
+ NetworkManager.PacketCallback callback = new NetworkManager.PacketCallback(OnPacket);
+
+ Packets.Clear();
Done = false;
Count = 0;
- NetworkManager.PacketCallback callback = new NetworkManager.PacketCallback(OnPacket);
try
{
Total = Int32.Parse(args[0]);
- Writer = XmlWriter.Create(args[1]);
- Writer.WriteStartElement("packets");
+ writer = XmlWriter.Create(args[1]);
Client.Network.RegisterCallback(PacketType.Default, callback);
}
catch (Exception e)
{
- return "Usage: packetlog 10 tenpackets.xml" + Environment.NewLine + e;
+ return "Usage: packetlog 10 tenpackets.xml (" + e + ")";
}
while (!Done)
@@ -48,30 +50,32 @@ namespace libsecondlife.TestClient
Client.Network.UnregisterCallback(PacketType.Default, callback);
- lock (Writer)
+ try
{
- Writer.WriteEndElement();
- Writer.Close();
+ Helpers.PacketListToXml(Packets, writer);
}
+ catch (Exception e)
+ {
+ return "Serialization failed: " + e.ToString();
+ }
+
+ writer.Close();
+ Packets.Clear();
return "Exported " + Count + " packets to " + args[1];
}
private void OnPacket(Packet packet, Simulator simulator)
{
- lock (Writer)
+ lock (Packets)
{
- if (Writer.WriteState == WriteState.Error)
- {
- Done = true;
- }
- else if (Count >= Total)
+ if (Count >= Total)
{
Done = true;
}
else
{
- packet.ToXml(Writer);
+ Packets.Add(packet);
Count++;
}
}