From 50fc0db56fa10c778f5c4126dd2f59382cd45cdb Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sun, 3 Dec 2006 01:50:00 +0000 Subject: [PATCH] Packets are exported as a List with Helpers.PacketListToXml() now to make deserialization easier git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@675 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/Helpers.cs | 865 +++++++++--------- .../TestClient/Commands/PacketLogCommand.cs | 34 +- 2 files changed, 460 insertions(+), 439 deletions(-) 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++; } }