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 = ""; Endianness FileEndainness; bool ps3; Tools tools; Int32 Version; FileStream InfoFile; MemoryStream TreeTable; MemoryStream IDTable; MemoryStream HashIDTable; MemoryStream StringTable; MemoryStream WStringTable; MemoryStream HashTable; MemoryStream IntArrayTable; MemoryStream FloatArrayTable; MemoryStream FileTable; Boolean IsInitalized = false; String SilicaTypingInformation = ""; public void Init(string XMLFile, string CxmlFile) { GetSilicaTypingInformation(XMLFile); MagicNumber = GetTypingInformation("MAGIC"); Version = Int32.Parse(GetTypingInformation("VERSION").Replace("0x", ""), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture); // Determine filetype. FileEndainness = (Endianness)Enum.Parse(typeof(Endianness), GetTypingInformation("ENDAINESS")); ps3 = GetTypingInformation("PS3") == "true"; tools = new Tools((FileEndainness == Endianness.BIG_ENDIAN)); InfoFile = File.Open(CxmlFile, FileMode.OpenOrCreate, FileAccess.ReadWrite); InfoFile.SetLength(0); TreeTable = new MemoryStream(); IDTable = new MemoryStream(); StringTable = new MemoryStream(); IntArrayTable = new MemoryStream(); FloatArrayTable = new MemoryStream(); FileTable = new MemoryStream(); WStringTable = new MemoryStream(); HashIDTable = new MemoryStream(); HashTable = new MemoryStream(); 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 HashTableEntry { public int offset; public int value; } public class IDTableEntry { public int offset; public string value; public int loopbackOffset; } public class HashIDTableEntry { 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 WStringTableEntries = new List(); List HashTableEntries = new List(); List IntArrayTableEntries = new List(); List FloatArrayTableEntries = new List(); List FileTableEntries = new List(); List IDTableEntries = new List(); List HashIDTableEntries = 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 AddGetHashIdTable(int value, int loopbackPtr) { foreach (HashIDTableEntry entry in HashIDTableEntries) { 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 = HashIDTable.Position; HashIDTable.Seek(entry.offset, SeekOrigin.Begin); tools.WriteInt32(HashIDTable, loopbackPtr); HashIDTable.Seek(position1, SeekOrigin.Begin); return entry.offset; } } HashIDTableEntry ent = new HashIDTableEntry(); ent.value = value; ent.loopbackOffset = loopbackPtr; ent.offset = Convert.ToInt32(HashIDTable.Position); HashIDTableEntries.Add(ent); tools.WriteInt32(HashIDTable, loopbackPtr); tools.WriteInt32(HashIDTable, value); return ent.offset; } public int AddGetIdTable(string value, int loopbackPtr) { foreach (IDTableEntry entry in IDTableEntries) { 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 = IDTable.Position; IDTable.Seek(entry.offset, SeekOrigin.Begin); tools.WriteInt32(IDTable, loopbackPtr); IDTable.Seek(position1, SeekOrigin.Begin); return entry.offset; } } IDTableEntry ent = new IDTableEntry(); ent.value = value; ent.loopbackOffset = loopbackPtr; ent.offset = Convert.ToInt32(IDTable.Position); IDTableEntries.Add(ent); tools.WriteInt32(IDTable, loopbackPtr); Tools.WriteStringToStream(IDTable, value); IDTable.WriteByte(0x00); if (loopbackPtr != -1 && !ps3) { Align(IDTable, 4); } return ent.offset; } public int AddGetFileTable(byte[] value, string hash) { foreach (StringTableEntry entry in FileTableEntries) if (entry.name == hash) return entry.offset; StringTableEntry ent = new StringTableEntry(); ent.name = Tools.GenerateHash(value); 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) { tools.WriteSingle(FloatArrayTable, 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) { tools.WriteInt32(IntArrayTable, i); IntArrayTableEntries.Add(i); } return offset; } public int AddGetHashTable(int value) { foreach (HashTableEntry entry in HashTableEntries) if (entry.value == value) return entry.offset/4; HashTableEntry ent = new HashTableEntry(); ent.value = value; ent.offset = Convert.ToInt32(HashTable.Position); HashTableEntries.Add(ent); tools.WriteInt32(HashTable, 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 AddGetWstringTable(string elementName) { foreach (StringTableEntry entry in WStringTableEntries) if (entry.name == elementName) return entry.offset; StringTableEntry ent = new StringTableEntry(); ent.name = elementName; ent.offset = Convert.ToInt32(WStringTable.Position)/2; WStringTableEntries.Add(ent); Tools.WriteUtf16StringToStream(WStringTable, elementName); WStringTable.WriteByte(0x00); WStringTable.WriteByte(0x00); return ent.offset; } public int ConvertAttributeTypeToInt(AttributeType type) { if (!ps3) return (int) type; else return (int) (AttributeTypePs3)Enum.Parse(typeof(AttributeTypePs3), type.ToString()); } public AttributeType DetermineType(string ElementName, string AttributeName) { return (AttributeType)Enum.Parse(typeof(AttributeType), GetTypingInformation(ElementName + ":" + AttributeName)); } public void WriteAttribute(XmlAttribute attribute, MemoryStream WorkRam, string ElementName) { int AttributePtr = AddGetStringTable(attribute.Name); AttributeType Type = DetermineType(ElementName, attribute.Name); tools.WriteInt32(WorkRam, AttributePtr); tools.WriteInt32(WorkRam, ConvertAttributeTypeToInt(Type)); Console.WriteLine("AttributeType: " + Type.ToString()); switch (Type) { case AttributeType.TYPE_INT: int intWrite = Int32.Parse(attribute.Value, CultureInfo.InvariantCulture); tools.WriteInt32(WorkRam, intWrite); tools.WriteInt32(WorkRam, 0x00); Console.WriteLine("Int: " + intWrite.ToString()); break; case AttributeType.TYPE_FLOAT: float FloatValue = Single.Parse(attribute.Value.Replace("f", ""), CultureInfo.InvariantCulture); tools.WriteSingle(WorkRam, FloatValue); tools.WriteInt32(WorkRam, 0x00); Console.WriteLine("Float: " + FloatValue.ToString()); break; case AttributeType.TYPE_STRING: int StringOffset = AddGetStringTable(attribute.Value); int StringLen = Encoding.UTF8.GetBytes(attribute.Value).Length; tools.WriteInt32(WorkRam, StringOffset); tools.WriteInt32(WorkRam, StringLen); Console.WriteLine("String: " + attribute.Value); break; case AttributeType.TYPE_WSTRING: int WStringOffset = AddGetWstringTable(attribute.Value); int WStringLength = Encoding.Unicode.GetBytes(attribute.Value).Length; tools.WriteInt32(WorkRam, WStringOffset); tools.WriteInt32(WorkRam, WStringLength); Console.WriteLine("WString: " + attribute.Value); break; case AttributeType.TYPE_HASH: int hashId = Int32.Parse(CXMLSymbols.LookupId(attribute.Value), NumberStyles.HexNumber, CultureInfo.InvariantCulture); int HashTableOffset = AddGetHashTable(hashId); int HashTableSize = 4; tools.WriteInt32(WorkRam, HashTableOffset); tools.WriteInt32(WorkRam, HashTableSize); Console.WriteLine("Hash 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; tools.WriteInt32(WorkRam, IntArrayOffset); tools.WriteInt32(WorkRam, 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; tools.WriteInt32(WorkRam, FloatArrayOffset); tools.WriteInt32(WorkRam, 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, Tools.GenerateHash(data)); int FileSz = data.Length; tools.WriteInt32(WorkRam, FilePtr); tools.WriteInt32(WorkRam, FileSz); Console.WriteLine("Reading File: " + fPath); break; case AttributeType.TYPE_ID_REF: int IdTableOffset = AddGetIdTable(attribute.Value, Convert.ToInt32(TreeTable.Position)); tools.WriteInt32(WorkRam, IdTableOffset); tools.WriteInt32(WorkRam, 0x00); Console.WriteLine("ID Ref: " + ElementName + " " + TreeTable.Position.ToString("X") + " (" + TreeTable.Position.ToString("X") + ")"); Console.WriteLine("ID Ref Offset: " + IdTableOffset + " sz: 0"); break; case AttributeType.TYPE_ID: IdTableOffset = AddGetIdTable(attribute.Value, -1); tools.WriteInt32(WorkRam, IdTableOffset); tools.WriteInt32(WorkRam, 0x00); Console.WriteLine("ID : " + IdTableOffset + " sz: 0"); break; case AttributeType.TYPE_ID_HASH_REF: int hash = Int32.Parse(CXMLSymbols.LookupId(attribute.Value), NumberStyles.HexNumber, CultureInfo.InvariantCulture); int HashIdTableOffset = AddGetHashIdTable(hash, Convert.ToInt32(TreeTable.Position)); tools.WriteInt32(WorkRam, HashIdTableOffset); tools.WriteInt32(WorkRam, 0x00); Console.WriteLine("Hash ID Ref: " + ElementName + " " + TreeTable.Position.ToString("X") + " (" + TreeTable.Position.ToString("X") + ")"); Console.WriteLine("Hash ID REF offset: " + HashIdTableOffset + " sz: 0"); break; case AttributeType.TYPE_ID_HASH: hash = Int32.Parse(CXMLSymbols.LookupId(attribute.Value), NumberStyles.HexNumber, CultureInfo.InvariantCulture); HashIdTableOffset = AddGetHashIdTable(hash, -1); tools.WriteInt32(WorkRam, HashIdTableOffset); tools.WriteInt32(WorkRam, 0x00); Console.WriteLine("Hash ID: " + HashIdTableOffset + " sz: 0"); break; case AttributeType.TYPE_NONE: break; default: throw new NotImplementedException("UNKNOWN TYPE @ " + TreeTable.Position); break; } Console.WriteLine(attribute.Name + "=" + attribute.Value); } public void WriteElement(XmlNode node) { MemoryStream WorkRam = new MemoryStream(); 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; tools.WriteInt32(WorkRam, ElementPtr); tools.WriteInt32(WorkRam, NumAttributes); tools.WriteInt32(WorkRam, ParentPtr); tools.WriteInt32(WorkRam, PrevSibling); tools.WriteInt32(WorkRam, NextSibling); tools.WriteInt32(WorkRam, FirstChild); tools.WriteInt32(WorkRam, LastChild); foreach (XmlAttribute attribute in node.Attributes) { WriteAttribute(attribute, WorkRam, 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); tools.WriteInt32(TreeTable, ElementPtr); tools.WriteInt32(TreeTable, NumAttributes); tools.WriteInt32(TreeTable, ParentPtr); tools.WriteInt32(TreeTable, PrevSibling); tools.WriteInt32(TreeTable, NextSibling); tools.WriteInt32(TreeTable, FirstChild); tools.WriteInt32(TreeTable, 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(); IDTable.Close(); HashIDTable.Close(); StringTable.Close(); WStringTable.Close(); HashTable.Close(); IntArrayTable.Close(); FloatArrayTable.Close(); FileTable.Close(); TreeTableEntries.Clear(); StringTableEntries.Clear(); WStringTableEntries.Clear(); HashTableEntries.Clear(); IntArrayTableEntries.Clear(); FloatArrayTableEntries.Clear(); FileTableEntries.Clear(); IDTableEntries.Clear(); HashIDTableEntries.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); Tools.WriteLittleEndainInt(InfoFile, Version); byte[] headerPlaceholder; if (!ps3) headerPlaceholder = new byte[0x48]; else headerPlaceholder = new byte[0x30]; InfoFile.Write(headerPlaceholder, 0x00, headerPlaceholder.Length); Align(InfoFile, 0x10); 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 IdTableOffset = Convert.ToInt32(InfoFile.Position); int IdTableSize = Convert.ToInt32(IDTable.Length); Align(IDTable, 0x10); IDTable.Seek(0x00, SeekOrigin.Begin); IDTable.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 StringTableOffset = Convert.ToInt32(InfoFile.Position); int StringTableSize = Convert.ToInt32(StringTable.Length); Align(StringTable, 0x10); StringTable.Seek(0x00, SeekOrigin.Begin); StringTable.CopyTo(InfoFile); int WStringTableOffset = Convert.ToInt32(InfoFile.Position); int WStringTableSize = Convert.ToInt32(WStringTable.Length); Align(WStringTable, 0x10); WStringTable.Seek(0x00, SeekOrigin.Begin); WStringTable.CopyTo(InfoFile); int HashTableOffset = Convert.ToInt32(InfoFile.Position); int HashTableSize = Convert.ToInt32(HashTable.Length); Align(HashTable, 0x10); HashTable.Seek(0x00, SeekOrigin.Begin); HashTable.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); tools.WriteInt32(InfoFile, TreeTableOffset); tools.WriteInt32(InfoFile, TreeTableSize); tools.WriteInt32(InfoFile, IdTableOffset); tools.WriteInt32(InfoFile, IdTableSize); if (!ps3) { tools.WriteInt32(InfoFile, HashIdTableOffset); tools.WriteInt32(InfoFile, HashIdTableSize); } tools.WriteInt32(InfoFile, StringTableOffset); tools.WriteInt32(InfoFile, StringTableSize); if (!ps3) { tools.WriteInt32(InfoFile, WStringTableOffset); tools.WriteInt32(InfoFile, WStringTableSize); tools.WriteInt32(InfoFile, HashTableOffset); tools.WriteInt32(InfoFile, HashTableSize); } tools.WriteInt32(InfoFile, IntArrayTableOffset); tools.WriteInt32(InfoFile, IntArrayTableSize); tools.WriteInt32(InfoFile, FloatArrayTableOffset); tools.WriteInt32(InfoFile, FloatArrayTableSize); tools.WriteInt32(InfoFile, FileTableOffset); tools.WriteInt32(InfoFile, FileTableSize); Term(); } } }