using CXML; using General; using Ionic.Zlib; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Xml; namespace CXMLDecompiler { class CXMLBuilder { String MainDir = ""; String XMLFilename = ""; String MagicNumber = ""; Int32 Version; FileStream InfoFile; MemoryStream TreeTable; MemoryStream StringIDTable; MemoryStream IntIDTable; MemoryStream StringTable; MemoryStream CharTable; MemoryStream HashIDTable; MemoryStream IntArrayTable; MemoryStream FloatArrayTable; MemoryStream FileTable; Boolean IsInitalized = false; public Boolean HashStrings = false; BinaryWriter bInfoFile; BinaryWriter bTreeTable; BinaryWriter bIntIDTable; BinaryWriter bFloatArrayTable; BinaryWriter bIntArrayTable; BinaryWriter bHashIDTable; BinaryWriter bStringIDTable; String SilicaTypingInformation = ""; public void Init(string XMLFile, string CxmlFile) { GetSilicaTypingInformation(XMLFile); MagicNumber = GetTypingInformation("MAGIC"); Version = Int32.Parse(GetTypingInformation("VERSION"), CultureInfo.InvariantCulture); InfoFile = File.Open(CxmlFile, FileMode.OpenOrCreate, FileAccess.ReadWrite); InfoFile.SetLength(0); TreeTable = new MemoryStream(); StringIDTable = new MemoryStream(); IntIDTable = new MemoryStream(); StringTable = new MemoryStream(); CharTable = new MemoryStream(); HashIDTable = new MemoryStream(); IntArrayTable = new MemoryStream(); FloatArrayTable = new MemoryStream(); FileTable = new MemoryStream(); bInfoFile = new BinaryWriter(InfoFile); bTreeTable = new BinaryWriter(TreeTable); bIntIDTable = new BinaryWriter(IntIDTable); bFloatArrayTable = new BinaryWriter(FloatArrayTable); bIntArrayTable = new BinaryWriter(IntArrayTable); bHashIDTable = new BinaryWriter(HashIDTable); bStringIDTable = new BinaryWriter(StringIDTable); XMLFilename = XMLFile; MainDir = Path.GetDirectoryName(XMLFilename); IsInitalized = true; return; } public void GetSilicaTypingInformation(string XmlFileName) { if (XmlFileName.Contains(""; string xmlData = File.ReadAllText(XmlFileName); int ind1 = xmlData.IndexOf(SearchFor); xmlData = xmlData.Substring(ind1 + SearchFor.Length); int ind2 = xmlData.IndexOf(SearchFor2); xmlData = xmlData.Substring(0, ind2); SilicaTypingInformation = xmlData; } public string GetTypingInformation(string Key) { string[] typeinf = SilicaTypingInformation.Split(','); foreach(string type in typeinf) { string[] keyValuePair = type.Split('='); if (keyValuePair[0] == Key) return keyValuePair[1]; } return "0"; } public class StringTableEntry { public int offset; public string name; } public class IntTableEntry { public int offset; public int value; } public class StringIDTableEntry { public int offset; public string value; public int loopbackOffset; } public class IntIDTableEntry { public int offset; public int value; public int loopbackOffset; } public class TreeTableEntry { public int offset; public XmlNode node; } List TreeTableEntries = new List(); List StringTableEntries = new List(); List CharTableEntries = new List(); List HashIdTableEntries = new List(); List IntArrayTableEntries = new List(); List FloatArrayTableEntries = new List(); List FileTableEntries = new List(); List StringIDTableEntries = new List(); List IntIDTableEntries = new List(); public int AddGetTreeTable(XmlNode node) { if (!(node.NodeType == XmlNodeType.Element || node.NodeType == XmlNodeType.EndElement || node.NodeType == XmlNodeType.Attribute)) return -1; foreach (TreeTableEntry entry in TreeTableEntries) if (entry.node == node) return entry.offset; TreeTableEntry ent = new TreeTableEntry(); ent.node = node; ent.offset = Convert.ToInt32(TreeTable.Position); TreeTableEntries.Add(ent); WriteElement(node); return ent.offset; } public int AddGetIntIdTable(int value, int loopbackPtr) { foreach (IntIDTableEntry entry in IntIDTableEntries) { if (entry.loopbackOffset == loopbackPtr && loopbackPtr != -1) { return entry.offset; } else if (entry.value == value) { if (loopbackPtr == -1) return entry.offset; entry.loopbackOffset = loopbackPtr; long position1 = IntIDTable.Position; IntIDTable.Seek(entry.offset, SeekOrigin.Begin); bIntIDTable.Write(loopbackPtr); IntIDTable.Seek(position1, SeekOrigin.Begin); return entry.offset; } } IntIDTableEntry ent = new IntIDTableEntry(); ent.value = value; ent.loopbackOffset = loopbackPtr; ent.offset = Convert.ToInt32(IntIDTable.Position); IntIDTableEntries.Add(ent); bIntIDTable.Write((int)loopbackPtr); bIntIDTable.Write((int)value); return ent.offset; } public int AddGetStringIdTable(string value, int loopbackPtr) { foreach (StringIDTableEntry entry in StringIDTableEntries) { if (entry.loopbackOffset == loopbackPtr && loopbackPtr != -1) { return entry.offset; } else if (entry.value == value) { if (loopbackPtr == -1) return entry.offset; entry.loopbackOffset = loopbackPtr; long position1 = StringIDTable.Position; StringIDTable.Seek(entry.offset, SeekOrigin.Begin); bStringIDTable.Write(loopbackPtr); StringIDTable.Seek(position1, SeekOrigin.Begin); return entry.offset; } } StringIDTableEntry ent = new StringIDTableEntry(); ent.value = value; ent.loopbackOffset = loopbackPtr; ent.offset = Convert.ToInt32(StringIDTable.Position); StringIDTableEntries.Add(ent); bStringIDTable.Write(loopbackPtr); Tools.WriteStringToStream(StringIDTable, value); StringIDTable.WriteByte(0x00); if (loopbackPtr != -1) { Align(StringIDTable, 4); } return ent.offset; } public int AddGetFileTable(byte[] value, string filename) { foreach (StringTableEntry entry in FileTableEntries) if (entry.name == filename) return entry.offset; StringTableEntry ent = new StringTableEntry(); ent.name = filename; ent.offset = Convert.ToInt32(FileTable.Position); FileTableEntries.Add(ent); FileTable.Write(value, 0x00, value.Length); Align(FileTable, 0x10); return ent.offset; } public int AddGetFloatArrayTable(float[] value) { float[] Test = new float[value.Length]; float[] TestAgainst = FloatArrayTableEntries.ToArray(); for (int i = 0; i < TestAgainst.Length; i += 1) { if (i + value.Length > TestAgainst.Length) break; Array.ConstrainedCopy(TestAgainst, i, Test, 0, value.Length); if(value.SequenceEqual(Test)) return i; } int offset = Convert.ToInt32(FloatArrayTable.Position) / 4; foreach (float i in value) { bFloatArrayTable.Write((float)i); FloatArrayTableEntries.Add(i); } return offset; } public int AddGetIntArrayTable(int[] value) { int[] Test = new int[value.Length]; int[] TestAgainst = IntArrayTableEntries.ToArray(); for (int i = 0; i < TestAgainst.Length; i += 1) { if (i + value.Length > TestAgainst.Length) break; Array.ConstrainedCopy(TestAgainst, i, Test, 0, value.Length); if (value.SequenceEqual(Test)) return i; } int offset = Convert.ToInt32(IntArrayTable.Position) / 4; foreach (int i in value) { bIntArrayTable.Write((int)i); IntArrayTableEntries.Add(i); } return offset; } public int AddGetHashIdTable(int value) { foreach (IntTableEntry entry in HashIdTableEntries) if (entry.value == value) return entry.offset/4; IntTableEntry ent = new IntTableEntry(); ent.value = value; ent.offset = Convert.ToInt32(HashIDTable.Position); HashIdTableEntries.Add(ent); bHashIDTable.Write((int)value); return ent.offset/4; } public int AddGetStringTable(string elementName) { foreach(StringTableEntry entry in StringTableEntries) if (entry.name == elementName) return entry.offset; StringTableEntry ent = new StringTableEntry(); ent.name = elementName; ent.offset = Convert.ToInt32(StringTable.Position); StringTableEntries.Add(ent); Tools.WriteStringToStream(StringTable, elementName); StringTable.WriteByte(0x00); return ent.offset; } public int AddGetCharTable(string elementName) { foreach (StringTableEntry entry in CharTableEntries) if (entry.name == elementName) return entry.offset; StringTableEntry ent = new StringTableEntry(); ent.name = elementName; ent.offset = Convert.ToInt32(CharTable.Position)/2; CharTableEntries.Add(ent); Tools.WriteUtf16StringToStream(CharTable, elementName); CharTable.WriteByte(0x00); CharTable.WriteByte(0x00); return ent.offset; } public AttributeType DetermineType(string ElementName, string AttributeName) { return (AttributeType)Int32.Parse(GetTypingInformation(ElementName + ":" + AttributeName), CultureInfo.InvariantCulture); } public void WriteAttribute(XmlAttribute attribute, MemoryStream WorkRam, BinaryWriter bWorkRam, string ElementName) { int AttributePtr = AddGetStringTable(attribute.Name); AttributeType Type = DetermineType(ElementName, attribute.Name); bWorkRam.Write(AttributePtr); bWorkRam.Write((int)Type); Console.WriteLine("AttributeType: " + Type.ToString()); switch (Type) { case AttributeType.TYPE_NONE: Console.WriteLine("UNSUPPORTED TYPE @ " + TreeTable.Position); Console.ReadKey(); break; case AttributeType.TYPE_INT: int intWrite = Int32.Parse(attribute.Value, CultureInfo.InvariantCulture); bWorkRam.Write((int)intWrite); bWorkRam.Write((int)0x00); Console.WriteLine("Int - Value: " + intWrite.ToString()); break; case AttributeType.TYPE_FLOAT: float FloatValue = Single.Parse(attribute.Value.Substring(0, attribute.Value.Length - 1), CultureInfo.InvariantCulture); bWorkRam.Write((float)FloatValue); bWorkRam.Write((int)0x00); Console.WriteLine("Float - Value: " + FloatValue.ToString()); break; case AttributeType.TYPE_STRING: int StringOffset = AddGetStringTable(attribute.Value); int StringLen = Encoding.UTF8.GetBytes(attribute.Value).Length; bWorkRam.Write(StringOffset); bWorkRam.Write(StringLen); Console.WriteLine("String: " + attribute.Value); break; case AttributeType.TYPE_CHAR: int CharOffset = AddGetCharTable(attribute.Value); int CharLen = Encoding.Unicode.GetBytes(attribute.Value).Length; bWorkRam.Write(CharOffset); bWorkRam.Write(CharLen); Console.WriteLine("Char: " + attribute.Value); break; case AttributeType.TYPE_HASH_ID: int hashId = Int32.Parse(attribute.Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture); if (HashStrings) hashId = Int32.Parse(Tools.GenerateShortHash(Encoding.UTF8.GetBytes(attribute.Value)), NumberStyles.HexNumber, CultureInfo.InvariantCulture); int HashTableOffset = AddGetHashIdTable(hashId); int HashTableSize = 4; bWorkRam.Write(HashTableOffset); bWorkRam.Write(HashTableSize); Console.WriteLine("Hash ID Offset:" + HashTableOffset.ToString() + " size: " + HashTableSize); break; case AttributeType.TYPE_INTEGER_ARRAY: string[] arr = attribute.Value.Replace("[", "").Replace("]", "").Replace(" ", "").Split(','); int[] intArr = new int[arr.Length]; for (int i = 0; i < intArr.Length; i++) intArr[i] = Int32.Parse(arr[i], CultureInfo.InvariantCulture); int IntArrayOffset = AddGetIntArrayTable(intArr); int IntArraySize = intArr.Length; bWorkRam.Write(IntArrayOffset); bWorkRam.Write(IntArraySize); Console.WriteLine("Int Array: " + attribute.Value); break; case AttributeType.TYPE_FLOAT_ARRAY: arr = attribute.Value.Replace("[", "").Replace("]", "").Replace(" ", "").Replace("f", "").Split(','); float[] floatArr = new float[arr.Length]; for (int i = 0; i < floatArr.Length; i++) floatArr[i] = Single.Parse(arr[i], CultureInfo.InvariantCulture); int FloatArrayOffset = AddGetFloatArrayTable(floatArr); int FloatArraySize = floatArr.Length; bWorkRam.Write(FloatArrayOffset); bWorkRam.Write(FloatArraySize); Console.WriteLine("Float Array: " + attribute.Value); break; case AttributeType.TYPE_FILE: string fPath = Path.Combine(MainDir, attribute.Value); byte[] data = File.ReadAllBytes(fPath); int FilePtr = AddGetFileTable(data, Path.Combine(Environment.CurrentDirectory, fPath)); int FileSz = data.Length; bWorkRam.Write(FilePtr); bWorkRam.Write(FileSz); Console.WriteLine("Reading File: " + fPath); break; case AttributeType.TYPE_ID_STRING_LOOPBACK: int StringIdTableOffset = AddGetStringIdTable(attribute.Value, Convert.ToInt32(TreeTable.Position)); bWorkRam.Write(StringIdTableOffset); bWorkRam.Write((int)0x00); Console.WriteLine("String Loopback: " + ElementName + " " + TreeTable.Position.ToString("X") + " (" + TreeTable.Position.ToString("X") + ")"); Console.WriteLine("Loopback ID String: " + StringIdTableOffset + " sz: 0"); break; case AttributeType.TYPE_ID_STRING: StringIdTableOffset = AddGetStringIdTable(attribute.Value, -1); bWorkRam.Write(StringIdTableOffset); bWorkRam.Write((int)0x00); Console.WriteLine("ID String: " + StringIdTableOffset + " sz: 0"); break; case AttributeType.TYPE_ID_INT_LOOPBACK: int hash = Int32.Parse(attribute.Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture); if (HashStrings) hash = Int32.Parse(Tools.GenerateShortHash(Encoding.UTF8.GetBytes(attribute.Value)), NumberStyles.HexNumber, CultureInfo.InvariantCulture); int IntIdTableOffset = AddGetIntIdTable(hash, Convert.ToInt32(TreeTable.Position)); bWorkRam.Write(IntIdTableOffset); bWorkRam.Write((int)0x00); Console.WriteLine("Int Loopback: " + ElementName + " " + TreeTable.Position.ToString("X") + " (" + TreeTable.Position.ToString("X") + ")"); Console.WriteLine("Loopback ID Int: " + IntIdTableOffset + " sz: 0"); break; case AttributeType.TYPE_ID_INT: hash = Int32.Parse(attribute.Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture); if (HashStrings) hash = Int32.Parse(Tools.GenerateShortHash(Encoding.UTF8.GetBytes(attribute.Value)), NumberStyles.HexNumber, CultureInfo.InvariantCulture); IntIdTableOffset = AddGetIntIdTable(hash, -1); bWorkRam.Write(IntIdTableOffset); bWorkRam.Write((int)0x00); Console.WriteLine("Int Id: " + IntIdTableOffset + " sz: 0"); break; default: Console.WriteLine("UNKNOWN TYPE @ " + TreeTable.Position); Console.ReadKey(); break; } Console.WriteLine(attribute.Name + "=" + attribute.Value); } public void WriteElement(XmlNode node) { MemoryStream WorkRam = new MemoryStream(); BinaryWriter bWorkRam = new BinaryWriter(WorkRam); int ElementPtr = AddGetStringTable(node.Name); int NumAttributes = 0; int ParentPtr = -1; int PrevSibling = -1; int NextSibling = -1; int FirstChild = -1; int LastChild = -1; long Position = TreeTable.Position; bWorkRam.Write(ElementPtr); bWorkRam.Write(NumAttributes); bWorkRam.Write(ParentPtr); bWorkRam.Write(PrevSibling); bWorkRam.Write(NextSibling); bWorkRam.Write(FirstChild); bWorkRam.Write(LastChild); foreach (XmlAttribute attribute in node.Attributes) { WriteAttribute(attribute, WorkRam, bWorkRam, node.Name); } WorkRam.Seek(0x00, SeekOrigin.Begin); WorkRam.CopyTo(TreeTable); foreach (XmlNode childNode in node.ChildNodes) { if(childNode != null) AddGetTreeTable(childNode); } // Rewrite header now if (node.Attributes != null) NumAttributes = node.Attributes.Count; if (node.ParentNode != null) ParentPtr = AddGetTreeTable(node.ParentNode); if (node.PreviousSibling != null) PrevSibling = AddGetTreeTable(node.PreviousSibling); if (node.NextSibling != null) NextSibling = AddGetTreeTable(node.NextSibling); if (node.FirstChild != null) FirstChild = AddGetTreeTable(node.FirstChild); if (node.LastChild != null) LastChild = AddGetTreeTable(node.LastChild); long Position2 = TreeTable.Position; TreeTable.Seek(Position, SeekOrigin.Begin); bTreeTable.Write(ElementPtr); bTreeTable.Write(NumAttributes); bTreeTable.Write(ParentPtr); bTreeTable.Write(PrevSibling); bTreeTable.Write(NextSibling); bTreeTable.Write(FirstChild); bTreeTable.Write(LastChild); TreeTable.Seek(Position2, SeekOrigin.Begin); } public void Align(Stream s, int align) { int size = Convert.ToInt32(s.Length); if (size % align != 0) { int totalAlign = (align - (size % align)); for (int i = 0; i < totalAlign; i++) { s.WriteByte(0x00); } } } public void Term() { InfoFile.Close(); TreeTable.Close(); StringIDTable.Close(); IntIDTable.Close(); StringTable.Close(); CharTable.Close(); HashIDTable.Close(); IntArrayTable.Close(); FloatArrayTable.Close(); FileTable.Close(); bTreeTable.Close(); bIntIDTable.Close(); bFloatArrayTable.Close(); bIntArrayTable.Close(); bHashIDTable.Close(); TreeTableEntries.Clear(); StringTableEntries.Clear(); CharTableEntries.Clear(); HashIdTableEntries.Clear(); IntArrayTableEntries.Clear(); FloatArrayTableEntries.Clear(); FileTableEntries.Clear(); StringIDTableEntries.Clear(); IntIDTableEntries.Clear(); IsInitalized = false; } public void BuildCXML(string XmlFile, string CxmlFile) { if (!IsInitalized) Init(XmlFile, CxmlFile); Console.WriteLine("Magic Number: " + MagicNumber); Console.WriteLine("Version: " + Version.ToString("X")); XmlDocument XMLFile = new XmlDocument(); XMLFile.Load(XmlFile); Tools.WriteStringToStream(InfoFile, MagicNumber); bInfoFile.Write((Int32)Version); byte[] headerPlaceholder = new byte[0x48]; InfoFile.Write(headerPlaceholder, 0x00, headerPlaceholder.Length); foreach(XmlNode childNode in XMLFile.ChildNodes) { AddGetTreeTable(childNode); } int TreeTableOffset = Convert.ToInt32(InfoFile.Position); int TreeTableSize = Convert.ToInt32(TreeTable.Length); Align(TreeTable, 0x10); TreeTable.Seek(0x00, SeekOrigin.Begin); TreeTable.CopyTo(InfoFile); int StringIdOffset = Convert.ToInt32(InfoFile.Position); int StringIdSize = Convert.ToInt32(StringIDTable.Length); Align(StringIDTable, 0x10); StringIDTable.Seek(0x00, SeekOrigin.Begin); StringIDTable.CopyTo(InfoFile); int IntIdOffset = Convert.ToInt32(InfoFile.Position); int IntIdSize = Convert.ToInt32(IntIDTable.Length); Align(IntIDTable, 0x10); IntIDTable.Seek(0x00, SeekOrigin.Begin); IntIDTable.CopyTo(InfoFile); int StringTableOffset = Convert.ToInt32(InfoFile.Position); int StringTableSize = Convert.ToInt32(StringTable.Length); Align(StringTable, 0x10); StringTable.Seek(0x00, SeekOrigin.Begin); StringTable.CopyTo(InfoFile); int CharTableOffset = Convert.ToInt32(InfoFile.Position); int CharTableSize = Convert.ToInt32(CharTable.Length); Align(CharTable, 0x10); CharTable.Seek(0x00, SeekOrigin.Begin); CharTable.CopyTo(InfoFile); int HashIdTableOffset = Convert.ToInt32(InfoFile.Position); int HashIdTableSize = Convert.ToInt32(HashIDTable.Length); Align(HashIDTable, 0x10); HashIDTable.Seek(0x00, SeekOrigin.Begin); HashIDTable.CopyTo(InfoFile); int IntArrayTableOffset = Convert.ToInt32(InfoFile.Position); int IntArrayTableSize = Convert.ToInt32(IntArrayTable.Length); Align(IntArrayTable, 0x10); IntArrayTable.Seek(0x00, SeekOrigin.Begin); IntArrayTable.CopyTo(InfoFile); int FloatArrayTableOffset = Convert.ToInt32(InfoFile.Position); int FloatArrayTableSize = Convert.ToInt32(FloatArrayTable.Length); Align(FloatArrayTable, 0x10); FloatArrayTable.Seek(0x00, SeekOrigin.Begin); FloatArrayTable.CopyTo(InfoFile); int FileTableOffset = Convert.ToInt32(InfoFile.Position); int FileTableSize = Convert.ToInt32(FileTable.Length); Align(FileTable, 0x10); FileTable.Seek(0x00, SeekOrigin.Begin); FileTable.CopyTo(InfoFile); string excessData = GetTypingInformation("EXCESS"); if (excessData != "0") { Console.WriteLine("Excess Data Found, Writing to end."); byte[] ExcessData = Convert.FromBase64String(excessData.Replace("*", "=")); InfoFile.Read(ExcessData, 0x00, ExcessData.Length); byte[] UnCompressed = ZlibStream.UncompressBuffer(ExcessData); InfoFile.Write(UnCompressed, 0x00, UnCompressed.Length); } InfoFile.Seek(0x8, SeekOrigin.Begin); bInfoFile.Write(TreeTableOffset); bInfoFile.Write(TreeTableSize); bInfoFile.Write(StringIdOffset); bInfoFile.Write(StringIdSize); bInfoFile.Write(IntIdOffset); bInfoFile.Write(IntIdSize); bInfoFile.Write(StringTableOffset); bInfoFile.Write(StringTableSize); bInfoFile.Write(CharTableOffset); bInfoFile.Write(CharTableSize); bInfoFile.Write(HashIdTableOffset); bInfoFile.Write(HashIdTableSize); bInfoFile.Write(IntArrayTableOffset); bInfoFile.Write(IntArrayTableSize); bInfoFile.Write(FloatArrayTableOffset); bInfoFile.Write(FloatArrayTableSize); bInfoFile.Write(FileTableOffset); bInfoFile.Write(FileTableSize); Term(); } } }