/* * Copyright (c) 2006-2016, openmetaverse.co * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.co nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Reflection; using System.Text; namespace OpenMetaverse.Packets { public static class PacketDecoder { /// /// A custom decoder callback /// /// The key of the object /// the data to decode /// A string represending the fieldData public delegate string CustomPacketDecoder(string fieldName, object fieldData); private static Dictionary> Callbacks = new Dictionary>(); static PacketDecoder() { AddCallback("Color", DecodeColorField); AddCallback("TextColor", DecodeColorField); AddCallback("Timestamp", DecodeTimeStamp); AddCallback("EstateCovenantReply.Data.CovenantTimestamp", DecodeTimeStamp); AddCallback("CreationDate", DecodeTimeStamp); AddCallback("BinaryBucket", DecodeBinaryBucket); AddCallback("ParcelData.Data", DecodeBinaryToHexString); AddCallback("LayerData.Data", DecodeBinaryToHexString); AddCallback("ImageData.Data", DecodeImageData); AddCallback("TransferData.Data", DecodeBinaryToHexString); AddCallback("ObjectData.TextureEntry", DecodeTextureEntry); AddCallback("ImprovedInstantMessage.MessageBlock.Dialog", DecodeDialog); // Inventory/Permissions AddCallback("BaseMask", DecodePermissionMask); AddCallback("OwnerMask", DecodePermissionMask); AddCallback("EveryoneMask", DecodePermissionMask); AddCallback("NextOwnerMask", DecodePermissionMask); AddCallback("GroupMask", DecodePermissionMask); // FetchInventoryDescendents AddCallback("InventoryData.SortOrder", DecodeInventorySort); AddCallback("WearableType", DecodeWearableType); // AddCallback("InventoryData.Type", DecodeInventoryType); AddCallback("InvType", DecodeInventoryInvType); AddCallback("InventoryData.Flags", DecodeInventoryFlags); // BulkUpdateInventory AddCallback("ItemData.Type", DecodeInventoryType); AddCallback("ItemData.Flags", DecodeInventoryFlags); AddCallback("SaleType", DecodeObjectSaleType); AddCallback("ScriptControlChange.Data.Controls", DecodeScriptControls); AddCallback("RegionFlags", DecodeRegionFlags); AddCallback("SimAccess", DecodeSimAccess); AddCallback("ControlFlags", DecodeControlFlags); // AgentUpdate AddCallback("AgentUpdate.AgentData.State", DecodeAgentState); AddCallback("AgentUpdate.AgentData.Flags", DecodeAgentFlags); // ViewerEffect TypeData AddCallback("ViewerEffect.Effect.TypeData", DecodeViewerEffectTypeData); AddCallback("ViewerEffect.Effect.Type", DecodeViewerEffectType); // Prim/ObjectUpdate decoders AddCallback("ObjectUpdate.ObjectData.PCode", DecodeObjectPCode); AddCallback("ObjectUpdate.ObjectData.Material", DecodeObjectMaterial); AddCallback("ObjectUpdate.ObjectData.ClickAction", DecodeObjectClickAction); AddCallback("ObjectData.UpdateFlags", DecodeObjectUpdateFlags); AddCallback("ObjectUpdate.ObjectData.ObjectData", DecodeObjectData); AddCallback("TextureAnim", DecodeObjectTextureAnim); AddCallback("ObjectUpdate.ObjectData.NameValue", DecodeNameValue); AddCallback("ObjectUpdate.ObjectData.Data", DecodeObjectData); AddCallback("ObjectUpdate.ObjectData.PSBlock", DecodeObjectParticleSystem); AddCallback("ParticleSys", DecodeObjectParticleSystem); AddCallback("ObjectUpdate.ObjectData.ExtraParams", DecodeObjectExtraParams); AddCallback("ImprovedTerseObjectUpdate.ObjectData.Data", DecodeTerseUpdate); AddCallback("ImprovedTerseObjectUpdate.ObjectData.TextureEntry", DecodeTerseTextureEntry); AddCallback("ObjectUpdateCompressed.ObjectData.Data", DecodeObjectCompressedData); // ImprovedTerseObjectUpdate & ObjectUpdate AttachmentPoint & ObjectUpdateCompressed AddCallback("ObjectData.State", DecodeObjectState); //AddCallback("ObjectUpdateCompressed.ObjectData.State", DecodeObjectState); //AddCallback("ImprovedTerseObjectUpdate.ObjectData.State", DecodeObjectState); // ChatFromSimulator AddCallback("ChatData.SourceType", DecodeChatSourceType); AddCallback("ChatData.ChatType", DecodeChatChatType); AddCallback("ChatData.Audible", DecodeChatAudible); AddCallback("AttachedSound.DataBlock.Flags", DecodeAttachedSoundFlags); AddCallback("RequestImage.Type", DecodeImageType); AddCallback("EstateOwnerMessage.ParamList.Parameter", DecodeEstateParameter); AddCallback("Codec", DecodeImageCodec); AddCallback("Info.TeleportFlags", DecodeTeleportFlags); // map AddCallback("MapBlockRequest.AgentData.Flags", DecodeMapRequestFlags); AddCallback("MapItemRequest.AgentData.Flags", DecodeMapRequestFlags); AddCallback("MapBlockReply.Data.Access", DecodeMapAccess); AddCallback("FolderData.Type", DecodeFolderType); AddCallback("RequestData.ItemType", DecodeGridItemType); // TransferRequest/TransferInfo AddCallback("TransferInfo.Params", DecodeTransferParams); AddCallback("TransferInfo.ChannelType", DecodeTransferChannelType); AddCallback("TransferInfo.SourceType", DecodeTransferSourceType); AddCallback("TransferInfo.TargetType", DecodeTransferTargetType); AddCallback("TransferData.ChannelType", DecodeTransferChannelType); // SendXferPacket AddCallback("DataPacket.Data", DecodeBinaryToHexString); // Directory Manager AddCallback("DirClassifiedQuery.QueryData.QueryFlags", DecodeDirClassifiedQueryFlags); AddCallback("QueryData.QueryFlags", DecodeDirQueryFlags); AddCallback("Category", DecodeCategory); AddCallback("QueryData.SearchType", SearchTypeFlags); AddCallback("ClassifiedFlags", DecodeDirClassifiedFlags); AddCallback("EventFlags", DecodeEventFlags); AddCallback("ParcelAccessListRequest.Data.Flags", DecodeParcelACL); AddCallback("ParcelAccessListReply.Data.Flags", DecodeParcelACL); //AddCallback("ParcelAccessListReply.List.Flags", DecodeParcelACLReply); // AgentAnimation AddCallback("AnimID", DecodeAnimToConst); AddCallback("LayerData.LayerID.Type", DecodeLayerDataType); AddCallback("GroupPowers", DecodeGroupPowers); } /// /// Add a custom decoder callback /// /// The key of the field to decode /// The custom decode handler public static void AddCallback(string key, CustomPacketDecoder customPacketHandler) { if (Callbacks.ContainsKey(key)) { lock (Callbacks) Callbacks[key].Add(customPacketHandler); } else { lock (Callbacks) Callbacks.Add(key, new List() { customPacketHandler }); } } /// /// Remove a custom decoder callback /// /// The key of the field to decode /// The custom decode handler public static void RemoveCustomHandler(string key, CustomPacketDecoder customPacketHandler) { if (Callbacks.ContainsKey(key)) lock (Callbacks) { if (Callbacks[key].Contains(customPacketHandler)) Callbacks[key].Remove(customPacketHandler); } } #region Custom Decoders private static string DecodeTerseUpdate(string fieldName, object fieldData) { byte[] block = (byte[])fieldData; int i = 4; StringBuilder result = new StringBuilder(); // LocalID result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "LocalID", Utils.BytesToUInt(block, 0), "Uint32"); // State byte point = block[i++]; result.AppendFormat("{0,30}: {1,-3} {2,-36} [{3}]" + Environment.NewLine, "State", point, "(" + (AttachmentPoint)point + ")", "AttachmentPoint"); // Avatar boolean bool isAvatar = (block[i++] != 0); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "IsAvatar", isAvatar, "Boolean"); // Collision normal for avatar if (isAvatar) { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "CollisionPlane", new Vector4(block, i), "Vector4"); i += 16; } // Position result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Position", new Vector3(block, i), "Vector3"); i += 12; // Velocity result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Velocity", new Vector3( Utils.UInt16ToFloat(block, i, -128.0f, 128.0f), Utils.UInt16ToFloat(block, i + 2, -128.0f, 128.0f), Utils.UInt16ToFloat(block, i + 4, -128.0f, 128.0f)), "Vector3"); i += 6; // Acceleration result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Acceleration", new Vector3( Utils.UInt16ToFloat(block, i, -64.0f, 64.0f), Utils.UInt16ToFloat(block, i + 2, -64.0f, 64.0f), Utils.UInt16ToFloat(block, i + 4, -64.0f, 64.0f)), "Vector3"); i += 6; // Rotation (theta) result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Rotation", new Quaternion( Utils.UInt16ToFloat(block, i, -1.0f, 1.0f), Utils.UInt16ToFloat(block, i + 2, -1.0f, 1.0f), Utils.UInt16ToFloat(block, i + 4, -1.0f, 1.0f), Utils.UInt16ToFloat(block, i + 6, -1.0f, 1.0f)), "Quaternion"); i += 8; // Angular velocity (omega) result.AppendFormat("{0,30}: {1,-40} [{2}]", "AngularVelocity", new Vector3( Utils.UInt16ToFloat(block, i, -64.0f, 64.0f), Utils.UInt16ToFloat(block, i + 2, -64.0f, 64.0f), Utils.UInt16ToFloat(block, i + 4, -64.0f, 64.0f)), "Vector3"); //pos += 6; // TODO: What is in these 6 bytes? return result.ToString(); } private static string DecodeObjectCompressedData(string fieldName, object fieldData) { StringBuilder result = new StringBuilder(); byte[] block = (byte[])fieldData; int i = 0; // UUID result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "ID", new UUID(block, 0), "UUID"); i += 16; // Local ID uint LocalID = (uint)(block[i++] + (block[i++] << 8) + (block[i++] << 16) + (block[i++] << 24)); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "LocalID", LocalID, "Uint32"); // PCode PCode pcode = (PCode)block[i++]; result.AppendFormat("{0,30}: {1,-3} {2,-36} [{3}]" + Environment.NewLine, "PCode", (int)pcode, "(" + pcode + ")", "PCode"); // State AttachmentPoint point = (AttachmentPoint)block[i++]; result.AppendFormat("{0,30}: {1,-3} {2,-36} [{3}]" + Environment.NewLine, "State", (byte)point, "(" + point + ")", "AttachmentPoint"); // TODO: CRC i += 4; // Material result.AppendFormat("{0,30}: {1,-3} {2,-36} [{3}]" + Environment.NewLine, "Material", block[i], "(" + (Material)block[i++] + ")", "Material"); // Click action result.AppendFormat("{0,30}: {1,-3} {2,-36} [{3}]" + Environment.NewLine, "ClickAction", block[i], "(" + (ClickAction)block[i++] + ")", "ClickAction"); // Scale result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Scale", new Vector3(block, i), "Vector3"); i += 12; // Position result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Position", new Vector3(block, i), "Vector3"); i += 12; // Rotation result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Rotation", new Vector3(block, i), "Vector3"); i += 12; // Compressed flags CompressedFlags flags = (CompressedFlags)Utils.BytesToUInt(block, i); result.AppendFormat("{0,30}: {1,-10} {2,-29} [{3}]" + Environment.NewLine, "CompressedFlags", Utils.BytesToUInt(block, i), "(" + (CompressedFlags)Utils.BytesToUInt(block, i) + ")", "UInt"); i += 4; // Owners ID result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "OwnerID", new UUID(block, i), "UUID"); i += 16; // Angular velocity if ((flags & CompressedFlags.HasAngularVelocity) != 0) { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "AngularVelocity", new Vector3(block, i), "Vector3"); i += 12; } // Parent ID if ((flags & CompressedFlags.HasParent) != 0) { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "ParentID", (uint)(block[i++] + (block[i++] << 8) + (block[i++] << 16) + (block[i++] << 24)), "UInt"); } // Tree data if ((flags & CompressedFlags.Tree) != 0) { result.AppendFormat("{0,30}: {1,-2} {2,-37} [{3}]" + Environment.NewLine, "TreeSpecies", block[i++], "(" + (Tree)block[i] + ")", "Tree"); } // Scratch pad else if ((flags & CompressedFlags.ScratchPad) != 0) { int size = block[i++]; byte[] scratch = new byte[size]; Buffer.BlockCopy(block, i, scratch, 0, size); result.AppendFormat("{0,30}: {1,-40} [ScratchPad[]]" + Environment.NewLine, "ScratchPad", Utils.BytesToHexString(scratch, String.Format("{0,30}", "Data"))); i += size; } // Floating text if ((flags & CompressedFlags.HasText) != 0) { string text = String.Empty; while (block[i] != 0) { text += (char)block[i]; i++; } i++; // Floating text result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "Text", text, "string"); // Text color result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "TextColor", new Color4(block, i, false), "Color4"); i += 4; } // Media URL if ((flags & CompressedFlags.MediaURL) != 0) { string text = String.Empty; while (block[i] != 0) { text += (char)block[i]; i++; } i++; result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "MediaURL", text, "string"); } // Particle system if ((flags & CompressedFlags.HasParticles) != 0) { Primitive.ParticleSystem p = new Primitive.ParticleSystem(block, i); result.AppendLine(DecodeObjectParticleSystem("ParticleSystem", p)); i += 86; } // Extra parameters TODO: Primitive prim = new Primitive(); i += prim.SetExtraParamsFromBytes(block, i); //Sound data if ((flags & CompressedFlags.HasSound) != 0) { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "SoundID", new UUID(block, i), "UUID"); i += 16; result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "SoundGain", Utils.BytesToFloat(block, i), "Float"); i += 4; result.AppendFormat("{0,30}: {1,-2} {2,-37} [{3}]" + Environment.NewLine, "SoundFlags", block[i++], "(" + (SoundFlags)block[i] + ")", "SoundFlags"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "SoundRadius", Utils.BytesToFloat(block, i), "Float"); i += 4; } // Name values if ((flags & CompressedFlags.HasNameValues) != 0) { string text = String.Empty; while (block[i] != 0) { text += (char)block[i]; i++; } i++; // Parse the name values if (text.Length > 0) { string[] lines = text.Split('\n'); NameValue[] nameValues = new NameValue[lines.Length]; for (int j = 0; j < lines.Length; j++) { if (!String.IsNullOrEmpty(lines[j])) { NameValue nv = new NameValue(lines[j]); nameValues[j] = nv; } } DecodeNameValue("NameValues", nameValues); } } result.AppendFormat("{0,30}: {1,-2} {2,-37} [{3}]" + Environment.NewLine, "PathCurve", block[i], "(" + (PathCurve)block[i++] + ")", "PathCurve"); ushort pathBegin = Utils.BytesToUInt16(block, i); i += 2; result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathBegin", Primitive.UnpackBeginCut(pathBegin), "float"); ushort pathEnd = Utils.BytesToUInt16(block, i); i += 2; result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathEnd", Primitive.UnpackEndCut(pathEnd), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathScaleX", Primitive.UnpackPathScale(block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathScaleY", Primitive.UnpackPathScale(block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathShearX", Primitive.UnpackPathShear((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathShearY", Primitive.UnpackPathShear((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathTwist", Primitive.UnpackPathTwist((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathTwistBegin", Primitive.UnpackPathTwist((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathRadiusOffset", Primitive.UnpackPathTwist((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathTaperX", Primitive.UnpackPathTaper((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathTaperY", Primitive.UnpackPathTaper((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathRevolutions", Primitive.UnpackPathRevolutions(block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "PathSkew", Primitive.UnpackPathTwist((sbyte)block[i++]), "float"); result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "ProfileCurve", block[i++], "float"); ushort profileBegin = Utils.BytesToUInt16(block, i); i += 2; result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "ProfileBegin", Primitive.UnpackBeginCut(profileBegin), "float"); ushort profileEnd = Utils.BytesToUInt16(block, i); i += 2; result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "ProfileEnd", Primitive.UnpackEndCut(profileEnd), "float"); ushort profileHollow = Utils.BytesToUInt16(block, i); i += 2; result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, "ProfileHollow", Primitive.UnpackProfileHollow(profileHollow), "float"); int textureEntryLength = (int)Utils.BytesToUInt(block, i); i += 4; //prim.Textures = new Primitive.TextureEntry(block, i, textureEntryLength); String s = DecodeTextureEntry("TextureEntry", new Primitive.TextureEntry(block, i, textureEntryLength)); result.AppendLine(s); i += textureEntryLength; // Texture animation if ((flags & CompressedFlags.TextureAnimation) != 0) { i += 4; string a = DecodeObjectTextureAnim("TextureAnimation", new Primitive.TextureAnimation(block, i)); result.AppendLine(a); } return result.ToString(); } private static string DecodeObjectData(string fieldName, object fieldData) { byte[] data = (byte[])fieldData; if (data.Length == 1) { return String.Format("{0,30}: {1,2} {2,-38} [{3}]", fieldName + " (Tree Species)", fieldData, "(" + (Tree)(byte)fieldData + ")", fieldData.GetType().Name); } else if (data.Length == 60) { /* TODO: these are likely useful packed fields, * need to unpack them */ return Utils.BytesToHexString((byte[])fieldData, String.Format("{0,30}", fieldName)); } else { return Utils.BytesToHexString((byte[])fieldData, String.Format("{0,30}", fieldName)); } } private static string DecodeObjectTextureAnim(string fieldName, object fieldData) { StringBuilder result = new StringBuilder(); Primitive.TextureAnimation TextureAnim; if (fieldData is Primitive.TextureAnimation) TextureAnim = (Primitive.TextureAnimation)fieldData; else TextureAnim = new Primitive.TextureAnimation((byte[])fieldData, 0); result.AppendFormat("{0,30}", " " + Environment.NewLine); GenericTypeDecoder(TextureAnim, ref result); result.AppendFormat("{0,30}", ""); return result.ToString(); } private static string DecodeEstateParameter(string fieldName, object fieldData) { byte[] bytes = (byte[])fieldData; if (bytes.Length == 17) { return String.Format("{0,30}: {1,-40} [UUID]", fieldName, new UUID((byte[])fieldData, 0)); } else { return String.Format("{0,30}: {1,-40} [Byte[]]", fieldName, Utils.BytesToString((byte[])fieldData)); } } private static string DecodeNameValue(string fieldName, object fieldData) { string nameValue = Utils.BytesToString((byte[])fieldData); NameValue[] nameValues = null; if (nameValue.Length > 0) { string[] lines = nameValue.Split('\n'); nameValues = new NameValue[lines.Length]; for (int i = 0; i < lines.Length; i++) { if (!String.IsNullOrEmpty(lines[i])) { NameValue nv = new NameValue(lines[i]); nameValues[i] = nv; } } } StringBuilder result = new StringBuilder(); result.AppendFormat("{0,30}", " " + Environment.NewLine); if (nameValues != null) { for (int i = 0; i < nameValues.Length; i++) { result.AppendFormat( "{0,30}: Name={1} Value={2} Class={3} Type={4} Sendto={5}" + Environment.NewLine, "NameValue", nameValues[i].Name, nameValues[i].Value, nameValues[i].Class, nameValues[i].Type, nameValues[i].Sendto); } } result.AppendFormat("{0,30}", ""); return result.ToString(); } private static string DecodeObjectExtraParams(string fieldName, object fieldData) { byte[] data = (byte[])fieldData; int i = 0; //int totalLength = 1; Primitive.FlexibleData Flexible = null; Primitive.LightData Light = null; Primitive.SculptData Sculpt = null; byte extraParamCount = data[i++]; for (int k = 0; k < extraParamCount; k++) { ExtraParamType type = (ExtraParamType)Utils.BytesToUInt16(data, i); i += 2; uint paramLength = Utils.BytesToUInt(data, i); i += 4; if (type == ExtraParamType.Flexible) Flexible = new Primitive.FlexibleData(data, i); else if (type == ExtraParamType.Light) Light = new Primitive.LightData(data, i); else if (type == ExtraParamType.Sculpt) Sculpt = new Primitive.SculptData(data, i); i += (int)paramLength; //totalLength += (int)paramLength + 6; } StringBuilder result = new StringBuilder(); result.AppendFormat("{0,30}", "" + Environment.NewLine); if (Flexible != null) { result.AppendFormat("{0,30}", "" + Environment.NewLine); GenericTypeDecoder(Flexible, ref result); result.AppendFormat("{0,30}", "" + Environment.NewLine); } if (Sculpt != null) { result.AppendFormat("{0,30}", "" + Environment.NewLine); GenericTypeDecoder(Sculpt, ref result); result.AppendFormat("{0,30}", "" + Environment.NewLine); } if (Light != null) { result.AppendFormat("{0,30}", "" + Environment.NewLine); GenericTypeDecoder(Light, ref result); result.AppendFormat("{0,30}", "" + Environment.NewLine); } result.AppendFormat("{0,30}", ""); return result.ToString(); } private static string DecodeObjectParticleSystem(string fieldName, object fieldData) { StringBuilder result = new StringBuilder(); Primitive.ParticleSystem ParticleSys; if (fieldData is Primitive.ParticleSystem) ParticleSys = (Primitive.ParticleSystem)fieldData; else ParticleSys = new Primitive.ParticleSystem((byte[])fieldData, 0); result.AppendFormat("{0,30}", "" + Environment.NewLine); GenericTypeDecoder(ParticleSys, ref result); result.AppendFormat("{0,30}", ""); return result.ToString(); } private static void GenericTypeDecoder(object obj, ref StringBuilder result) { FieldInfo[] fields = obj.GetType().GetFields(); foreach (FieldInfo field in fields) { String special; if (SpecialDecoder("a" + "." + "b" + "." + field.Name, field.GetValue(obj), out special)) { result.AppendLine(special); } else { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, field.Name, field.GetValue(obj), field.FieldType.Name); } } } private static string DecodeObjectPCode(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-3} {2,-36} [PCode]", fieldName, fieldData, "(" + (PCode)(byte)fieldData + ")"); } private static string DecodeImageType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-3} {2,-36} [ImageType]", fieldName, fieldData, "(" + (ImageType)(byte)fieldData + ")"); } private static string DecodeImageCodec(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-3} {2,-36} [ImageCodec]", fieldName, fieldData, "(" + (ImageCodec)(byte)fieldData + ")"); } private static string DecodeObjectMaterial(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-3} {2,-36} [Material]", fieldName, fieldData, "(" + (Material)(byte)fieldData + ")"); } private static string DecodeObjectClickAction(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-3} {2,-36} [ClickAction]", fieldName, fieldData, "(" + (ClickAction)(byte)fieldData + ")"); } private static string DecodeEventFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-3} {2,-36} [EventFlags]", fieldName, fieldData, "(" + (DirectoryManager.EventFlags)(uint)fieldData + ")"); } private static string DecodeDirQueryFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [DirectoryManager.DirFindFlags]", fieldName, fieldData, "(" + (DirectoryManager.DirFindFlags)(uint)fieldData + ")"); } private static string DecodeDirClassifiedQueryFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [ClassifiedQueryFlags]", fieldName, fieldData, "(" + (DirectoryManager.ClassifiedQueryFlags)(uint)fieldData + ")"); } private static string DecodeDirClassifiedFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [ClassifiedFlags]", fieldName, fieldData, "(" + (DirectoryManager.ClassifiedFlags)(byte)fieldData + ")"); } private static string DecodeGroupPowers(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-20} {2,-19} [GroupPowers]", fieldName, fieldData, "(" + (GroupPowers)(ulong)fieldData + ")"); } private static string DecodeParcelACL(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [AccessList]", fieldName, fieldData, "(" + (AccessList)(uint)fieldData + ")"); } private static string SearchTypeFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [DirectoryManager.SearchTypeFlags]", fieldName, fieldData, "(" + (DirectoryManager.SearchTypeFlags)(uint)fieldData + ")"); } private static string DecodeCategory(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-3} {2,-36} [ParcelCategory]", fieldName, fieldData, "(" + fieldData + ")"); } private static string DecodeObjectUpdateFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [PrimFlags]", fieldName, fieldData, "(" + (PrimFlags)(uint)fieldData + ")"); } private static string DecodeTeleportFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [TeleportFlags]", fieldName, fieldData, "(" + (TeleportFlags)(uint)fieldData + ")"); } private static string DecodeScriptControls(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [AgentManager.ControlFlags]", fieldName, (uint)fieldData, "(" + (AgentManager.ControlFlags)(uint)fieldData + ")"); } private static string DecodeColorField(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-40} [Color4]", fieldName, fieldData.GetType().Name.Equals("Color4") ? (Color4)fieldData : new Color4((byte[])fieldData, 0, false)); } private static string DecodeTimeStamp(string fieldName, object fieldData) { if (fieldData is Int32 && (int)fieldData > 0) return String.Format("{0,30}: {1,-10} {2,-29} [{3}]", fieldName, fieldData, "(" + Utils.UnixTimeToDateTime((int)fieldData) + ")", fieldData.GetType().Name); else if (fieldData is uint && (uint)fieldData > 0) return String.Format("{0,30}: {1,-10} {2,-29} [{3}]", fieldName, fieldData, "(" + Utils.UnixTimeToDateTime((uint)fieldData) + ")", fieldData.GetType().Name); else return String.Format("{0,30}: {1,-40} [{2}]", fieldName, fieldData, fieldData.GetType().Name); } private static string DecodeBinaryBucket(string fieldName, object fieldData) { byte[] bytes = (byte[])fieldData; string bucket = String.Empty; if (bytes.Length == 1) { bucket = String.Format("{0}", bytes[0]); } else if (bytes.Length == 17) { bucket = String.Format("{0,-36} {1} ({2})", new UUID(bytes, 1), bytes[0], (AssetType)(sbyte)bytes[0]); } else if (bytes.Length == 16) // the folder ID for the asset to be stored into if we accept an inventory offer { bucket = new UUID(bytes, 0).ToString(); } else { bucket = Utils.BytesToString(bytes); // we'll try a string lastly } return String.Format("{0,30}: {1,-40} [Byte[{2}]]", fieldName, bucket, bytes.Length); } private static string DecodeBinaryToHexString(string fieldName, object fieldData) { return String.Format("{0,30}", Utils.BytesToHexString((byte[])fieldData, String.Format("{0,30}", fieldName))); } private static string DecodeWearableType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [WearableType]", fieldName, (byte)fieldData, "(" + (WearableType)fieldData + ")"); } private static string DecodeInventoryType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [AssetType]", fieldName, (sbyte)fieldData, "(" + (AssetType)(sbyte)fieldData + ")"); } private static string DecodeInventorySort(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [InventorySortOrder]", fieldName, fieldData, "(" + (InventorySortOrder)(int)fieldData + ")"); } private static string DecodeInventoryInvType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [InventoryType]", fieldName, (sbyte)fieldData, "(" + (InventoryType)fieldData + ")"); } private static string DecodeFolderType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [Folderype]", fieldName, (sbyte)fieldData, "(" + (FolderType)fieldData + ")"); } private static string DecodeInventoryFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [InventoryItemFlags]", fieldName, (uint)fieldData, "(" + (InventoryItemFlags)(uint)fieldData + ")"); } private static string DecodeObjectSaleType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [SaleType]", fieldName, (byte)fieldData, "(" + (SaleType)fieldData + ")"); } private static string DecodeRegionFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [RegionFlags]", fieldName, fieldData, "(" + (RegionFlags)(uint)fieldData + ")"); } private static string DecodeTransferParams(string fieldName, object fieldData) { byte[] paramData = (byte[])fieldData; StringBuilder result = new StringBuilder(); result.AppendLine(" "); if (paramData.Length == 20) { result.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, "AssetID", new UUID(paramData, 0)); result.AppendFormat("{0,30}: {1,-2} {2,-37} [AssetType]" + Environment.NewLine, "AssetType", (sbyte)paramData[16], "(" + (AssetType)(sbyte)paramData[16] + ")"); } else if (paramData.Length == 100) { //UUID agentID = new UUID(info.TransferInfo.Params, 0); result.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, "AgentID", new UUID(paramData, 0)); //UUID sessionID = new UUID(info.TransferInfo.Params, 16); result.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, "SessionID", new UUID(paramData, 16)); //UUID ownerID = new UUID(info.TransferInfo.Params, 32); result.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, "OwnerID", new UUID(paramData, 32)); //UUID taskID = new UUID(info.TransferInfo.Params, 48); result.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, "TaskID", new UUID(paramData, 48)); //UUID itemID = new UUID(info.TransferInfo.Params, 64); result.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, "ItemID", new UUID(paramData, 64)); result.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, "AssetID", new UUID(paramData, 80)); result.AppendFormat("{0,30}: {1,-2} {2,-37} [AssetType]" + Environment.NewLine, "AssetType", (sbyte)paramData[96], "(" + (AssetType)(sbyte)paramData[96] + ")"); } else { Console.WriteLine("Oh Poop!"); } result.Append(""); return result.ToString(); } private static string DecodeTransferChannelType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [ChannelType]", fieldName, fieldData, "(" + (ChannelType)(int)fieldData + ")"); } private static string DecodeTransferSourceType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [SourceType]", fieldName, fieldData, "(" + (SourceType)(int)fieldData + ")"); } private static string DecodeTransferTargetType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [TargetType]", fieldName, fieldData, "(" + (TargetType)(int)fieldData + ")"); } private static string DecodeMapRequestFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [GridLayerType]", fieldName, fieldData, "(" + (GridLayerType)(uint)fieldData + ")"); } private static string DecodeGridItemType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [GridItemType]", fieldName, fieldData, "(" + (GridItemType)(uint)fieldData + ")"); } private static string DecodeLayerDataType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [LayerType]", fieldName, fieldData, "(" + (TerrainPatch.LayerType)(byte)fieldData + ")"); } private static string DecodeMapAccess(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [SimAccess]", fieldName, fieldData, "(" + (SimAccess)(byte)fieldData + ")"); } private static string DecodeSimAccess(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [SimAccess]", fieldName, (byte)fieldData, "(" + (SimAccess)fieldData + ")"); } private static string DecodeAttachedSoundFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [SoundFlags]", fieldName, (byte)fieldData, "(" + (SoundFlags)fieldData + ")"); } private static string DecodeChatSourceType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [SourceType]", fieldName, fieldData, "(" + (SourceType)(byte)fieldData + ")"); } private static string DecodeChatChatType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [ChatType]", fieldName, (byte)fieldData, "(" + (ChatType)fieldData + ")"); } private static string DecodeChatAudible(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [ChatAudibleLevel]", fieldName, (byte)fieldData, "(" + (ChatAudibleLevel)(byte)fieldData + ")"); } private static string DecodeImageData(string fieldName, object fieldData) { return String.Format("{0,10}", Utils.BytesToHexString((byte[])fieldData, String.Format("{0,30}", fieldName))); } private static string DecodeTerseTextureEntry(string fieldName, object fieldData) { byte[] block = (byte[])fieldData; Primitive.TextureEntry te = new Primitive.TextureEntry(block, 4, block.Length - 4); StringBuilder result = new StringBuilder(); result.AppendFormat("{0,30}", " " + Environment.NewLine); if (te.DefaultTexture != null) { result.AppendFormat("{0,30}", " " + Environment.NewLine); GenericFieldsDecoder(te.DefaultTexture, ref result); GenericPropertiesDecoder(te.DefaultTexture, ref result); result.AppendFormat("{0,30}", " " + Environment.NewLine); } result.AppendFormat("{0,30}", " " + Environment.NewLine); for (int i = 0; i < te.FaceTextures.Length; i++) { if (te.FaceTextures[i] != null) { result.AppendFormat("{0,30}[{1}]" + Environment.NewLine, "FaceTexture", i); GenericFieldsDecoder(te.FaceTextures[i], ref result); GenericPropertiesDecoder(te.FaceTextures[i], ref result); } } result.AppendFormat("{0,30}", " " + Environment.NewLine); result.AppendFormat("{0,30}", ""); return result.ToString(); } private static string DecodeTextureEntry(string fieldName, object fieldData) { Primitive.TextureEntry te; if (fieldData is Primitive.TextureEntry) te = (Primitive.TextureEntry)fieldData; else { byte[] tebytes = (byte[])fieldData; te = new Primitive.TextureEntry(tebytes, 0, tebytes.Length); } StringBuilder result = new StringBuilder(); result.AppendFormat("{0,30}", " " + Environment.NewLine); if (te.DefaultTexture != null) { result.AppendFormat("{0,30}", " " + Environment.NewLine); GenericFieldsDecoder(te.DefaultTexture, ref result); GenericPropertiesDecoder(te.DefaultTexture, ref result); result.AppendFormat("{0,30}", " " + Environment.NewLine); } result.AppendFormat("{0,30}", " " + Environment.NewLine); for (int i = 0; i < te.FaceTextures.Length; i++) { if (te.FaceTextures[i] != null) { result.AppendFormat("{0,30}[{1}]" + Environment.NewLine, "FaceTexture", i); GenericFieldsDecoder(te.FaceTextures[i], ref result); GenericPropertiesDecoder(te.FaceTextures[i], ref result); } } result.AppendFormat("{0,30}", " " + Environment.NewLine); result.AppendFormat("{0,30}", ""); return result.ToString(); } private static void GenericFieldsDecoder(object obj, ref StringBuilder result) { Type parcelType = obj.GetType(); FieldInfo[] fields = parcelType.GetFields(); foreach (FieldInfo field in fields) { String special; if (SpecialDecoder("a" + "." + "b" + "." + field.Name, field.GetValue(obj), out special)) { result.AppendLine(special); } else { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, field.Name, field.GetValue(obj), field.FieldType.Name); } } } private static void GenericPropertiesDecoder(object obj, ref StringBuilder result) { Type parcelType = obj.GetType(); PropertyInfo[] propertyInfos = parcelType.GetProperties(); foreach (PropertyInfo property in propertyInfos) { String special; if (SpecialDecoder("a" + "." + "b" + "." + property.Name, property.GetValue(obj, null), out special)) { result.AppendLine(special); } else { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, property.Name, property.GetValue(obj, null), property.PropertyType.Name); } } } private static string DecodeDialog(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [{3}]", fieldName, (byte)fieldData, "(" + (InstantMessageDialog)fieldData + ")", fieldData.GetType().Name); } private static string DecodeControlFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [{3}]", fieldName, fieldData, "(" + (AgentManager.ControlFlags)(uint)fieldData + ")", fieldData.GetType().Name); } private static string DecodePermissionMask(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [{3}]", fieldName, (uint)fieldData, "(" + (PermissionMask)fieldData + ")", fieldData.GetType().Name); } private static string DecodeViewerEffectTypeData(string fieldName, object fieldData) { byte[] data = (byte[])fieldData; StringBuilder sb = new StringBuilder(); if (data.Length == 56 || data.Length == 57) { UUID sourceAvatar = new UUID(data, 0); UUID targetObject = new UUID(data, 16); Vector3d targetPos = new Vector3d(data, 32); sb.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, fieldName, "Source AvatarID=" + sourceAvatar); sb.AppendFormat("{0,30}: {1,-40} [UUID]" + Environment.NewLine, fieldName, "Target ObjectID=" + targetObject); float lx, ly; Helpers.GlobalPosToRegionHandle((float)targetPos.X, (float)targetPos.Y, out lx, out ly); sb.AppendFormat("{0,30}: {1,-40} [Vector3d]", fieldName, targetPos); if (data.Length == 57) { sb.AppendLine(); sb.AppendFormat("{0,30}: {1,-17} {2,-22} [Byte]", fieldName, "Point At Type=" + data[56], "(" + (PointAtType)data[56] + ")"); } return sb.ToString(); } else { return String.Format("{0,30}: (No Decoder) Length={1}" + Environment.NewLine, fieldName, data.Length) + Utils.BytesToHexString(data, String.Format("{0,30}", "")); } } private static string DecodeAgentState(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [AgentState]", fieldName, fieldData, "(" + (AgentState)(byte)fieldData + ")"); } private static string DecodeAgentFlags(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [AgentFlags]", fieldName, fieldData, "(" + (AgentFlags)(byte)fieldData + ")"); } private static string DecodeObjectState(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [AttachmentPoint]", fieldName, fieldData, "(" + (AttachmentPoint)(byte)fieldData + ")"); } private static string DecodeViewerEffectType(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-2} {2,-37} [{3}]", fieldName, fieldData, "(" + (EffectType)(byte)fieldData + ")", fieldData.GetType().Name); } private static string DecodeAnimToConst(string fieldName, object fieldData) { string animConst = "UUID"; Dictionary animsDict = Animations.ToDictionary(); if (animsDict.ContainsKey((UUID)fieldData)) animConst = animsDict[(UUID)fieldData]; return String.Format("{0,30}: {1,-40} [{2}]", fieldName, fieldData, animConst); } #endregion /// /// Creates a formatted string containing the values of a Packet /// /// The Packet /// A formatted string of values of the nested items in the Packet object public static string PacketToString(Packet packet) { StringBuilder result = new StringBuilder(); result.AppendFormat("Packet Type: {0} http://lib.openmetaverse.co/wiki/{0} http://wiki.secondlife.com/wiki/{0}" + Environment.NewLine, packet.Type); result.AppendLine("[Packet Header]"); // payload result.AppendFormat("Sequence: {0}" + Environment.NewLine, packet.Header.Sequence); result.AppendFormat(" Options: {0}" + Environment.NewLine, InterpretOptions(packet.Header)); result.AppendLine(); result.AppendLine("[Packet Payload]"); FieldInfo[] fields = packet.GetType().GetFields(); for (int i = 0; i < fields.Length; i++) { // we're not interested in any of these here if (fields[i].Name == "Type" || fields[i].Name == "Header" || fields[i].Name == "HasVariableBlocks") continue; if (fields[i].FieldType.IsArray) { result.AppendFormat("{0,30} []" + Environment.NewLine, "-- " + fields[i].Name + " --"); RecursePacketArray(fields[i], packet, ref result); } else { result.AppendFormat("{0,30}" + Environment.NewLine, "-- " + fields[i].Name + " --"); RecursePacketField(fields[i], packet, ref result); } } return result.ToString(); } public static string InterpretOptions(Header header) { return "[" + (header.AppendedAcks ? "Ack" : " ") + " " + (header.Resent ? "Res" : " ") + " " + (header.Reliable ? "Rel" : " ") + " " + (header.Zerocoded ? "Zer" : " ") + "]" ; } private static void RecursePacketArray(FieldInfo fieldInfo, object packet, ref StringBuilder result) { var packetDataObject = fieldInfo.GetValue(packet); foreach (object nestedArrayRecord in packetDataObject as Array) { FieldInfo[] fields = nestedArrayRecord.GetType().GetFields(); foreach (FieldInfo t in fields) { String special; if (SpecialDecoder(packet.GetType().Name + "." + fieldInfo.Name + "." + t.Name, t.GetValue(nestedArrayRecord), out special)) { result.AppendLine(special); } else if (t.FieldType.IsArray) // default for an array (probably a byte[]) { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, t.Name, Utils.BytesToString((byte[])t.GetValue(nestedArrayRecord)), /*fields[i].GetValue(nestedArrayRecord).GetType().Name*/ "String"); } else // default for a field { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, t.Name, t.GetValue(nestedArrayRecord), t.GetValue(nestedArrayRecord).GetType().Name); } } // Handle Properties foreach (PropertyInfo propertyInfo in nestedArrayRecord.GetType().GetProperties()) { if (propertyInfo.Name.Equals("Length")) continue; string special; if (SpecialDecoder(packet.GetType().Name + "." + fieldInfo.Name + "." + propertyInfo.Name, propertyInfo.GetValue(nestedArrayRecord, null), out special)) { result.AppendLine(special); } else { var p = propertyInfo.GetValue(nestedArrayRecord, null); /* Leave the c for now at the end, it signifies something useful that still needs to be done i.e. a decoder written */ result.AppendFormat("{0, 30}: {1,-40} [{2}]c" + Environment.NewLine, propertyInfo.Name, Utils.BytesToString((byte[])propertyInfo.GetValue(nestedArrayRecord, null)), propertyInfo.PropertyType.Name); } } result.AppendFormat("{0,32}" + Environment.NewLine, "***"); } } private static void RecursePacketField(FieldInfo fieldInfo, object packet, ref StringBuilder result) { object packetDataObject = fieldInfo.GetValue(packet); // handle Fields foreach (FieldInfo packetValueField in fieldInfo.GetValue(packet).GetType().GetFields()) { string special; if (SpecialDecoder(packet.GetType().Name + "." + fieldInfo.Name + "." + packetValueField.Name, packetValueField.GetValue(packetDataObject), out special)) { result.AppendLine(special); } else if (packetValueField.FieldType.IsArray) { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, packetValueField.Name, Utils.BytesToString((byte[])packetValueField.GetValue(packetDataObject)), /*packetValueField.FieldType.Name*/ "String"); } else { result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine, packetValueField.Name, packetValueField.GetValue(packetDataObject), packetValueField.FieldType.Name); } } // Handle Properties foreach (PropertyInfo propertyInfo in packetDataObject.GetType().GetProperties()) { if (propertyInfo.Name.Equals("Length")) continue; string special; if (SpecialDecoder(packet.GetType().Name + "." + fieldInfo.Name + "." + propertyInfo.Name, propertyInfo.GetValue(packetDataObject, null), out special)) { result.AppendLine(special); } else if (propertyInfo.GetValue(packetDataObject, null).GetType() == typeof(byte[])) { result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, propertyInfo.Name, Utils.BytesToString((byte[])propertyInfo.GetValue(packetDataObject, null)), propertyInfo.PropertyType.Name); } else { result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, propertyInfo.Name, propertyInfo.GetValue(packetDataObject, null), propertyInfo.PropertyType.Name); } } } private static bool SpecialDecoder(string decoderKey, object fieldData, out string result) { result = string.Empty; string[] keys = decoderKey.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); string[] keyList = { decoderKey, decoderKey.Replace("Packet", ""), keys[1] + "." + keys[2], keys[2] }; foreach (string key in keyList) { bool ok = true; if (fieldData is byte[]) { byte[] fd = (byte[])fieldData; ok = fd.Length > 0; if (!ok) { // bypass the decoder since we were passed an empty byte array result = String.Format("{0,30}:", keys[2]); return true; } } if (ok && Callbacks.ContainsKey(key)) // fieldname e.g: Plane { foreach (CustomPacketDecoder decoder in Callbacks[key]) result = decoder(keys[2], fieldData); return true; } } return false; } /// /// Decode an IMessage object into a beautifully formatted string /// /// The IMessage object /// Recursion level (used for indenting) /// A formatted string containing the names and values of the source object public static string MessageToString(object message, int recurseLevel) { if (message == null) return String.Empty; StringBuilder result = new StringBuilder(); // common/custom types if (recurseLevel <= 0) { result.AppendFormat("Message Type: {0} http://lib.openmetaverse.co/wiki/{0}" + Environment.NewLine, message.GetType().Name); } else { string pad = " +--".PadLeft(recurseLevel + 3); result.AppendFormat("{0} {1}" + Environment.NewLine, pad, message.GetType().Name); } recurseLevel++; foreach (FieldInfo messageField in message.GetType().GetFields()) { // an abstract message class if (messageField.FieldType.IsAbstract) { result.AppendLine(MessageToString(messageField.GetValue(message), recurseLevel)); } // a byte array else if (messageField.GetValue(message) != null && messageField.GetValue(message).GetType() == typeof(Byte[])) { result.AppendFormat("{0, 30}:" + Environment.NewLine, messageField.Name); result.AppendFormat("{0}" + Environment.NewLine, Utils.BytesToHexString((byte[])messageField.GetValue(message), String.Format("{0,30}", ""))); } // an array of class objects else if (messageField.FieldType.IsArray) { var messageObjectData = messageField.GetValue(message); result.AppendFormat("-- {0} --" + Environment.NewLine, messageField.FieldType.Name); foreach (object nestedArrayObject in messageObjectData as Array) { if (nestedArrayObject == null) { result.AppendFormat("{0,30}" + Environment.NewLine, "-- null --"); continue; } else { result.AppendFormat("{0,30}" + Environment.NewLine, "-- " + nestedArrayObject.GetType().Name + " --"); } foreach (FieldInfo nestedField in nestedArrayObject.GetType().GetFields()) { if (nestedField.FieldType.IsEnum) { result.AppendFormat("{0,30}: {1,-10} {2,-29} [{3}]" + Environment.NewLine, nestedField.Name, Enum.Format(nestedField.GetValue(nestedArrayObject).GetType(), nestedField.GetValue(nestedArrayObject), "D"), "(" + nestedField.GetValue(nestedArrayObject) + ")", nestedField.GetValue(nestedArrayObject).GetType().Name); } else if (nestedField.FieldType.IsInterface) { result.AppendLine(MessageToString(nestedField.GetValue(nestedArrayObject), recurseLevel)); } else { result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, nestedField.Name, nestedField.GetValue(nestedArrayObject), nestedField.FieldType.Name); } } } } else { if (messageField.FieldType.IsEnum) { result.AppendFormat("{0,30}: {1,-2} {2,-37} [{3}]" + Environment.NewLine, messageField.Name, Enum.Format(messageField.GetValue(message).GetType(), messageField.GetValue(message), "D"), "(" + messageField.GetValue(message) + ")", messageField.FieldType.Name); } else if (messageField.FieldType.IsInterface) { result.AppendLine(MessageToString(messageField.GetValue(message), recurseLevel)); } else { result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, messageField.Name, messageField.GetValue(message), messageField.FieldType.Name); } } } return result.ToString(); } } }