509 lines
15 KiB
C#
509 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Xml;
|
|
using Decal.Adapter.NetParser;
|
|
using Decal.Interop.Net;
|
|
|
|
namespace Decal.Adapter;
|
|
|
|
/// <summary>
|
|
/// Protocol Message Data
|
|
/// </summary>
|
|
public class Message : MessageStruct
|
|
{
|
|
private static Dictionary<int, MemberParser> mRecv;
|
|
|
|
private static Dictionary<int, MemberParser> mSend;
|
|
|
|
private static Dictionary<string, MemberParser> mTypes;
|
|
|
|
private int mType;
|
|
|
|
internal MessageStruct mStruct;
|
|
|
|
/// <summary>
|
|
/// Message Type
|
|
/// </summary>
|
|
public int Type => mType;
|
|
|
|
/// <summary>
|
|
/// Returns the number of fields (or vector length)
|
|
/// </summary>
|
|
public override int Count => mStruct.Count;
|
|
|
|
/// <summary>
|
|
/// Returns the specified field data
|
|
/// </summary>
|
|
/// <param name="index">Field index</param>
|
|
/// <returns>Field value</returns>
|
|
public override object this[int index] => mStruct[index];
|
|
|
|
/// <summary>
|
|
/// Returns the specified field data
|
|
/// </summary>
|
|
/// <param name="name">Field name</param>
|
|
/// <returns>Field value</returns>
|
|
public override object this[string name] => mStruct[name];
|
|
|
|
/// <summary>
|
|
/// Returns the raw bytes for this field
|
|
/// </summary>
|
|
public override byte[] RawData => mStruct.RawData;
|
|
|
|
/// <summary>
|
|
/// Returns the next object in the (parent) vector
|
|
/// </summary>
|
|
public override object Next => mStruct.Next;
|
|
|
|
/// <summary>
|
|
/// Returns the parent field
|
|
/// </summary>
|
|
public override MessageStruct Parent => mStruct.Parent;
|
|
|
|
internal Message()
|
|
{
|
|
}
|
|
|
|
internal Message(IMessage2 msg, MessageDirection dir)
|
|
{
|
|
mType = BitConverter.ToInt32(msg.RawData, 0);
|
|
mStruct = new MessageStruct(msg.RawData, 4, GetParser(mType, dir));
|
|
}
|
|
|
|
internal Message(byte[] Data, MemberParser Parser)
|
|
{
|
|
mType = BitConverter.ToInt32(Data, 0);
|
|
mStruct = new MessageStruct(Data, 4, Parser);
|
|
}
|
|
|
|
internal Message(Message Source)
|
|
{
|
|
mType = Source.mType;
|
|
mStruct = Source.mStruct;
|
|
}
|
|
|
|
internal static MemberParser GetParser(int type, MessageDirection dir)
|
|
{
|
|
MemberParser result = null;
|
|
switch (dir)
|
|
{
|
|
case MessageDirection.Inbound:
|
|
if (mRecv.ContainsKey(type))
|
|
{
|
|
result = mRecv[type];
|
|
}
|
|
break;
|
|
case MessageDirection.Outbound:
|
|
if (mSend.ContainsKey(type))
|
|
{
|
|
result = mSend[type];
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the field name for the specified index
|
|
/// </summary>
|
|
/// <param name="index">Field index</param>
|
|
/// <returns>Name of the field</returns>
|
|
public override string Name(int index)
|
|
{
|
|
return mStruct.Name(index);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the passed in value?
|
|
/// </summary>
|
|
/// <param name="memberName"></param>
|
|
/// <returns></returns>
|
|
public override string Name(string memberName)
|
|
{
|
|
return mStruct.Name(memberName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the specified child structure
|
|
/// </summary>
|
|
/// <param name="index">Field index</param>
|
|
/// <returns>MessageStruct for the specified field</returns>
|
|
public override MessageStruct Struct(int index)
|
|
{
|
|
return mStruct.Struct(index);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the specified child structure
|
|
/// </summary>
|
|
/// <param name="name">Field name</param>
|
|
/// <returns>MessageStruct for the specified field</returns>
|
|
public override MessageStruct Struct(string name)
|
|
{
|
|
return mStruct.Struct(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the specified field value
|
|
/// </summary>
|
|
/// <typeparam name="FieldType">Type of the field</typeparam>
|
|
/// <param name="index">Field index</param>
|
|
/// <returns>Field value cast to the specified FieldType</returns>
|
|
public override FieldType Value<FieldType>(int index)
|
|
{
|
|
return mStruct.Value<FieldType>(index);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the specified field value
|
|
/// </summary>
|
|
/// <typeparam name="FieldType">Type of the field</typeparam>
|
|
/// <param name="name">Field name</param>
|
|
/// <returns>Field value cast to the specified FieldType</returns>
|
|
public override FieldType Value<FieldType>(string name)
|
|
{
|
|
return mStruct.Value<FieldType>(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the raw bytes of the specified field
|
|
/// </summary>
|
|
/// <param name="index">Field index</param>
|
|
/// <returns>Raw field value</returns>
|
|
public override byte[] RawValue(int index)
|
|
{
|
|
return mStruct.RawValue(index);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the raw bytes of the specified field
|
|
/// </summary>
|
|
/// <param name="name">Field name</param>
|
|
/// <returns>Raw field value</returns>
|
|
public override byte[] RawValue(string name)
|
|
{
|
|
return mStruct.RawValue(name);
|
|
}
|
|
|
|
internal static void Initialize(string xmlFile)
|
|
{
|
|
mRecv = new Dictionary<int, MemberParser>();
|
|
mSend = new Dictionary<int, MemberParser>();
|
|
mTypes = new Dictionary<string, MemberParser>();
|
|
XmlDocument xmlDocument = new XmlDocument();
|
|
xmlDocument.Load(xmlFile);
|
|
foreach (XmlNode item in xmlDocument.SelectNodes("/schema/datatypes/type"))
|
|
{
|
|
ParseType(item, xmlDocument);
|
|
}
|
|
foreach (XmlNode item2 in xmlDocument.SelectNodes("/schema/messages/message"))
|
|
{
|
|
ParseMessage(item2, xmlDocument);
|
|
}
|
|
}
|
|
|
|
private static void ParseType(XmlNode node, XmlDocument doc)
|
|
{
|
|
XmlAttribute xmlAttribute = node.Attributes["name"];
|
|
if (xmlAttribute == null)
|
|
{
|
|
return;
|
|
}
|
|
string value = xmlAttribute.Value;
|
|
if (mTypes.ContainsKey(value))
|
|
{
|
|
return;
|
|
}
|
|
MemberParser memberParser = new MemberParser();
|
|
mTypes.Add(value, memberParser);
|
|
xmlAttribute = node.Attributes["primitive"];
|
|
if (xmlAttribute != null && string.Compare(xmlAttribute.Value, "true", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
if (string.Compare(value, "BYTE", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.BYTE;
|
|
}
|
|
else if (string.Compare(value, "WORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.WORD;
|
|
}
|
|
else if (string.Compare(value, "PackedWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.PackedWORD;
|
|
}
|
|
else if (string.Compare(value, "DWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.DWORD;
|
|
}
|
|
else if (string.Compare(value, "PackedDWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.PackedDWORD;
|
|
}
|
|
else if (string.Compare(value, "QWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.QWORD;
|
|
}
|
|
else if (string.Compare(value, "float", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.@float;
|
|
}
|
|
else if (string.Compare(value, "double", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.@double;
|
|
}
|
|
else if (string.Compare(value, "String", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.String;
|
|
}
|
|
else if (string.Compare(value, "WString", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser.MemberType = MemberParserType.WString;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memberParser.MemberType = MemberParserType.Struct;
|
|
memberParser.Child = ParseStruct(node.ChildNodes, doc);
|
|
}
|
|
}
|
|
|
|
private static void ParseMessage(XmlNode node, XmlDocument doc)
|
|
{
|
|
XmlAttribute xmlAttribute = node.Attributes["type"];
|
|
if (xmlAttribute == null)
|
|
{
|
|
return;
|
|
}
|
|
string text = xmlAttribute.Value;
|
|
if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X'))
|
|
{
|
|
text = text.Substring(2);
|
|
}
|
|
int key = int.Parse(text, NumberStyles.HexNumber);
|
|
bool flag = true;
|
|
bool flag2 = false;
|
|
xmlAttribute = node.Attributes["direction"];
|
|
if (xmlAttribute != null)
|
|
{
|
|
if (string.Compare(xmlAttribute.Value, "outbound", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
flag = false;
|
|
flag2 = true;
|
|
}
|
|
else if (string.Compare(xmlAttribute.Value, "both", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
flag2 = true;
|
|
}
|
|
}
|
|
if ((flag && !mRecv.ContainsKey(key)) || (flag2 && !mSend.ContainsKey(key)))
|
|
{
|
|
MemberParser value = ParseStruct(node.ChildNodes, doc);
|
|
if (flag && !mRecv.ContainsKey(key))
|
|
{
|
|
mRecv.Add(key, value);
|
|
}
|
|
if (flag2 && !mSend.ContainsKey(key))
|
|
{
|
|
mSend.Add(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static MemberParser ParseStruct(XmlNodeList nodes, XmlDocument doc)
|
|
{
|
|
MemberParser memberParser = new MemberParser();
|
|
MemberParser memberParser2 = memberParser;
|
|
int count = nodes.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
XmlNode xmlNode = nodes[i];
|
|
if (xmlNode.NodeType != XmlNodeType.Element)
|
|
{
|
|
continue;
|
|
}
|
|
string name = xmlNode.Name;
|
|
if (string.Compare(name, "field", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
XmlAttribute xmlAttribute = xmlNode.Attributes["type"];
|
|
if (xmlAttribute != null)
|
|
{
|
|
string value = xmlAttribute.Value;
|
|
if (!mTypes.ContainsKey(value))
|
|
{
|
|
ParseType(doc.SelectSingleNode("/schema/datatypes/type[@name='" + value + "']"), doc);
|
|
}
|
|
memberParser2.Next = new MemberParser(mTypes[value]);
|
|
memberParser2 = memberParser2.Next;
|
|
memberParser2.MemberName = xmlNode.Attributes["name"].Value;
|
|
}
|
|
}
|
|
else if (string.Compare(name, "maskmap", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
XmlAttribute xmlAttribute = xmlNode.Attributes["xor"];
|
|
long conditionXor = 0L;
|
|
string value;
|
|
if (xmlAttribute != null)
|
|
{
|
|
value = xmlAttribute.Value;
|
|
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
|
|
{
|
|
value = value.Substring(2);
|
|
}
|
|
conditionXor = long.Parse(value, NumberStyles.HexNumber);
|
|
}
|
|
value = xmlNode.Attributes["name"].Value;
|
|
memberParser2.Next = ParseMaskMap(xmlNode.ChildNodes, doc);
|
|
while (memberParser2.Next != null)
|
|
{
|
|
memberParser2 = memberParser2.Next;
|
|
memberParser2.Condition = MemberParserCondition.NE;
|
|
memberParser2.ConditionResult = 0L;
|
|
memberParser2.ConditionField = value;
|
|
memberParser2.ConditionXor = conditionXor;
|
|
}
|
|
}
|
|
else if (string.Compare(name, "switch", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
string value = xmlNode.Attributes["name"].Value;
|
|
XmlAttribute xmlAttribute = xmlNode.Attributes["mask"];
|
|
long conditionXor = -1L;
|
|
if (xmlAttribute != null)
|
|
{
|
|
value = xmlAttribute.Value;
|
|
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
|
|
{
|
|
value = value.Substring(2);
|
|
}
|
|
conditionXor = long.Parse(value, NumberStyles.HexNumber);
|
|
}
|
|
memberParser2.Next = ParseSwitch(xmlNode.ChildNodes, doc);
|
|
while (memberParser2.Next != null)
|
|
{
|
|
memberParser2 = memberParser2.Next;
|
|
memberParser2.Condition = MemberParserCondition.EQ;
|
|
memberParser2.ConditionField = value;
|
|
memberParser2.ConditionAnd = conditionXor;
|
|
}
|
|
}
|
|
else if (string.Compare(name, "vector", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser2.Next = new MemberParser();
|
|
memberParser2 = memberParser2.Next;
|
|
memberParser2.MemberType = MemberParserType.Vector;
|
|
memberParser2.MemberName = xmlNode.Attributes["name"].Value;
|
|
memberParser2.LengthField = xmlNode.Attributes["length"].Value;
|
|
XmlAttribute xmlAttribute = xmlNode.Attributes["mask"];
|
|
long conditionXor = -1L;
|
|
if (xmlAttribute != null)
|
|
{
|
|
string value = xmlAttribute.Value;
|
|
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
|
|
{
|
|
value = value.Substring(2);
|
|
}
|
|
conditionXor = long.Parse(value, NumberStyles.HexNumber);
|
|
}
|
|
memberParser2.LengthMask = conditionXor;
|
|
xmlAttribute = xmlNode.Attributes["skip"];
|
|
int num = 0;
|
|
if (xmlAttribute != null)
|
|
{
|
|
string value = xmlAttribute.Value;
|
|
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
|
|
{
|
|
value = value.Substring(2);
|
|
}
|
|
num = int.Parse(value, NumberStyles.HexNumber);
|
|
}
|
|
memberParser2.LengthDelta = -num;
|
|
memberParser2.Child = ParseStruct(xmlNode.ChildNodes, doc);
|
|
}
|
|
else if (string.Compare(name, "align", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
string value = xmlNode.Attributes["type"].Value;
|
|
if (string.Compare(value, "WORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser2.PostAlignment = 2;
|
|
}
|
|
else if (string.Compare(value, "DWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser2.PostAlignment = 4;
|
|
}
|
|
else if (string.Compare(value, "QWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
|
|
{
|
|
memberParser2.PostAlignment = 8;
|
|
}
|
|
}
|
|
}
|
|
if (memberParser.PostAlignment != 0 && memberParser.Next != null)
|
|
{
|
|
memberParser.Next.PreAlignment = memberParser.PostAlignment;
|
|
}
|
|
return memberParser.Next;
|
|
}
|
|
|
|
private static MemberParser ParseMaskMap(XmlNodeList nodes, XmlDocument doc)
|
|
{
|
|
MemberParser memberParser = new MemberParser();
|
|
MemberParser memberParser2 = memberParser;
|
|
int count = nodes.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
XmlNode xmlNode = nodes[i];
|
|
if (xmlNode.NodeType != XmlNodeType.Element || string.Compare(xmlNode.Name, "mask", ignoreCase: true, CultureInfo.InvariantCulture) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
long conditionAnd = 0L;
|
|
XmlAttribute xmlAttribute = xmlNode.Attributes["value"];
|
|
if (xmlAttribute != null)
|
|
{
|
|
string text = xmlAttribute.Value;
|
|
if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X'))
|
|
{
|
|
text = text.Substring(2);
|
|
}
|
|
conditionAnd = long.Parse(text, NumberStyles.HexNumber);
|
|
}
|
|
memberParser2.Next = new MemberParser();
|
|
memberParser2 = memberParser2.Next;
|
|
memberParser2.MemberType = MemberParserType.Case;
|
|
memberParser2.ConditionAnd = conditionAnd;
|
|
memberParser2.Child = ParseStruct(xmlNode.ChildNodes, doc);
|
|
}
|
|
return memberParser.Next;
|
|
}
|
|
|
|
private static MemberParser ParseSwitch(XmlNodeList nodes, XmlDocument doc)
|
|
{
|
|
MemberParser memberParser = new MemberParser();
|
|
MemberParser memberParser2 = memberParser;
|
|
int count = nodes.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
XmlNode xmlNode = nodes[i];
|
|
if (xmlNode.NodeType != XmlNodeType.Element || string.Compare(xmlNode.Name, "case", ignoreCase: true, CultureInfo.InvariantCulture) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
long conditionResult = 0L;
|
|
XmlAttribute xmlAttribute = xmlNode.Attributes["value"];
|
|
if (xmlAttribute != null)
|
|
{
|
|
string text = xmlAttribute.Value;
|
|
if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X'))
|
|
{
|
|
text = text.Substring(2);
|
|
}
|
|
conditionResult = long.Parse(text, NumberStyles.HexNumber);
|
|
}
|
|
memberParser2.Next = new MemberParser();
|
|
memberParser2 = memberParser2.Next;
|
|
memberParser2.MemberType = MemberParserType.Case;
|
|
memberParser2.ConditionResult = conditionResult;
|
|
memberParser2.Child = ParseStruct(xmlNode.ChildNodes, doc);
|
|
}
|
|
return memberParser.Next;
|
|
}
|
|
}
|