This corrects a severe error where as AssetTypes were being reused for the Folder FolderType id resulting in invalid ids for various system folder types causing inventory validation issues from within the SL viewer.
1800 lines
75 KiB
C#
1800 lines
75 KiB
C#
/*
|
|
* Copyright (c) 2006-2014, openmetaverse.org
|
|
* 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.org 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
|
|
{
|
|
/// <summary>
|
|
/// A custom decoder callback
|
|
/// </summary>
|
|
/// <param name="fieldName">The key of the object</param>
|
|
/// <param name="fieldData">the data to decode</param>
|
|
/// <returns>A string represending the fieldData</returns>
|
|
public delegate string CustomPacketDecoder(string fieldName, object fieldData);
|
|
|
|
private static Dictionary<string, List<CustomPacketDecoder>> Callbacks = new Dictionary<string, List<CustomPacketDecoder>>();
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a custom decoder callback
|
|
/// </summary>
|
|
/// <param name="key">The key of the field to decode</param>
|
|
/// <param name="customPacketHandler">The custom decode handler</param>
|
|
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<CustomPacketDecoder>() { customPacketHandler });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a custom decoder callback
|
|
/// </summary>
|
|
/// <param name="key">The key of the field to decode</param>
|
|
/// <param name="customPacketHandler">The custom decode handler</param>
|
|
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}", " <TextureAnimation>" + Environment.NewLine);
|
|
GenericTypeDecoder(TextureAnim, ref result);
|
|
result.AppendFormat("{0,30}", "</TextureAnimation>");
|
|
|
|
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}", " <NameValues>" + 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}", "</NameValues>");
|
|
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}", "<ExtraParams>" + Environment.NewLine);
|
|
if (Flexible != null)
|
|
{
|
|
result.AppendFormat("{0,30}", "<Flexible>" + Environment.NewLine);
|
|
GenericTypeDecoder(Flexible, ref result);
|
|
result.AppendFormat("{0,30}", "</Flexible>" + Environment.NewLine);
|
|
}
|
|
|
|
if (Sculpt != null)
|
|
{
|
|
result.AppendFormat("{0,30}", "<Sculpt>" + Environment.NewLine);
|
|
GenericTypeDecoder(Sculpt, ref result);
|
|
result.AppendFormat("{0,30}", "</Sculpt>" + Environment.NewLine);
|
|
}
|
|
|
|
if (Light != null)
|
|
{
|
|
result.AppendFormat("{0,30}", "<Light>" + Environment.NewLine);
|
|
GenericTypeDecoder(Light, ref result);
|
|
result.AppendFormat("{0,30}", "</Light>" + Environment.NewLine);
|
|
}
|
|
|
|
result.AppendFormat("{0,30}", "</ExtraParams>");
|
|
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}", "<ParticleSystem>" + Environment.NewLine);
|
|
GenericTypeDecoder(ParticleSys, ref result);
|
|
result.AppendFormat("{0,30}", "</ParticleSystem>");
|
|
|
|
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(" <Params>");
|
|
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("</Params>");
|
|
|
|
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}", " <TextureEntry>" + Environment.NewLine);
|
|
if (te.DefaultTexture != null)
|
|
{
|
|
result.AppendFormat("{0,30}", " <DefaultTexture>" + Environment.NewLine);
|
|
GenericFieldsDecoder(te.DefaultTexture, ref result);
|
|
GenericPropertiesDecoder(te.DefaultTexture, ref result);
|
|
result.AppendFormat("{0,30}", " </DefaultTexture>" + Environment.NewLine);
|
|
}
|
|
result.AppendFormat("{0,30}", " <FaceTextures>" + 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}", " </FaceTextures>" + Environment.NewLine);
|
|
result.AppendFormat("{0,30}", "</TextureEntry>");
|
|
|
|
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}", " <TextureEntry>" + Environment.NewLine);
|
|
if (te.DefaultTexture != null)
|
|
{
|
|
result.AppendFormat("{0,30}", " <DefaultTexture>" + Environment.NewLine);
|
|
GenericFieldsDecoder(te.DefaultTexture, ref result);
|
|
GenericPropertiesDecoder(te.DefaultTexture, ref result);
|
|
result.AppendFormat("{0,30}", " </DefaultTexture>" + Environment.NewLine);
|
|
}
|
|
result.AppendFormat("{0,30}", " <FaceTextures>" + 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}", " </FaceTextures>" + Environment.NewLine);
|
|
result.AppendFormat("{0,30}", "</TextureEntry>");
|
|
|
|
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<UUID, string> animsDict = Animations.ToDictionary();
|
|
if (animsDict.ContainsKey((UUID)fieldData))
|
|
animConst = animsDict[(UUID)fieldData];
|
|
return String.Format("{0,30}: {1,-40} [{2}]",
|
|
fieldName,
|
|
fieldData,
|
|
animConst);
|
|
}
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Creates a formatted string containing the values of a Packet
|
|
/// </summary>
|
|
/// <param name="packet">The Packet</param>
|
|
/// <returns>A formatted string of values of the nested items in the Packet object</returns>
|
|
public static string PacketToString(Packet packet)
|
|
{
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
result.AppendFormat("Packet Type: {0} http://lib.openmetaverse.org/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();
|
|
|
|
for (int i = 0; i < fields.Length; i++)
|
|
{
|
|
String special;
|
|
if (SpecialDecoder(packet.GetType().Name + "." + fieldInfo.Name + "." + fields[i].Name,
|
|
fields[i].GetValue(nestedArrayRecord), out special))
|
|
{
|
|
result.AppendLine(special);
|
|
}
|
|
else if (fields[i].FieldType.IsArray) // default for an array (probably a byte[])
|
|
{
|
|
result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine,
|
|
fields[i].Name,
|
|
Utils.BytesToString((byte[])fields[i].GetValue(nestedArrayRecord)),
|
|
/*fields[i].GetValue(nestedArrayRecord).GetType().Name*/ "String");
|
|
}
|
|
else // default for a field
|
|
{
|
|
result.AppendFormat("{0,30}: {1,-40} [{2}]" + Environment.NewLine,
|
|
fields[i].Name,
|
|
fields[i].GetValue(nestedArrayRecord),
|
|
fields[i].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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decode an IMessage object into a beautifully formatted string
|
|
/// </summary>
|
|
/// <param name="message">The IMessage object</param>
|
|
/// <param name="recurseLevel">Recursion level (used for indenting)</param>
|
|
/// <returns>A formatted string containing the names and values of the source object</returns>
|
|
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.org/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();
|
|
}
|
|
}
|
|
}
|