From 3981091915d0b8191e261ce5c1e78586f3e7fb68 Mon Sep 17 00:00:00 2001 From: BtbN Date: Sun, 21 Apr 2019 02:27:07 +0200 Subject: [PATCH] Refactor to avoid reflection --- .../KBinXML/{EnumHelpers.cs => Helpers.cs} | 205 +++-- eAmuseCore/KBinXML/KBinXML.cs | 161 ++-- eAmuseCore/KBinXML/TypeHelpers.cs | 153 ---- eAmuseCore/KBinXML/XmlTypes.cs | 796 +++++------------- eAmuseTest/Program.cs | 47 +- 5 files changed, 444 insertions(+), 918 deletions(-) rename eAmuseCore/KBinXML/{EnumHelpers.cs => Helpers.cs} (59%) delete mode 100644 eAmuseCore/KBinXML/TypeHelpers.cs diff --git a/eAmuseCore/KBinXML/EnumHelpers.cs b/eAmuseCore/KBinXML/Helpers.cs similarity index 59% rename from eAmuseCore/KBinXML/EnumHelpers.cs rename to eAmuseCore/KBinXML/Helpers.cs index 3d9e0fd..f15c281 100644 --- a/eAmuseCore/KBinXML/EnumHelpers.cs +++ b/eAmuseCore/KBinXML/Helpers.cs @@ -11,11 +11,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(8); - input = input.Skip(8); + input = input.Take(8); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToUInt64(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToUInt64(input.ToArray(), 0); } } @@ -23,11 +22,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(8); - input = input.Skip(8); + input = input.Take(8); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToInt64(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToInt64(input.ToArray(), 0); } } @@ -35,11 +33,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(4); - input = input.Skip(4); + input = input.Take(4); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToUInt32(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToUInt32(input.ToArray(), 0); } } @@ -47,11 +44,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(4); - input = input.Skip(4); + input = input.Take(4); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToInt32(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToInt32(input.ToArray(), 0); } } @@ -59,11 +55,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(2); - input = input.Skip(2); + input = input.Take(2); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToUInt16(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToUInt16(input.ToArray(), 0); } } @@ -71,11 +66,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(2); - input = input.Skip(2); + input = input.Take(2); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToInt16(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToInt16(input.ToArray(), 0); } } @@ -93,11 +87,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(4); - input = input.Skip(4); + input = input.Take(4); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToSingle(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToSingle(input.ToArray(), 0); } } @@ -105,11 +98,10 @@ namespace eAmuseCore.KBinXML.Helpers { for (int i = 0; i < count; ++i) { - var state = input.Take(8); - input = input.Skip(8); + input = input.Take(8); if (BitConverter.IsLittleEndian) - state = state.Reverse(); - yield return BitConverter.ToDouble(state.ToArray(), 0); + input = input.Reverse(); + yield return BitConverter.ToDouble(input.ToArray(), 0); } } @@ -173,31 +165,6 @@ namespace eAmuseCore.KBinXML.Helpers list.Add(unchecked((byte)data)); } - public static void AddU32(this List list, uint data) - { - IEnumerable bytes = BitConverter.GetBytes(data); - if (BitConverter.IsLittleEndian) - bytes = bytes.Reverse(); - list.AddRange(bytes); - } - - public static void AddS32(this List list, int data) - { - IEnumerable bytes = BitConverter.GetBytes(data); - if (BitConverter.IsLittleEndian) - bytes = bytes.Reverse(); - list.AddRange(bytes); - } - - public static void Realign(this List list, int alignment = 4) - { - int align = alignment - (list.Count % alignment); - if (align == alignment) - return; - while (align-- > 0) - list.Add(0); - } - public static void AddRangeAligned(this List list, IEnumerable data, int alignment = 4) { list.AddRange(data); @@ -224,4 +191,128 @@ namespace eAmuseCore.KBinXML.Helpers return encoding.GetString(data, 0, data.Length - 1); // drop final null byte } } + + public static class ListHelpers + { + public static void AddU32(this List list, uint data) + { + IEnumerable bytes = BitConverter.GetBytes(data); + if (BitConverter.IsLittleEndian) + bytes = bytes.Reverse(); + list.AddRange(bytes); + } + + public static void AddS32(this List list, int data) + { + IEnumerable bytes = BitConverter.GetBytes(data); + if (BitConverter.IsLittleEndian) + bytes = bytes.Reverse(); + list.AddRange(bytes); + } + + public static void Realign(this List list, int alignment = 4) + { + int align = alignment - (list.Count % alignment); + if (align == alignment) + return; + while (align-- > 0) + list.Add(0); + } + } + + public static class PrimHelpers + { + public static byte[] ToBytes(this byte i) + { + return new byte[] { i }; + } + + public static byte[] ToBytes(this sbyte i) + { + return new byte[] { unchecked((byte)i) }; + } + + public static byte[] ToBytes(this short i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static byte[] ToBytes(this ushort i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static byte[] ToBytes(this int i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static byte[] ToBytes(this uint i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static byte[] ToBytes(this long i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static byte[] ToBytes(this ulong i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static byte[] ToBytes(this float i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static byte[] ToBytes(this double i) + { + byte[] res = BitConverter.GetBytes(i); + if (BitConverter.IsLittleEndian) + Array.Reverse(res); + return res; + } + + public static bool ToBool(this string s) + { + try + { + return Convert.ToBoolean(s); + } + catch(FormatException) + { + try + { + return Convert.ToBoolean(Convert.ToInt32(s)); + } + catch(FormatException) + { + return false; + } + } + } + } } diff --git a/eAmuseCore/KBinXML/KBinXML.cs b/eAmuseCore/KBinXML/KBinXML.cs index 97fa9e8..0af414b 100644 --- a/eAmuseCore/KBinXML/KBinXML.cs +++ b/eAmuseCore/KBinXML/KBinXML.cs @@ -17,7 +17,6 @@ namespace eAmuseCore.KBinXML static KBinXML() { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - XmlTypes.XmlTypes.RegisterAll(); } private static Encoding GetEncoding(byte sig) @@ -105,7 +104,7 @@ namespace eAmuseCore.KBinXML GenerateNode(Document.Root); - nodeList.AddU8(XmlTypes.XmlTypes.SectionEndType | 64); + nodeList.AddU8(XmlType.SectionEndType | 64); nodeList.Realign(); header.AddU32((uint)nodeList.Count); @@ -188,23 +187,65 @@ namespace eAmuseCore.KBinXML dataList.Realign(); } + private IEnumerable GetNodeData(XElement node, byte nodeType, XmlType xmlType, int count) + { + if (nodeType == XmlType.StrType) + { + if (count != 1) + throw new FormatException("String value cannot have a count != 1."); + + return BinEncoding.GetBytes(node.Value).Concat(new byte[] { 0 }); + } + else if (nodeType == XmlType.BinType) + { + string val = node.Value; + if ((val.Length % 2) != 0) + throw new ArgumentException("Hex string needs to consist of pairs of two chars."); + + byte[] res = new byte[val.Length / 2]; + for (int i = 0, j = 0; i < val.Length; i += 2, ++j) + res[j] = Convert.ToByte(val.Substring(i, 2), 16); + + return res; + } + else + { + IEnumerable parts = node.Value.Split(' ').AsEnumerable(); + if (parts.Count() != count * xmlType.Count) + throw new ArgumentException("Node value does not have required amount of fields.", "node"); + + IEnumerable res = Enumerable.Empty(); + for (int i = 0; i < count; ++i) + { + res = res.Concat( + xmlType.KFromString( + string.Join(" ", parts.Take(xmlType.Count)))); + parts = parts.Skip(xmlType.Count); + } + + return res; + } + } + private void GenerateNode(XElement node) { if (NodeIsMixed(node)) throw new ArgumentException("Nodes with mixed elements/text are not supported.", "node"); XAttribute nodeTypeXAttr = node.Attribute("__type"); - KValueAttribute nodeTypeAttrs; + byte nodeType; + XmlType xmlType = null; if (nodeTypeXAttr != null) { - nodeTypeAttrs = KValueAttribute.GetAttrByName(nodeTypeXAttr.Value.ToLower()); + nodeType = XmlType.GetIdByName(nodeTypeXAttr.Value.ToLower()); + xmlType = XmlType.GetByType(nodeType); } else { if (node.IsEmpty || node.HasElements) - nodeTypeAttrs = KValueAttribute.GetAttrByType(XmlTypes.XmlTypes.VoidType); + nodeType = XmlType.VoidType; else - nodeTypeAttrs = KValueAttribute.GetAttrByType(XmlTypes.XmlTypes.StrType); + nodeType = XmlType.StrType; } bool isArray = false; @@ -216,32 +257,14 @@ namespace eAmuseCore.KBinXML isArray = true; } - nodeList.AddU8((byte)(nodeTypeAttrs.NodeType | (isArray ? 64 : 0))); + nodeList.AddU8((byte)(nodeType | (isArray ? 64 : 0))); AddNodeName(node.Name.LocalName); - if (nodeTypeAttrs.NodeType != XmlTypes.XmlTypes.VoidType) + if (nodeType != XmlType.VoidType) { - Type valueType = XmlTypes.XmlTypes.GetByType(nodeTypeAttrs.NodeType); + IEnumerable data = GetNodeData(node, nodeType, xmlType, count); - IEnumerable data; - if (nodeTypeAttrs.NodeType == XmlTypes.XmlTypes.StrType) - { - if (count != 1) - throw new FormatException("String value cannot have a count != 1."); - - data = BinEncoding.GetBytes(node.Value).Concat(new byte[] { 0 }); - } - else if (nodeTypeAttrs.NodeType == XmlTypes.XmlTypes.BinType) - { - data = XmlTypes.Bin.FromString(node.Value).ToBytes(); - } - else - { - IKValue kValue = XmlTypes.XmlTypes.KValueFromString(valueType, node.Value, count); - data = kValue.ToBytes(); - } - - if (isArray || nodeTypeAttrs.Count < 0) + if (isArray || xmlType.Count < 0) { dataList.AddU32((uint)data.Count()); dataList.AddRangeAligned(data); @@ -257,7 +280,7 @@ namespace eAmuseCore.KBinXML if (new[] { "__type", "__size", "__count" }.Contains(attr.Name.LocalName)) continue; - nodeList.AddU8(XmlTypes.XmlTypes.AttrType); + nodeList.AddU8(XmlType.AttrType); AddNodeName(attr.Name.LocalName); AddStringAligned(attr.Value); @@ -266,7 +289,7 @@ namespace eAmuseCore.KBinXML foreach (XElement child in node.Elements()) GenerateNode(child); - nodeList.AddU8(XmlTypes.XmlTypes.NodeEndType | 64); + nodeList.AddU8(XmlType.NodeEndType | 64); } private void Parse() @@ -352,26 +375,52 @@ namespace eAmuseCore.KBinXML } } - void SetNodeValue(XElement node, IEnumerable data, KValueAttribute nodeAttrs, int varCount, int arrCount) + void SetNodeValue(XElement node, byte nodeType, XmlType xmlType, bool isArray) { - if (nodeAttrs.NodeType == XmlTypes.XmlTypes.BinType) + int varCount = xmlType.Count; + int arrCount = 1; + if (varCount < 0) + { + varCount = dataBuf.FirstS32(); + dataBuf = dataBuf.Skip(4); + isArray = true; + } + else if (isArray) + { + arrCount = dataBuf.FirstS32() / (xmlType.Size * xmlType.Count); + dataBuf = dataBuf.Skip(4); + node.SetAttributeValue("__count", arrCount); + } + int totCount = arrCount * varCount; + int totSize = totCount * xmlType.Size; + + IEnumerable data = TakeDataAligned(totSize, isArray); + + if (nodeType == XmlType.BinType) { node.SetAttributeValue("__size", varCount); - node.SetValue(XmlTypes.Bin.FromBytes(data)); + node.SetValue(string.Join("", data.Select(b => Convert.ToString(b, 16).PadLeft(2, '0')))); } - else if (nodeAttrs.NodeType == XmlTypes.XmlTypes.StrType) + else if (nodeType == XmlType.StrType) { - node.SetValue(XmlTypes.Str.FromBytes(data, BinEncoding)); + byte[] strData = data.ToArray(); + node.SetValue(BinEncoding.GetString(strData, 0, strData.Length - 1)); } else { - node.SetValue(XmlTypes.XmlTypes.MakeNodeFromBytes(nodeAttrs, arrCount, data)); + string[] parts = new string[arrCount]; + for (int i = 0; i < arrCount; ++i) + { + parts[i] = xmlType.KToString(data.Take(xmlType.Size)); + data = data.Skip(xmlType.Size); + } + node.SetValue(string.Join(" ", parts)); } } private string GetNodeName(byte nodeType) { - if (nodeType != XmlTypes.XmlTypes.NodeEndType && nodeType != XmlTypes.XmlTypes.SectionEndType) + if (nodeType != XmlType.NodeEndType && nodeType != XmlType.SectionEndType) { if (compressed) { @@ -415,30 +464,30 @@ namespace eAmuseCore.KBinXML string name = GetNodeName(nodeType); - KValueAttribute nodeAttrs = null; + XmlType xmlType = null; bool startNode = false; switch (nodeType) { - case XmlTypes.XmlTypes.AttrType: + case XmlType.AttrType: string attrVal = EnumHelpers.TakeStringAligned(ref dataBuf, BinEncoding); node.SetAttributeValue(name, attrVal); break; - case XmlTypes.XmlTypes.NodeEndType: + case XmlType.NodeEndType: node = node.Parent; break; - case XmlTypes.XmlTypes.SectionEndType: + case XmlType.SectionEndType: nodesLeft = false; break; - case XmlTypes.XmlTypes.VoidType: + case XmlType.VoidType: startNode = true; break; default: - nodeAttrs = KValueAttribute.GetAttrByType(nodeType); + xmlType = XmlType.GetByType(nodeType); break; } - if (nodeAttrs == null && !startNode) + if (xmlType == null && !startNode) continue; XElement child = new XElement(name); @@ -448,28 +497,8 @@ namespace eAmuseCore.KBinXML if (startNode) continue; - node.SetAttributeValue("__type", nodeAttrs.Name); - - int varCount = nodeAttrs.Count; - int arrCount = 1; - if (varCount < 0) - { - varCount = dataBuf.FirstS32(); - dataBuf = dataBuf.Skip(4); - isArray = true; - } - else if (isArray) - { - arrCount = dataBuf.FirstS32() / (nodeAttrs.Size * nodeAttrs.Count); - dataBuf = dataBuf.Skip(4); - node.SetAttributeValue("__count", arrCount); - } - int totCount = arrCount * varCount; - int totSize = totCount * nodeAttrs.Size; - - IEnumerable data = TakeDataAligned(totSize, isArray); - - SetNodeValue(node, data, nodeAttrs, varCount, arrCount); + node.SetAttributeValue("__type", xmlType.Name); + SetNodeValue(node, nodeType, xmlType, isArray); } Document = new XDocument(fakeroot.FirstNode); diff --git a/eAmuseCore/KBinXML/TypeHelpers.cs b/eAmuseCore/KBinXML/TypeHelpers.cs deleted file mode 100644 index 62c59ed..0000000 --- a/eAmuseCore/KBinXML/TypeHelpers.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace eAmuseCore.KBinXML.Helpers -{ - public interface IKValue - { - string ToString(); - IEnumerable ToBytes(); - } - - public class KValueArray : IKValue where T : IKValue - { - private T[] values; - - public KValueArray(params T[] values) - { - this.values = values; - } - - public T this[int idx] - { - get - { - return values[idx]; - } - set - { - values[idx] = value; - } - } - - public IEnumerable AsEnumerable() - { - return values.AsEnumerable(); - } - - public IEnumerable ToBytes() - { - IEnumerable res = Enumerable.Empty(); - foreach (IKValue val in values) - res = res.Concat(val.ToBytes()); - return res; - } - - public override string ToString() - { - return string.Join(" ", values.Select(v => v.ToString())); - } - } - - [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)] - public class KValueAttribute : Attribute - { - public KValueAttribute(byte nodeType, params string[] names) - { - NodeType = nodeType; - Names = names; - } - - public byte NodeType { get; } - - public string[] Names { get; } - - public string Name => Names[0]; - - public int Count { get; set; } - - public int Size { get; set; } - - - private static readonly Dictionary nameLookupMap = new Dictionary(); - private static readonly Dictionary typeLookupMap = new Dictionary(); - - public static void Register(KValueAttribute attr) - { - typeLookupMap[attr.NodeType] = attr; - - foreach (string name in attr.Names) - nameLookupMap[name] = attr; - } - - public static KValueAttribute GetAttrByName(string name) - { - if (!nameLookupMap.ContainsKey(name)) - throw new NotImplementedException("KValue name not implemented: " + name); - return nameLookupMap[name]; - } - - public static KValueAttribute GetAttrByType(byte type) - { - if (!typeLookupMap.ContainsKey(type)) - throw new NotImplementedException("KValue type not implemented: " + type); - return typeLookupMap[type]; - } - } - - public class KValue : IKValue - { - public T Value { get; set; } - - public KValue() { } - public KValue(T value) => Value = value; - - public override string ToString() - { - return (Value != null) ? Value.ToString() : ""; - } - - public virtual IEnumerable ToBytes() - { - if (Value == null) - return Enumerable.Empty(); - - dynamic v = Value; - Type t = v.GetType(); - - if (t == typeof(byte) || t == typeof(sbyte)) - return new byte[] { unchecked((byte)v) }; - - IEnumerable res = BitConverter.GetBytes(v); - - if (BitConverter.IsLittleEndian) - res = res.Reverse(); - - return res; - } - - protected KValueAttribute KValAttr - { - get - { - var res = GetType().GetCustomAttributes(typeof(KValueAttribute), true).FirstOrDefault() as KValueAttribute; - if (res == null) - throw new InvalidOperationException("KValue types need KValueAttributes!"); - return res; - } - } - - public int NodeType => KValAttr.NodeType; - - public string[] Names => KValAttr.Names; - - public string Name => KValAttr.Name; - - public int Count => KValAttr.Count; - - public int Size => KValAttr.Size; - - public Type ValueType => typeof(T); - } -} diff --git a/eAmuseCore/KBinXML/XmlTypes.cs b/eAmuseCore/KBinXML/XmlTypes.cs index 77ab203..0990517 100644 --- a/eAmuseCore/KBinXML/XmlTypes.cs +++ b/eAmuseCore/KBinXML/XmlTypes.cs @@ -8,526 +8,41 @@ using System.Globalization; using eAmuseCore.KBinXML.Helpers; -namespace eAmuseCore.KBinXML.XmlTypes +namespace eAmuseCore.KBinXML { -#pragma warning disable IDE0060 // Remove unused parameter - [KValue(1, "void", Count = 1, Size = 0)] - public class Void : KValue + public delegate string KBinToString(IEnumerable input); + public delegate IEnumerable KBinFromString(string input); + + public class KBinConverter { - public Void(object _) => Value = null; - static public Void FromString(string input) => new Void(null); - - static public Void FromBytes(IEnumerable input) => new Void(null); - } -#pragma warning restore IDE0060 - - [KValue(2, "s8", Count = 1, Size = 1)] - public class S8 : KValue - { - public S8(sbyte value) => Value = value; - static public S8 FromString(string input) => new S8(Convert.ToSByte(input)); - static public S8 FromBytes(IEnumerable input) => new S8(input.FirstS8()); - } - - [KValue(3, "u8", Count = 1, Size = 1)] - public class U8 : KValue - { - public U8(byte value) => Value = value; - static public U8 FromString(string input) => new U8(Convert.ToByte(input)); - static public U8 FromBytes(IEnumerable input) => new U8(input.FirstU8()); - } - - [KValue(4, "s16", Count = 1, Size = 2)] - public class S16 : KValue - { - public S16(short value) => Value = value; - static public S16 FromString(string input) => new S16(Convert.ToInt16(input)); - static public S16 FromBytes(IEnumerable input) => new S16(input.FirstS16()); - } - - [KValue(5, "u16", Count = 1, Size = 2)] - public class U16 : KValue - { - public U16(ushort value) => Value = value; - static public U16 FromString(string input) => new U16(Convert.ToUInt16(input)); - static public U16 FromBytes(IEnumerable input) => new U16(input.FirstU16()); - } - - [KValue(6, "s32", Count = 1, Size = 4)] - public class S32 : KValue - { - public S32(int value) => Value = value; - static public S32 FromString(string input) => new S32(Convert.ToInt32(input)); - static public S32 FromBytes(IEnumerable input) => new S32(input.FirstS32()); - } - - [KValue(7, "u32", Count = 1, Size = 4)] - public class U32 : KValue - { - public U32(uint value) => Value = value; - static public U32 FromString(string input) => new U32(Convert.ToUInt32(input)); - static public U32 FromBytes(IEnumerable input) => new U32(input.FirstU32()); - } - - [KValue(8, "s64", Count = 1, Size = 8)] - public class S64 : KValue - { - public S64(long value) => Value = value; - static public S64 FromString(string input) => new S64(Convert.ToInt64(input)); - static public S64 FromBytes(IEnumerable input) => new S64(input.FirstS64()); - } - - [KValue(9, "u64", Count = 1, Size = 8)] - public class U64 : KValue - { - public U64(ulong value) => Value = value; - static public U64 FromString(string input) => new U64(Convert.ToUInt64(input)); - static public U64 FromBytes(IEnumerable input) => new U64(input.FirstU64()); - } - - [KValue(10, "bin", "binary", Count = -1, Size = 1)] - public class Bin : KValue - { - public Bin(byte[] value) => Value = value; - - public override string ToString() + public KBinConverter(KBinFromString fromString, KBinToString toString) { - return string.Join("", Value.Select(b => Convert.ToString(b, 16).PadLeft(2, '0'))); + KFromString = fromString; + KToString = toString; } - public override IEnumerable ToBytes() => Value; + public KBinFromString KFromString { get; } + public KBinToString KToString { get; } - static public Bin FromString(string input) - { - if ((input.Length % 2) != 0) - throw new ArgumentException("Hex string needs to consist of pairs of two chars.", "input"); - byte[] res = new byte[input.Length / 2]; - for (int i = 0, j = 0; i < input.Length; i += 2, ++j) - res[j] = Convert.ToByte(input.Substring(i, 2), 16); - return new Bin(res); - } + public static KBinConverter S8 = new KBinConverter(s => Convert.ToSByte(s).ToBytes(), b => b.FirstS8().ToString()); + public static KBinConverter U8 = new KBinConverter(s => Convert.ToByte(s).ToBytes(), b => b.FirstU8().ToString()); + public static KBinConverter S16 = new KBinConverter(s => Convert.ToInt16(s).ToBytes(), b => b.FirstS16().ToString()); + public static KBinConverter U16 = new KBinConverter(s => Convert.ToUInt16(s).ToBytes(), b => b.FirstU16().ToString()); + public static KBinConverter S32 = new KBinConverter(s => Convert.ToInt32(s).ToBytes(), b => b.FirstS32().ToString()); + public static KBinConverter U32 = new KBinConverter(s => Convert.ToUInt32(s).ToBytes(), b => b.FirstU32().ToString()); + public static KBinConverter S64 = new KBinConverter(s => Convert.ToInt64(s).ToBytes(), b => b.FirstS64().ToString()); + public static KBinConverter U64 = new KBinConverter(s => Convert.ToUInt64(s).ToBytes(), b => b.FirstU64().ToString()); - static public Bin FromBytes(IEnumerable input) => new Bin(input.ToArray()); + public static KBinConverter KFloat = new KBinConverter(s => Convert.ToSingle(s).ToBytes(), b => b.FirstF().ToString()); + public static KBinConverter KDouble = new KBinConverter(s => Convert.ToDouble(s).ToBytes(), b => b.FirstD().ToString()); + + public static KBinConverter IP4 = new KBinConverter(s => IPAddress.Parse(s).GetAddressBytes(), b => new IPAddress(b.Take(4).ToArray()).ToString()); + public static KBinConverter Bool = new KBinConverter(s => new byte[] { s.ToBool() ? (byte)1 : (byte)0 }, b => (b.FirstU8() != 0) ? "1" : "0"); + + public static KBinConverter Invalid = new KBinConverter(s => throw new InvalidOperationException(), b => throw new InvalidOperationException()); } - [KValue(11, "str", "string", Count = -1, Size = 1)] - public class Str : KValue - { - public Str(string value) => Value = value; - - public override string ToString() => Value; - - public override IEnumerable ToBytes() => Encoding.UTF8.GetBytes(Value); - - public IEnumerable ToBytes(Encoding encoding) => encoding.GetBytes(Value); - - static public Str FromString(string input) => new Str(input); - - static public Str FromBytes(IEnumerable input, Encoding encoding) - { - byte[] data = input.ToArray(); - return new Str(encoding.GetString(data, 0, data.Length - 1)); - } - } - - [KValue(12, "ip4", Count = 1, Size = 4)] - public class IP4 : KValue - { - public IP4(IPAddress value) => Value = value; - - public override string ToString() => Value.ToString(); - - public override IEnumerable ToBytes() => Value.GetAddressBytes(); - - static public IP4 FromString(string input) => new IP4(IPAddress.Parse(input)); - - static public IP4 FromBytes(IEnumerable input) => new IP4(new IPAddress(input.Take(4).ToArray())); - } - - [KValue(13, "time", Count = 1, Size = 4)] - public class Time : KValue - { - public Time(uint value) => Value = value; - static public Time FromString(string input) => new Time(Convert.ToUInt32(input)); - static public Time FromBytes(IEnumerable input) => new Time(input.FirstU32()); - } - - [KValue(14, "float", "f", Count = 1, Size = 4)] - public class KFloat : KValue - { - public KFloat(float value) => Value = value; - - public override string ToString() - { - return Value.ToString(CultureInfo.InvariantCulture); - } - - public override IEnumerable ToBytes() - { - IEnumerable res = BitConverter.GetBytes(Value); - if (BitConverter.IsLittleEndian) - res = res.Reverse(); - return res; - } - - static public KFloat FromString(string input) => new KFloat(Convert.ToSingle(input, CultureInfo.InvariantCulture)); - - static public KFloat FromBytes(IEnumerable input) => new KFloat(input.FirstF()); - } - - [KValue(15, "double", "d", Count = 1, Size = 8)] - public class KDouble : KValue - { - public KDouble(double value) => Value = value; - - public override string ToString() - { - return Value.ToString(CultureInfo.InvariantCulture); - } - - public override IEnumerable ToBytes() - { - IEnumerable res = BitConverter.GetBytes(Value); - if (BitConverter.IsLittleEndian) - res = res.Reverse(); - return res; - } - - static public KDouble FromString(string input) => new KDouble(Convert.ToDouble(input, CultureInfo.InvariantCulture)); - - static public KDouble FromBytes(IEnumerable input) => new KDouble(input.FirstD()); - } - - [KValue(16, "2s8", Count = 2, Size = 1)] - public class K2S8 : KValueArray - { - public K2S8(S8 v1, S8 v2) : base(v1, v2) { } - static public K2S8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2S8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(17, "2u8", Count = 2, Size = 1)] - public class K2U8 : KValueArray - { - public K2U8(U8 v1, U8 v2) : base(v1, v2) { } - static public K2U8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2U8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(18, "2s16", Count = 2, Size = 2)] - public class K2S16 : KValueArray - { - public K2S16(S16 v1, S16 v2) : base(v1, v2) { } - static public K2S16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2S16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(19, "2u16", Count = 2, Size = 2)] - public class K2U16 : KValueArray - { - public K2U16(U16 v1, U16 v2) : base(v1, v2) { } - static public K2U16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2U16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(20, "2s32", Count = 2, Size = 4)] - public class K2S32 : KValueArray - { - public K2S32(S32 v1, S32 v2) : base(v1, v2) { } - static public K2S32 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2S32 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(21, "2u32", Count = 2, Size = 4)] - public class K2U32 : KValueArray - { - public K2U32(U32 v1, U32 v2) : base(v1, v2) { } - static public K2U32 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2U32 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(22, "2s64", "vs64", Count = 2, Size = 8)] - public class K2S64 : KValueArray - { - public K2S64(S64 v1, S64 v2) : base(v1, v2) { } - static public K2S64 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2S64 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(23, "2u64", "vu64", Count = 2, Size = 8)] - public class K2U64 : KValueArray - { - public K2U64(U64 v1, U64 v2) : base(v1, v2) { } - static public K2U64 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2U64 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(24, "2f", Count = 2, Size = 4)] - public class K2F : KValueArray - { - public K2F(KFloat v1, KFloat v2) : base(v1, v2) { } - static public K2F FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2F FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(25, "2d", "vd", Count = 2, Size = 8)] - public class K2D : KValueArray - { - public K2D(KDouble v1, KDouble v2) : base(v1, v2) { } - static public K2D FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2D FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(26, "3s8", Count = 3, Size = 1)] - public class K3S8 : KValueArray - { - public K3S8(S8 v1, S8 v2, S8 v3) : base(v1, v2, v3) { } - static public K3S8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3S8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(27, "3u8", Count = 3, Size = 1)] - public class K3U8 : KValueArray - { - public K3U8(U8 v1, U8 v2, U8 v3) : base(v1, v2, v3) { } - static public K3U8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3U8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(28, "3s16", Count = 3, Size = 2)] - public class K3S16 : KValueArray - { - public K3S16(S16 v1, S16 v2, S16 v3) : base(v1, v2, v3) { } - static public K3S16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3S16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(29, "3u16", Count = 3, Size = 2)] - public class K3U16 : KValueArray - { - public K3U16(U16 v1, U16 v2, U16 v3) : base(v1, v2, v3) { } - static public K3U16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3U16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(30, "3s32", Count = 3, Size = 4)] - public class K3S32 : KValueArray - { - public K3S32(S32 v1, S32 v2, S32 v3) : base(v1, v2, v3) { } - static public K3S32 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3S32 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(31, "3u32", Count = 3, Size = 4)] - public class K3U32 : KValueArray - { - public K3U32(U32 v1, U32 v2, U32 v3) : base(v1, v2, v3) { } - static public K3U32 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3U32 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(32, "3s64", Count = 3, Size = 8)] - public class K3S64 : KValueArray - { - public K3S64(S64 v1, S64 v2, S64 v3) : base(v1, v2, v3) { } - static public K3S64 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3S64 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(33, "3u64", Count = 3, Size = 8)] - public class K3U64 : KValueArray - { - public K3U64(U64 v1, U64 v2, U64 v3) : base(v1, v2, v3) { } - static public K3U64 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3U64 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(34, "3f", Count = 3, Size = 4)] - public class K3F : KValueArray - { - public K3F(KFloat v1, KFloat v2, KFloat v3) : base(v1, v2, v3) { } - static public K3F FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3F FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(35, "3d", Count = 3, Size = 8)] - public class K3D : KValueArray - { - public K3D(KDouble v1, KDouble v2, KDouble v3) : base(v1, v2, v3) { } - static public K3D FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3D FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(36, "4s8", Count = 4, Size = 1)] - public class K4S8 : KValueArray - { - public K4S8(S8 v1, S8 v2, S8 v3, S8 v4) : base(v1, v2, v3, v4) { } - static public K4S8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4S8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(37, "4u8", Count = 4, Size = 1)] - public class K4U8 : KValueArray - { - public K4U8(U8 v1, U8 v2, U8 v3, U8 v4) : base(v1, v2, v3, v4) { } - static public K4U8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4U8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(38, "4s16", Count = 4, Size = 2)] - public class K4S16 : KValueArray - { - public K4S16(S16 v1, S16 v2, S16 v3, S16 v4) : base(v1, v2, v3, v4) { } - static public K4S16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4S16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(39, "4u16", Count = 4, Size = 2)] - public class K4U16 : KValueArray - { - public K4U16(U16 v1, U16 v2, U16 v3, U16 v4) : base(v1, v2, v3, v4) { } - static public K4U16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4U16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(40, "4s32", "vs32", Count = 4, Size = 4)] - public class K4S32 : KValueArray - { - public K4S32(S32 v1, S32 v2, S32 v3, S32 v4) : base(v1, v2, v3, v4) { } - static public K4S32 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4S32 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(41, "4u32", "vu32", Count = 4, Size = 4)] - public class K4U32 : KValueArray - { - public K4U32(U32 v1, U32 v2, U32 v3, U32 v4) : base(v1, v2, v3, v4) { } - static public K4U32 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4U32 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(42, "4s64", Count = 4, Size = 8)] - public class K4S64 : KValueArray - { - public K4S64(S64 v1, S64 v2, S64 v3, S64 v4) : base(v1, v2, v3, v4) { } - static public K4S64 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4S64 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(43, "4u64", Count = 4, Size = 8)] - public class K4U64 : KValueArray - { - public K4U64(U64 v1, U64 v2, U64 v3, U64 v4) : base(v1, v2, v3, v4) { } - static public K4U64 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4U64 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(44, "4f", "vf", Count = 4, Size = 4)] - public class K4F : KValueArray - { - public K4F(KFloat v1, KFloat v2, KFloat v3, KFloat v4) : base(v1, v2, v3, v4) { } - static public K4F FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4F FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(45, "4d", Count = 4, Size = 8)] - public class K4D : KValueArray - { - public K4D(KDouble v1, KDouble v2, KDouble v3, KDouble v4) : base(v1, v2, v3, v4) { } - static public K4D FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4D FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(48, "vs8", Count = 16, Size = 1)] - public class KVS8 : KValueArray - { - public KVS8(S8 v1, S8 v2, S8 v3, S8 v4, S8 v5, S8 v6, S8 v7, S8 v8, S8 v9, S8 v10, S8 v11, S8 v12, S8 v13, S8 v14, S8 v15, S8 v16) - : base(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { } - static public KVS8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public KVS8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(49, "vu8", Count = 16, Size = 1)] - public class KVU8 : KValueArray - { - public KVU8(U8 v1, U8 v2, U8 v3, U8 v4, U8 v5, U8 v6, U8 v7, U8 v8, U8 v9, U8 v10, U8 v11, U8 v12, U8 v13, U8 v14, U8 v15, U8 v16) - : base(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { } - static public KVU8 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public KVU8 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(50, "vs16", Count = 8, Size = 2)] - public class KVS16 : KValueArray - { - public KVS16(S16 v1, S16 v2, S16 v3, S16 v4, S16 v5, S16 v6, S16 v7, S16 v8) - : base(v1, v2, v3, v4, v5, v6, v7, v8) { } - static public KVS16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public KVS16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(51, "vu16", Count = 8, Size = 2)] - public class KVU16 : KValueArray - { - public KVU16(U16 v1, U16 v2, U16 v3, U16 v4, U16 v5, U16 v6, U16 v7, U16 v8) - : base(v1, v2, v3, v4, v5, v6, v7, v8) { } - static public KVU16 FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public KVU16 FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(52, "bool", "b", Count = 1, Size = 1)] - public class KBool : KValue - { - public KBool(bool value) => Value = value; - public override string ToString() => Value ? "1" : "0"; - public override IEnumerable ToBytes() => new byte[] { (byte)(Value ? 1 : 0) }; - - static public KBool FromString(string input) - { - if (input == "0") - return new KBool(false); - else if (input == "1") - return new KBool(true); - else - return new KBool(Convert.ToBoolean(input)); - } - - static public KBool FromBytes(IEnumerable input) => new KBool(input.FirstU8() != 0); - } - - [KValue(53, "2b", Count = 2, Size = 1)] - public class K2B : KValueArray - { - public K2B(KBool v1, KBool v2) : base(v1, v2) { } - static public K2B FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K2B FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(54, "3b", Count = 3, Size = 1)] - public class K3B : KValueArray - { - public K3B(KBool v1, KBool v2, KBool v3) : base(v1, v2, v3) { } - static public K3B FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K3B FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(55, "4b", Count = 4, Size = 1)] - public class K4B : KValueArray - { - public K4B(KBool v1, KBool v2, KBool v3, KBool v4) : base(v1, v2, v3, v4) { } - static public K4B FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public K4B FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - [KValue(56, "vb", Count = 16, Size = 1)] - public class KVB : KValueArray - { - public KVB(KBool v1, KBool v2, KBool v3, KBool v4, KBool v5, KBool v6, KBool v7, KBool v8, KBool v9, KBool v10, KBool v11, KBool v12, KBool v13, KBool v14, KBool v15, KBool v16) - : base(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { } - static public KVB FromString(string input) => XmlTypes.ValueListTypeFromString(input); - static public KVB FromBytes(IEnumerable input) => XmlTypes.ValueListTypeFromBytes(input); - } - - public static class XmlTypes + public class XmlType { public const int VoidType = 1; public const int BinType = 10; @@ -537,112 +52,179 @@ namespace eAmuseCore.KBinXML.XmlTypes public const int NodeEndType = 190; public const int SectionEndType = 191; - private static readonly Dictionary lookupMap = new Dictionary(); + public XmlType(int size, KBinConverter converter, params string[] names) + : this(size, converter, 1, names) + { } - public static void RegisterAll() + public XmlType(int size, KBinConverter converter, int count, params string[] names) { - foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) - { - KValueAttribute attr = type.GetCustomAttribute(false); - if (attr == null) - continue; - - lookupMap[attr.NodeType] = type; - KValueAttribute.Register(attr); - } + Size = size; + Converter = converter; + Count = count; + Names = names; } - public static Type GetByType(byte type) + public int Size { get; } + private KBinConverter Converter { get; } + public int Count { get; } + public string[] Names { get; private set; } + public string Name { get => Names[0]; } + + public XmlType Rename(params string[] names) + { + Names = names; + return this; + } + + public XmlType Alias(params string[] newNames) + { + Names = Names.Concat(newNames).ToArray(); + return this; + } + + public IEnumerable KFromString(string input) + { + if (input.Length == 0) + return new byte[0]; + return Converter.KFromString(input); + } + + public string KToString(IEnumerable input) + { + if (input.Take(Size).Count() != Size) + throw new ArgumentException("input does not provide enough data", "input"); + return Converter.KToString(input); + } + + private XmlType Times(int count) + { + if (Count != 1) + throw new InvalidOperationException("Multiplying already multiplied XmlType!"); + + string[] names = Names.Select(name => count.ToString() + name).ToArray(); + int size = Size * count; + KBinFromString fromString = s => + { + string[] elems = s.Split(' '); + if (elems.Length != count) + throw new ArgumentException("input does not split into correct element count", "s"); + + IEnumerable res = Enumerable.Empty(); + foreach (string elem in elems) + res = res.Concat(Converter.KFromString(elem)); + return res; + }; + KBinToString toString = b => + { + IEnumerable data = b.Take(size); + if (data.Count() != size) + throw new ArgumentException("input does not provide enough data for all elements", "b"); + + string[] res = new string[count]; + for (int i = 0; i < count; ++i) + { + res[i] = Converter.KToString(data.Take(Size)); + data = data.Skip(Size); + } + return string.Join(" ", res); + }; + + return new XmlType(size, new KBinConverter(fromString, toString), count, names); + } + + public static XmlType S8 = new XmlType(1, KBinConverter.S8, "s8"); + public static XmlType U8 = new XmlType(1, KBinConverter.U8, "u8"); + public static XmlType S16 = new XmlType(2, KBinConverter.S16, "s16"); + public static XmlType U16 = new XmlType(2, KBinConverter.U16, "u16"); + public static XmlType S32 = new XmlType(4, KBinConverter.S32, "s32"); + public static XmlType U32 = new XmlType(4, KBinConverter.U32, "u32"); + public static XmlType S64 = new XmlType(8, KBinConverter.S64, "s64"); + public static XmlType U64 = new XmlType(8, KBinConverter.U64, "u64"); + + public static XmlType KFloat = new XmlType(4, KBinConverter.KFloat, "float", "f"); + public static XmlType KDouble = new XmlType(4, KBinConverter.KDouble, "double", "d"); + + public static XmlType Bin = new XmlType(1, KBinConverter.Invalid, -1, "bin", "binary"); + public static XmlType Str = new XmlType(1, KBinConverter.Invalid, -1, "str", "string"); + + public static XmlType IP4 = new XmlType(4, KBinConverter.IP4, "ip4"); + public static XmlType Time = new XmlType(4, KBinConverter.U32, "time"); + public static XmlType Bool = new XmlType(1, KBinConverter.Bool, "bool", "b"); + + private static readonly Dictionary lookupMap = new Dictionary() + { + [2] = S8, + [3] = U8, + [4] = S16, + [5] = U16, + [6] = S32, + [7] = U32, + [8] = S64, + [9] = U64, + [10] = Bin, + [11] = Str, + [12] = IP4, + [13] = Time, + [14] = KFloat, + [15] = KDouble, + [16] = S8.Times(2), + [17] = U8.Times(2), + [18] = S16.Times(2), + [19] = U16.Times(2), + [20] = S32.Times(2), + [21] = U32.Times(2), + [22] = S64.Times(2).Alias("vs64"), + [23] = U64.Times(2).Alias("vu64"), + [24] = KFloat.Times(2).Rename("2f"), + [25] = KDouble.Times(2).Rename("2d", "vd"), + [26] = S8.Times(3), + [27] = U8.Times(3), + [28] = S16.Times(3), + [29] = U16.Times(3), + [30] = S32.Times(3), + [31] = U32.Times(3), + [32] = S64.Times(3), + [33] = U64.Times(3), + [34] = KFloat.Times(3).Rename("3f"), + [35] = KDouble.Times(3).Rename("3d"), + [36] = S8.Times(4), + [37] = U8.Times(4), + [38] = S16.Times(4), + [39] = U16.Times(4), + [40] = S32.Times(4).Alias("vs32"), + [41] = U32.Times(4).Alias("vu32"), + [42] = S64.Times(4), + [43] = U64.Times(4), + [44] = KFloat.Times(4).Rename("4f", "vf"), + [45] = KDouble.Times(4).Rename("4d"), + [48] = S8.Times(16).Rename("vs8"), + [49] = U8.Times(16).Rename("vu8"), + [50] = S16.Times(8).Rename("vs16"), + [51] = U16.Times(8).Rename("vu16"), + [52] = Bool, + [53] = Bool.Times(2).Rename("2b"), + [54] = Bool.Times(3).Rename("3b"), + [55] = Bool.Times(4).Rename("4b"), + [56] = Bool.Times(16).Rename("vb"), + }; + + private static readonly Dictionary reverseLookupMap = lookupMap + .SelectMany(kvp => kvp.Value.Names.Select(name => new { k = name, v = kvp.Key })) + .ToDictionary(kv => kv.k, kv => kv.v); + + public static XmlType GetByType(byte type) { if (!lookupMap.ContainsKey(type)) - throw new NotImplementedException("KValue type not implemented: " + type); + throw new NotImplementedException("XmlType not implemented: " + type); return lookupMap[type]; } - public static object MakeNodeFromBytes(KValueAttribute attrs, int count, IEnumerable data) + public static byte GetIdByName(string name) { - Type valType = GetByType(attrs.NodeType); - MethodInfo fromBytes = valType.GetMethod("FromBytes", BindingFlags.Static | BindingFlags.Public); - - if (count <= 1) - { - return fromBytes.Invoke(null, new object[] { data }); - } - else - { - Type listType = typeof(KValueArray<>).MakeGenericType(valType); - int size = attrs.Size * attrs.Count; - - object[] p = new object[count]; - - for (int i = 0; i < count; i++) - { - p[i] = fromBytes.Invoke(null, new object[] { data }); - data = data.Skip(size); - } - - return Activator.CreateInstance(listType, p); - } - } - - public static IKValue KValueFromString(Type type, string input, int count) - { - Type listType = typeof(KValueArray<>).MakeGenericType(type); - KValueAttribute attr = type.GetCustomAttribute(false); - var fromString = type.GetMethod("FromString"); - - string[] vals = input.Split(' '); - IEnumerable strings = vals.AsEnumerable(); - - if (vals.Length != attr.Count * count) - throw new ArgumentException("input string had invalid field count", "input"); - - IKValue[] mainParams = new IKValue[count]; - - for (int i = 0; i < count; ++i) - { - mainParams[i] = (IKValue)fromString.Invoke(null, new object[] { string.Join(" ", strings.Take(attr.Count)) }); - strings = strings.Skip(attr.Count); - } - - if (count == 1) - return mainParams[0]; - - return (IKValue)Activator.CreateInstance(listType, mainParams); - } - - internal static T ValueListTypeFromString(string input) - { - KValueAttribute attr = typeof(T).GetCustomAttribute(false); - Type valueType = typeof(T).BaseType.GetGenericArguments()[0]; - var fromString = valueType.GetMethod("FromString"); - - string[] vals = input.Split(' '); - - if (vals.Length != attr.Count) - throw new ArgumentException("input string had invalid field count", "input"); - - object[] param = new object[attr.Count]; - for (int i = 0; i < attr.Count; ++i) - param[i] = fromString.Invoke(null, new object[] { vals[i] }); - - return (T)Activator.CreateInstance(typeof(T), param); - } - - internal static T ValueListTypeFromBytes(IEnumerable input) - { - KValueAttribute attr = typeof(T).GetCustomAttribute(false); - Type valueType = typeof(T).BaseType.GetGenericArguments()[0]; - var fromBytes = valueType.GetMethod("FromBytes"); - - object[] values = new object[attr.Count]; - for (int i = 0; i < attr.Count; ++i) - { - values[i] = fromBytes.Invoke(null, new object[] { input }); - input = input.Skip(attr.Size); - } - - return (T)Activator.CreateInstance(typeof(T), values); + name = name.ToLower(); + if (!reverseLookupMap.ContainsKey(name)) + throw new NotImplementedException("XmlType not implemented: " + name); + return reverseLookupMap[name]; } } } diff --git a/eAmuseTest/Program.cs b/eAmuseTest/Program.cs index 0689446..7195274 100644 --- a/eAmuseTest/Program.cs +++ b/eAmuseTest/Program.cs @@ -1,14 +1,13 @@ using System; using System.Text; using System.Linq; +using System.Xml.Linq; using System.Collections; using System.Collections.Generic; using eAmuseCore.Crypto; using eAmuseCore.Compression; using eAmuseCore.KBinXML; -using eAmuseCore.KBinXML; -using System.Xml.Linq; namespace eAmuseTest { @@ -16,28 +15,9 @@ namespace eAmuseTest { static void Main(string[] args) { - // string url = "/L44/8?model=L44:J:E:A:2018070901&f=gametop.get_pdata"; string compress = "lz77"; - string eamuse_info = "1-5ca39858-b503"; - // string user_agent = "EAMUSE.XRPC/1.0"; - byte[] data = new byte[] { - 0xb6, 0x5d, 0x24, 0x57, 0x42, 0x34, 0xa8, 0x8f, 0xc7, 0x6c, 0x3c, 0x05, 0xe4, 0xa1, 0xf9, 0x14, - 0x8e, 0xd1, 0xd1, 0x6a, 0xc9, 0xf7, 0x95, 0x50, 0xff, 0xe0, 0x12, 0xf0, 0xbc, 0x25, 0x21, 0x73, - 0x9a, 0x90, 0xd4, 0x5c, 0x07, 0xf6, 0x92, 0xd3, 0x72, 0x51, 0x21, 0x2a, 0xa3, 0xd1, 0x0c, 0x19, - 0x43, 0xfb, 0x9c, 0x6d, 0x7d, 0xb8, 0xcd, 0x6d, 0x20, 0x0d, 0x5d, 0xa3, 0xda, 0x91, 0x6d, 0x19, - 0x9a, 0x8d, 0x60, 0x40, 0x14, 0xda, 0x81, 0xa0, 0xcf, 0x60, 0x95, 0x57, 0xc3, 0x06, 0x86, 0x40, - 0x0f, 0xb3, 0x62, 0x4b, 0x6b, 0x71, 0x46, 0x5a, 0x38, 0x8c, 0x36, 0x08, 0x68, 0xc8, 0x60, 0xad, - 0x17, 0x0e, 0x97, 0xec, 0x69, 0xaf, 0x5e, 0x73, 0x5f, 0x55, 0xc0, 0x46, 0xbf, 0x21, 0xf1, 0x1a, - 0xbc, 0xca, 0x44, 0xa0, 0x52, 0xdf, 0x8d, 0xf4, 0x78, 0xa0, 0xe5, 0x2f, 0xa0, 0x68, 0x49, 0xd6, - 0xe0, 0xf7, 0x0c, 0x57, 0x85, 0x23, 0xb5, 0x0e, 0x3a, 0x80, 0x4b, 0x32, 0xe0, 0xaa, 0x2b, 0x50, - 0xcf, 0x19, 0xb8, 0x1b, 0x96, 0xc9, 0x81, 0xa3, 0x49, 0x50, 0x9d, 0xe4, 0x9d, 0xb8, 0x5d, 0xff, - 0xee, 0xbb, 0xf5, 0x58, 0x78, 0x68, 0x7a, 0xbc, 0x3c, 0x79, 0x82, 0x51, 0xd9, 0x26, 0x1e, 0x5d, - 0x26, 0x95, 0x5d, 0x66, 0x1c, 0x87, 0xda, 0x5c, 0x4e, 0x55, 0x9a, 0xaf, 0xa2, 0x5b, 0x4b, 0xde, - 0x38, 0x4c, 0x25, 0x3d, 0xb0, 0x65, 0xc3, 0xad, 0xa7, 0x00, 0xbc, 0x55, 0x48, 0x7b, 0x54, 0x5f, - 0xc5, 0xe9, 0x1c, 0x52, 0x25, 0xb2, 0x1d, 0xd2, 0x76, 0xa9, 0x74, 0xba, 0x94, 0xd4, 0xd6, 0xc1, - 0x20, 0xcf, 0x4c, 0xc2, 0x1d, 0xcf, 0x61, 0x45, 0xf7, 0x4f, 0x32, 0xd6, 0xd6, 0xc7, 0xf0, 0xe1, - 0x4b, 0x66, 0x07, 0xc0, 0xd0, 0xb3 - }; + string eamuse_info = "1-5ca3982d-98f8"; + byte[] data = HexToBytes("c119c325f093ef6c5f46eaafcd159e0f93648a37b415820de167b9b7f49dfc38736cf807ae57bc56e01f0a8e4ed9de1df774d854f9a1a751d8ef04cbdd4c951d2c1ac578f1ae7fe55e73ecba6f0f7aca4505adaf15a0586e3f28550e59390aea052cb25c1bceafca22f253f6b8ee77f9f69c42e0e7c8eb4f736376372e73c04d5c337f79c4b39804f19024eb850d94cbec79da6158bcc3a23bc67124bcd2bc1ac32c9162c550eb9ec0b1299bcb081325b18a91d691cdd9e190a2ae4d70764ecf9579901d7063efe9b657d83e0703de228c186b0fa85d973e98d9d4c2dfe6827a47ee9a8bc145454c0ecf673d82a2a84d0c2a1d061232ed979e143b1ecf1a1c55e43225a20cf4b212cdb07f138e76fb8e023ebb0be62038483c2a4c7b7319b03d23bf673f3feb0d987e54c3e50f5532e96bfca5434027489750954ad8a6943fa12ec4b87df6608d455411bbdce01b06606843b3c48e6f2cd8857c7f4530db2028691fe4ce7b496cbd1fbf5eb65953899b143217bc927c126d0ba53585ab00e2c561df9fbae915b7dfdf66eb8e18254ae38acd2d68889494a6ffe2e00c1aa2c4284a410c217297c263ba2af8c8e8f09bf3931afe0c012c6da39f3e2bf223fda7b53907393b38b7ae4d28422cad61a9a73514aeeb2dddf00a18ff9b0733cb7820f0d2b29467494d539a0696cfeee359bc9625c75aac059d7ca919c15674a234fe971852a163faad3169364c52992caf47c2db673b604b0727480c436fee525b2c7a782a03b8c7f2004c280fc186fb6a5d8ef4b48d417cf79f04c4878b0c254eb86b7954ec69c76ed1979f6f055620e85b60a97494e0ceb6e382054e7827cd3e05488e46e32341bbe652c855394a6ca109b856bab16417405057b32b0cb4361abb6f95b981ded651e31736bed382313438a26ad370029ca9188e447645ddecbf550387896959cb2d701720e5dfedac5750568da387c90afed4ce71e25a37af674ce86f83df1972a65eaf68f3c7ed8b1a522fb4ab61ad3db44250d000b79137bf703624b9e92d8fb67ad04dffdac2f4a016418caa5b7201820c0697437ad52249a4d8cfb604c0f50c217f30d03ece570e8e1c74be7ed3ea48a0018dd98bd48b388be9959995b185072db1f5a27d1b76362c00335bc44d8285baf90b1db9cb59a3c3814857837766974fd1d3c936da97f084d74c7efe287d53a1c168bc1dc14659b872e44c3864156f476b41251f927335eae425fbc5412ef5454fbe9c83ed701bb436b6e255db6f193894afcf8477c0053de5a1f0653f452ca53fda06cfaa73cf269e65be05eb1c2f68f152a446fae871ee185b73846077325454c575990a1640c1f96e005ae9698faa8c7f93091892c9a30260f05350e391e738bd65c9a76636de9fc0871a1433d2c574b2375bf46f7b5df94be57fd38223eb088b2b2537da0c50bec23c865227e67f16d1064cd58b36e03b13c3bba532458d81011ffa48e3ec3b22746fc6618c3b9dac69529c2f6070be0ead0c0a3a57a7c51b04c5e975141989f6a9ed45212d662a7be6e061927c70a4d9a09c040492c40ca32630bde78e3ea6e23ad848b6b474c2f014ba92630bcd31ab360eed244128db872011132160113e75f343af106c3f250933815e54dfb5c682f45209b6ad008dff0c4ed14e2ef4df8548b0d51b9b85721e3e8b6da7cff7c4b76c81a2d9f8485ad64ab4d5e5595646c16805b701c70240d7df7caa2bc0d932bb14b936445bb11fcc0a2d09fdb61b2d0fdf9261ae1f179c8f44621f372ba239ccfc3319249a8741ddf4337a2a040da64f0b61a0c6ab1fac5156f9c9e9c7b7d487933e8fbda0fc02d213e88aa92d357d9878f35fed46e7d6837ef789e1c32f839a684f8954560b13de7d98d4de2efd2034020799da8964b9602872192620e84f0df4dede10c2ea67fe1a4899868f8fce3a585f4936a72557c82db3943874a8f117c9c1f62fa5fdd1f8aa177828b630cca3f2f7f86451c19c99a49dc3cd60f770e650cf60379443d72d44889df15b223b94b44128c9e3155b7fd0eb1442e67396cf39bc35b89c3717659f05eb2c87f970cdac0ce19eaeb9e96ecf19a74cda7a2130a225c7b369d33f77b71d179467c70afda013f6e504f4b27af5f739b3aabd34edb79c9de365bbf9cc28fad4352d35c6e3c108935ffcee1b4a6670878dc9abe82716fc06834f21b3f2f9d9368622229b9152e0b36d60e41b72cc6b9a05ba508d9c69c90e884b796eb9d007fc8a781a28dd5b5c3285772d1642fe29126d468b32a9a9bbdd1926033032366ea3b650ca17766863d7642316b5b00297f82466e23574d391daee340183400452070e79c66ebdf88075d33c103a3a6614056159fdceff6b69467ab2af49425e62df863af55a095690eca2b9bd860e0b249fd954e878ff40facf35131e5a6dec2eaae8460d63d02b8a48466bfd2897a47545ce5ba24f40b93e17cd2f5b90d139fb329dcf07e9631a9123285c8099dade62d2a7d1bcbd362994b813c0d6816e2f9ea3c1359e23b5bceefe428336c2f0cddd2907c97c308f4660b9250b1d67da0c2a26e9ac2dc9b5e5439fda31fbf3ccb95ff773a6c041e0e8608accb4a612d17a06643b17e2af07de0e31a23f7e7421640f1e6f7627417e1d0aad7758ebe37bd979b43bd672c40487ed58db816b96a36edf9a323d76ab67e97346457d26977eb3443ec8c39b2faada8df3b97904636c3ea38ade93732ed4777d9889645baa016e6ff2b712f4a07a3724d85dc89b877325302651ea78f0aaa18da3e5579b682aca01764170034c2c3e08c61222c725ece83a712d98223d16a45a19507258a59ac64cf474750a5328e91e9364deb105ddbd193f8dde6f70d60349a4b30c0c9879f2476e6e1a542a3f276f2c9f4d922e303e0a38483249cd7480f2905dd77116aee6d426d34386437a23c02de911c356f155f1091bda37c15fd0e7d9539dbd928cb3c7e4e1403a4aefcd9bf0dffbe993d3f9ff369fdc0e90b89f6082e96e504d469b9329cca07c9d13b2ec8d09d743089af2263a0597f67a3d71e4fec1d8ab1fb23448a45863d9e122b8873a18abf9316617c82d418b13d6cf5ba2ff94f2dff602142e6ee241167d20e42d389fd50cab3e664a67b1448e62f2fac5f34633b85e79d0b98267f689b58ee78653394065f1d3bda945c90d4a66b84d61fe43f31f297c9e4ea177ee4be9139ca823bba69f2a7875ae55b0d8da798893c4bcd25f794da5ba99649283f8158618bb6cee794cf12d71a2a29e1171229bcd6ddd85baeba5d1b6fcfecbbe2ad3e19854c2afc73002ebc60c11833d9261b46044e53aef0daa50"); compress = compress.ToLower(); @@ -49,20 +29,8 @@ namespace eAmuseTest throw new ArgumentException("Unsupported compression algorithm"); KBinXML kbinxml = new KBinXML(rawData); - //KBinXML testDoc = new KBinXML(ExtractResource("eAmuseTest.testcases_out.kbin")); - - KBinXML reverseTest = new KBinXML(kbinxml.Document); Console.WriteLine(kbinxml); - - Console.WriteLine("Orig: " + BytesToString(rawData)); - Console.WriteLine("Len: " + rawData.Count()); - Console.WriteLine("Mine: " + BytesToString(reverseTest.Bytes)); - Console.WriteLine("Len: " + reverseTest.Bytes.Count()); - - KBinXML reReverseText = new KBinXML(reverseTest.Bytes); - - Console.WriteLine(reReverseText); } private static string BytesToString(IEnumerable bytes) @@ -99,5 +67,14 @@ namespace eAmuseTest return res; } } + + public static byte[] HexToBytes(string hex) + { + int len = hex.Length; + byte[] bytes = new byte[len / 2]; + for (int i = 0; i < len; i += 2) + bytes[i >> 1] = Convert.ToByte(hex.Substring(i, 2), 16); + return bytes; + } } }