From f9f66a9e42a0dc7640d45172789abaa9ffd93c1d Mon Sep 17 00:00:00 2001 From: BtbN Date: Wed, 17 Apr 2019 14:59:58 +0200 Subject: [PATCH] Refactoring --- eAmuseCore/KBinXML/KBinXML.cs | 158 +++++++++++++++++------------- eAmuseCore/KBinXML/TypeHelpers.cs | 47 +++++++-- eAmuseCore/KBinXML/XmlTypes.cs | 20 ++-- eAmuseTest/Program.cs | 4 +- 4 files changed, 147 insertions(+), 82 deletions(-) diff --git a/eAmuseCore/KBinXML/KBinXML.cs b/eAmuseCore/KBinXML/KBinXML.cs index fb108be..a16f85c 100644 --- a/eAmuseCore/KBinXML/KBinXML.cs +++ b/eAmuseCore/KBinXML/KBinXML.cs @@ -77,7 +77,6 @@ namespace eAmuseCore.KBinXML throw new ArgumentException("Invalid signature", "input"); input = input.Skip(1); - bool compressed; switch (input.FirstU8()) { case SIG_COMPRESSED: @@ -100,20 +99,98 @@ namespace eAmuseCore.KBinXML encoding = GetEncoding(encodingSig); - uint nodeEnd = input.FirstU32(); + int nodeSize = input.FirstS32(); input = input.Skip(4); - IEnumerable nodeBuf = input.Take((int)nodeEnd); - IEnumerable dataBuf = input.Skip((int)nodeEnd); + nodeBuf = input.Take(nodeSize); + dataBuf = input.Skip(nodeSize); + dataByteBuf = dataWordBuf = Enumerable.Empty(); - ParseNodes(nodeBuf, dataBuf, compressed); + ParseNodes(); } - private void ParseNodes(IEnumerable nodeBuf, IEnumerable dataBuf, bool compressed) - { - IEnumerable dataByteBuf = Enumerable.Empty(); - IEnumerable dataWordBuf = Enumerable.Empty(); + private IEnumerable nodeBuf = Enumerable.Empty(), dataBuf = Enumerable.Empty(); + private IEnumerable dataByteBuf = Enumerable.Empty(), dataWordBuf = Enumerable.Empty(); + bool compressed = false; + IEnumerable TakeDataAligned(int size, bool isArray) + { + if (size <= 0) + { + return Enumerable.Empty(); + } + else if (size == 1 && !isArray) + { + if (!dataByteBuf.Any()) + { + dataByteBuf = dataBuf; + dataBuf = dataBuf.Skip(4); + } + var res = dataByteBuf.Take(1); + dataByteBuf = dataByteBuf.Skip(1); + return res; + } + else if (size == 2 && !isArray) + { + if (!dataWordBuf.Any()) + { + dataWordBuf = dataBuf; + dataBuf = dataBuf.Skip(4); + } + var res = dataWordBuf.Take(2); + dataWordBuf = dataWordBuf.Skip(2); + return res; + } + else + { + return EnumHelpers.TakeBytesAligned(ref dataBuf, size); + } + } + + void SetNodeValue(XElement node, IEnumerable data, KValueAttribute nodeAttrs, int varCount, int arrCount) + { + if (nodeAttrs.NodeType == XmlTypes.XmlTypes.BinType) + { + node.SetAttributeValue("__size", varCount); + node.SetValue(XmlTypes.Bin.FromBytes(data)); + } + else if (nodeAttrs.NodeType == XmlTypes.XmlTypes.StrType) + { + node.SetValue(XmlTypes.Str.FromBytes(data, encoding)); + } + else + { + node.SetValue(XmlTypes.XmlTypes.MakeNodeFromBytes(nodeAttrs, arrCount, data)); + } + } + + private string GetNodeName(byte nodeType) + { + if (nodeType != XmlTypes.XmlTypes.NodeEndType && nodeType != XmlTypes.XmlTypes.SectionEndType) + { + if (compressed) + { + return SixBit.Unpack(ref nodeBuf); + } + else + { + int length = (nodeBuf.First() & ~64) + 1; + nodeBuf = nodeBuf.Skip(1); + + byte[] nameBytes = nodeBuf.Take(length).ToArray(); + nodeBuf = nodeBuf.Skip(length); + + return encoding.GetString(nameBytes); + } + } + else + { + return ""; + } + } + + private void ParseNodes() + { uint dataSize = dataBuf.FirstU32(); dataBuf = dataBuf.Skip(4); @@ -131,24 +208,7 @@ namespace eAmuseCore.KBinXML bool isArray = (nodeType & 64) != 0; nodeType = (byte)(nodeType & ~64); - string name = ""; - if (nodeType != XmlTypes.XmlTypes.NodeEndType && nodeType != XmlTypes.XmlTypes.SectionEndType) - { - if (compressed) - { - name = SixBit.Unpack(ref nodeBuf); - } - else - { - int length = (nodeBuf.First() & ~64) + 1; - nodeBuf = nodeBuf.Skip(1); - - byte[] nameBytes = nodeBuf.Take(length).ToArray(); - nodeBuf = nodeBuf.Skip(length); - - name = encoding.GetString(nameBytes); - } - } + string name = GetNodeName(nodeType); KValueAttribute nodeAttrs = null; bool startNode = false; @@ -202,49 +262,9 @@ namespace eAmuseCore.KBinXML int totCount = arrCount * varCount; int totSize = totCount * nodeAttrs.Size; - IEnumerable data = null; - if (isArray || totSize > 2) - { - data = EnumHelpers.TakeBytesAligned(ref dataBuf, totCount * nodeAttrs.Size); - } - else if (totSize == 1) - { - if (!dataByteBuf.Any()) - { - dataByteBuf = dataBuf; - dataBuf = dataBuf.Skip(4); - } - data = dataByteBuf.Take(1); - dataByteBuf = dataByteBuf.Skip(1); - } - else if (totSize == 2) - { - if (!dataWordBuf.Any()) - { - dataWordBuf = dataBuf; - dataBuf = dataBuf.Skip(4); - } - data = dataWordBuf.Take(2); - dataWordBuf = dataWordBuf.Skip(2); - } - else if (totSize == 0) - { - continue; - } + IEnumerable data = TakeDataAligned(totSize, isArray); - if (nodeType == XmlTypes.XmlTypes.BinType) - { - node.SetAttributeValue("__size", totCount); - node.SetValue(XmlTypes.Bin.FromBytes(data)); - } - else if (nodeType == XmlTypes.XmlTypes.StrType) - { - node.SetValue(XmlTypes.Str.FromBytes(data, encoding)); - } - else - { - node.SetValue(XmlTypes.XmlTypes.MakeNodeFromBytes(nodeType, arrCount, data)); - } + SetNodeValue(node, data, nodeAttrs, varCount, arrCount); } doc = new XDocument(fakeroot.FirstNode); diff --git a/eAmuseCore/KBinXML/TypeHelpers.cs b/eAmuseCore/KBinXML/TypeHelpers.cs index d4906d5..01f944e 100644 --- a/eAmuseCore/KBinXML/TypeHelpers.cs +++ b/eAmuseCore/KBinXML/TypeHelpers.cs @@ -4,8 +4,22 @@ using System.Linq; namespace eAmuseCore.KBinXML { - public class KValueList : List + public interface IKValue { + string ToString(); + IEnumerable ToBytes(); + } + + public class KValueList : List, IKValue where T : IKValue + { + public IEnumerable ToBytes() + { + IEnumerable res = Enumerable.Empty(); + foreach (IKValue val in this) + res = res.Concat(val.ToBytes()); + return res; + } + public override string ToString() { return string.Join(" ", this.Select(v => v.ToString())); @@ -32,9 +46,8 @@ namespace eAmuseCore.KBinXML public int Size { get; set; } - private static Dictionary nameLookupMap = new Dictionary(); - private static Dictionary typeLookupMap = new Dictionary(); - private static Dictionary classLookupMap = new Dictionary(); + private static readonly Dictionary nameLookupMap = new Dictionary(); + private static readonly Dictionary typeLookupMap = new Dictionary(); public static void Register(KValueAttribute attr) { @@ -59,7 +72,7 @@ namespace eAmuseCore.KBinXML } } - public class KValue + public class KValue : IKValue { public T Value { get; set; } @@ -71,7 +84,29 @@ namespace eAmuseCore.KBinXML return (Value != null) ? Value.ToString() : ""; } - protected KValueAttribute KValAttr => GetType().GetCustomAttributes(typeof(KValueAttribute), true).First() as KValueAttribute; + public virtual IEnumerable ToBytes() + { + if (Value == null) + return Enumerable.Empty(); + + IEnumerable res = BitConverter.GetBytes(Convert.ToUInt64(Value)); + + if (BitConverter.IsLittleEndian) + res = res.Reverse(); + + return res.Skip(8 - Size); + } + + 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; diff --git a/eAmuseCore/KBinXML/XmlTypes.cs b/eAmuseCore/KBinXML/XmlTypes.cs index 2dc2de7..9730ea3 100644 --- a/eAmuseCore/KBinXML/XmlTypes.cs +++ b/eAmuseCore/KBinXML/XmlTypes.cs @@ -8,13 +8,16 @@ using System.Collections; namespace eAmuseCore.KBinXML.XmlTypes { +#pragma warning disable IDE0060 // Remove unused parameter [KValue(1, "void", Count = 1, Size = 0)] public class Void : KValue { public Void(object _) => Value = null; - static public Void FromString(object _) => new Void(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 @@ -90,6 +93,8 @@ namespace eAmuseCore.KBinXML.XmlTypes return string.Join("", Value.Select(b => Convert.ToString(b, 16).PadLeft(2, '0'))); } + public override IEnumerable ToBytes() => Value; + static public Bin FromString(string input) { throw new NotImplementedException(); @@ -105,6 +110,8 @@ namespace eAmuseCore.KBinXML.XmlTypes public override string ToString() => Value; + public override IEnumerable ToBytes() => Encoding.UTF8.GetBytes(Value); + static public Str FromString(string input) => new Str(input); static public Str FromBytes(IEnumerable input, Encoding encoding) @@ -121,6 +128,8 @@ namespace eAmuseCore.KBinXML.XmlTypes 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())); @@ -135,7 +144,7 @@ namespace eAmuseCore.KBinXML.XmlTypes public const int NodeEndType = 190; public const int SectionEndType = 191; - private static Dictionary lookupMap = new Dictionary(); + private static readonly Dictionary lookupMap = new Dictionary(); public static void RegisterAll() { @@ -171,9 +180,9 @@ namespace eAmuseCore.KBinXML.XmlTypes return lookupMap[type]; } - public static object MakeNodeFromBytes(byte type, int count, IEnumerable data) + public static object MakeNodeFromBytes(KValueAttribute attrs, int count, IEnumerable data) { - Type valType = GetByType(type); + Type valType = GetByType(attrs.NodeType); MethodInfo fromBytes = valType.GetMethod("FromBytes", BindingFlags.Static | BindingFlags.Public); if (count <= 1) @@ -184,8 +193,7 @@ namespace eAmuseCore.KBinXML.XmlTypes { Type listType = typeof(KValueList<>).MakeGenericType(valType); IList list = (IList)Activator.CreateInstance(listType); - var attr = KValueAttribute.GetAttrByType(type); - int size = attr.Size * attr.Count; + int size = attrs.Size * attrs.Count; for (int i = 0; i < count; i++) { diff --git a/eAmuseTest/Program.cs b/eAmuseTest/Program.cs index 2fab9e0..72e0d96 100644 --- a/eAmuseTest/Program.cs +++ b/eAmuseTest/Program.cs @@ -46,7 +46,9 @@ namespace eAmuseTest else if (compress != "none") throw new ArgumentException("Unsupported compression algorithm"); - //KBinXML kbinxml = new KBinXML(rawData); + Console.WriteLine(BytesToString(BitConverter.GetBytes((byte)1))); + + KBinXML kbinxml = new KBinXML(rawData); KBinXML testDoc = new KBinXML(ExtractResource("eAmuseTest.testcases_out.kbin")); }