diff --git a/eAmuseCore/KBinXML/Helpers.cs b/eAmuseCore/KBinXML/Helpers.cs new file mode 100644 index 0000000..214ec7e --- /dev/null +++ b/eAmuseCore/KBinXML/Helpers.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace eAmuseCore.KBinXML +{ + static class EnumHelpers + { + public static uint FirstU32(this IEnumerable input) + { + input = input.Take(4); + if (BitConverter.IsLittleEndian) + input = input.Reverse(); + return BitConverter.ToUInt32(input.ToArray(), 0); + } + + public static int FirstI32(this IEnumerable input) + { + input = input.Take(4); + if (BitConverter.IsLittleEndian) + input = input.Reverse(); + return BitConverter.ToInt32(input.ToArray(), 0); + } + + public static ushort FirstU16(this IEnumerable input) + { + input = input.Take(2); + if (BitConverter.IsLittleEndian) + input = input.Reverse(); + return BitConverter.ToUInt16(input.ToArray(), 0); + } + + public static short FirstI16(this IEnumerable input) + { + input = input.Take(2); + if (BitConverter.IsLittleEndian) + input = input.Reverse(); + return BitConverter.ToInt16(input.ToArray(), 0); + } + } +} diff --git a/eAmuseCore/KBinXML/KBinXML.cs b/eAmuseCore/KBinXML/KBinXML.cs new file mode 100644 index 0000000..70bda0a --- /dev/null +++ b/eAmuseCore/KBinXML/KBinXML.cs @@ -0,0 +1,155 @@ +using System; +using System.Text; +using System.Linq; +using System.Xml.Linq; +using System.Collections.Generic; + +namespace eAmuseCore.KBinXML +{ + public class KBinXML + { + const byte SIGNATURE = 0xA0; + const byte SIG_COMPRESSED = 0x42; + const byte SIG_UNCOMPRESSED = 0x45; + + static KBinXML() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + + private static Encoding GetEncoding(byte sig) + { + switch (sig) + { + case 0x00: + case 0x80: + return Encoding.GetEncoding(932); + case 0x20: + return Encoding.ASCII; + case 0x40: + return Encoding.GetEncoding("iso-8859-1"); + case 0x60: + return Encoding.GetEncoding("EUC-JP"); + case 0xA0: + return Encoding.UTF8; + } + + throw new ArgumentException("Unsupported encoding", "sig"); + } + + private static byte GetEncodingSig(Encoding enc) + { + if (Encoding.UTF8.Equals(enc)) + return 0xA0; + if (Encoding.GetEncoding(932).Equals(enc)) + return 0x80; + if (Encoding.ASCII.Equals(enc)) + return 0x20; + if (Encoding.GetEncoding("iso-8859-1").Equals(enc)) + return 0x40; + if (Encoding.GetEncoding("EUC-JP").Equals(enc)) + return 0x60; + + throw new ArgumentException("Unsupported encoding", "enc"); + } + + private XDocument doc; + private Encoding encoding; + + public KBinXML(IEnumerable input) + { + + Parse(input); + } + + public KBinXML(XDocument doc) + { + this.doc = doc; + } + + private void Parse(IEnumerable input) + { + doc = new XDocument(); + + if (input.First() != SIGNATURE) + throw new ArgumentException("Invalid signature", "input"); + input = input.Skip(1); + + bool compressed; + switch (input.First()) + { + case SIG_COMPRESSED: + compressed = true; + break; + case SIG_UNCOMPRESSED: + compressed = false; + break; + default: + throw new ArgumentException("Invalud compression info", "input"); + } + input = input.Skip(1); + + Console.WriteLine("Compressed: " + compressed); + + byte encodingSig = input.First(); + input = input.Skip(1); + + if (input.First() != (0xFF ^ encodingSig)) + throw new ArgumentException("Encoding signature failed to verify", "input"); + input = input.Skip(1); + + encoding = GetEncoding(encodingSig); + + uint nodeEnd = input.FirstU32(); + input = input.Skip(4); + + IEnumerable nodeBuf = input.Take((int)nodeEnd); + IEnumerable dataBuf = input.Skip((int)nodeEnd); + + ParseNodes(nodeBuf, dataBuf, compressed); + } + + private void ParseNodes(IEnumerable nodeBuf, IEnumerable dataBuf, bool compressed) + { + uint dataSize = dataBuf.FirstU32(); + dataBuf = dataBuf.Skip(4); + + XElement node = doc.Root; + + bool nodesLeft = true; + while (nodesLeft && nodeBuf.Any()) + { + nodeBuf = nodeBuf.SkipWhile(b => b == 0); + + byte nodeType = nodeBuf.First(); + nodeBuf = nodeBuf.Skip(1); + + bool isArray = (nodeType & 64) != 0; + nodeType = (byte)(nodeType & ~64); + + XmlTypes.Entry nodeTypeEntry = XmlTypes.GetEntryByType(nodeType); + + string name = null; + if (nodeType != XmlTypes.NodeEnd.type && nodeType != XmlTypes.EndSection.type) + { + 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); + } + } + + Console.WriteLine("Name: " + name); + } + } + } +} diff --git a/eAmuseCore/KBinXML/SixBit.cs b/eAmuseCore/KBinXML/SixBit.cs new file mode 100644 index 0000000..05556ac --- /dev/null +++ b/eAmuseCore/KBinXML/SixBit.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Collections; +using System.Text; +using System.Linq; +using System; + +namespace eAmuseCore.KBinXML +{ + static class SixBit + { + static readonly string charmap = "0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; + + public static string Unpack(ref IEnumerable nodeBuf) + { + int length = nodeBuf.First(); + nodeBuf = nodeBuf.Skip(1); + + int length_bits = length * 6; + int length_bytes = (length_bits + 7) / 8; + + BitArray barr = new BitArray(nodeBuf.Take(length_bytes).ToArray()); + nodeBuf = nodeBuf.Skip(length_bytes); + + StringBuilder res = new StringBuilder(); + for (int i = 0, pos = length_bits - 1; i < length; ++i) + { + int resI = 0; + for (int j = 0; j < 6; ++j, --pos) + if (barr.Get(pos)) + resI |= 1 << j; + + res.Append(charmap[resI]); + } + return res.ToString(); + } + } +} diff --git a/eAmuseCore/KBinXML/XmlTypes.cs b/eAmuseCore/KBinXML/XmlTypes.cs new file mode 100644 index 0000000..daafd5b --- /dev/null +++ b/eAmuseCore/KBinXML/XmlTypes.cs @@ -0,0 +1,80 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace eAmuseCore.KBinXML +{ + public static class XmlTypes + { + public class Entry + { + public byte type; + public int size; + public int count; + public string[] names; + } + + public static readonly Entry NodeStart = new Entry + { + type = 1 + }; + + public static readonly Entry Attr = new Entry + { + type = 46 + }; + + public static readonly Entry NodeEnd = new Entry + { + type = 190 + }; + + public static readonly Entry EndSection = new Entry + { + type = 191 + }; + + private static Dictionary nameLookupMap = new Dictionary(); + private static Dictionary typeLookupMap = new Dictionary(); + + public static Entry GetEntryByName(string name) + { + if (nameLookupMap.ContainsKey(name)) + return nameLookupMap[name]; + + foreach (FieldInfo info in typeof(XmlTypes).GetFields()) + { + if (!info.IsStatic) + continue; + Entry res = info.GetValue(null) as Entry; + if (res == null || !res.names.Contains(name)) + continue; + nameLookupMap[name] = res; + return res; + } + + return null; + } + + public static Entry GetEntryByType(byte type) + { + if (typeLookupMap.ContainsKey(type)) + return typeLookupMap[type]; + + foreach (FieldInfo info in typeof(XmlTypes).GetFields()) + { + if (!info.IsStatic) + continue; + Entry res = info.GetValue(null) as Entry; + if (res == null || res.type != type) + continue; + typeLookupMap[type] = res; + return res; + } + + return null; + } + } +} diff --git a/eAmuseCore/eAmuseCore.csproj b/eAmuseCore/eAmuseCore.csproj index 7f86d88..1099f21 100644 --- a/eAmuseCore/eAmuseCore.csproj +++ b/eAmuseCore/eAmuseCore.csproj @@ -6,6 +6,7 @@ + diff --git a/eAmuseTest/Program.cs b/eAmuseTest/Program.cs index 91d3228..ceee509 100644 --- a/eAmuseTest/Program.cs +++ b/eAmuseTest/Program.cs @@ -1,11 +1,12 @@ using System; using System.Text; using System.Linq; +using System.Collections; using System.Collections.Generic; using eAmuseCore.Crypto; using eAmuseCore.Compression; -using System.Collections; +using eAmuseCore.KBinXML; namespace eAmuseTest { @@ -36,13 +37,18 @@ namespace eAmuseTest 0x4b, 0x66, 0x07, 0xc0, 0xd0, 0xb3 }; - var decryptedData = RC4.ApplyEAmuseInfo(eamuse_info, data).ToArray(); + compress = compress.ToLower(); - Console.WriteLine(BytesToString(decryptedData)); + var decryptedData = RC4.ApplyEAmuseInfo(eamuse_info, data); + var rawData = decryptedData; + if (compress == "lz77") + rawData = LZ77.Decompress(decryptedData); + else if (compress != "none") + throw new ArgumentException("Unsupported compression algorithm"); - Console.WriteLine("Ok go"); + Console.WriteLine(BytesToString(rawData)); - var rawData = LZ77.Decompress(decryptedData).ToArray(); + KBinXML kbinxml = new KBinXML(rawData); } private static string BytesToString(IEnumerable bytes)