using Ionic.Zlib; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Text; using System.Xml; namespace CXML { enum AttributeType { TYPE_NONE, TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_CHAR, TYPE_STYLE_ID, TYPE_INTEGER_ARRAY, TYPE_FLOAT_ARRAY, TYPE_FILE, TYPE_ID_STRING_LOOPBACK, TYPE_ID_STRING, TYPE_ID_INT_LOOPBACK, TYPE_ID_INT }; class Tools { public static String RemoveDecimals(float FloatValue) { String FloatStr = FloatValue.ToString(); String[] FloatParts = FloatStr.Split('.'); if (FloatParts.Length > 1) if(FloatParts[1].Length > 2) FloatParts[1] = FloatParts[1].Substring(0, 2); FloatStr = String.Join(".", FloatParts); return FloatStr; } public static MemoryStream ByteToStream(byte[] Array) { MemoryStream ms = new MemoryStream(); ms.Write(Array, 0x00, Array.Length); ms.Seek(0, SeekOrigin.Begin); return ms; } public static byte[] StreamToByte(Stream stream) { int StreamLen = (int)stream.Length; byte[] Bytes = new byte[StreamLen]; stream.Seek(0, SeekOrigin.Begin); stream.Read(Bytes, 0x00, StreamLen); return Bytes; } public static string GetFileExtension(byte[] Bytes) { if(IsImage(Bytes)) { return ".png"; } else if(IsZlib(Bytes)) { return ".zlib"; } else if(IsGim(Bytes)) { return ".gim"; } else if(IsRcf(Bytes)) { return ".rcs"; } else if(IsDDS(Bytes)) { return ".dds"; } else if (IsVAG(Bytes)) { return ".vag"; } else { return ".bin"; } } public static bool IsVAG(byte[] Bytes) { MemoryStream Data = ByteToStream(Bytes); String header = ReadString(Data, 4); if (header.StartsWith("VAG")) { return true; } else { return false; } } public static bool IsDDS(byte[] Bytes) { MemoryStream Data = ByteToStream(Bytes); String header = ReadString(Data, 4); if (header.StartsWith("DDS")) { return true; } else { return false; } } public static bool IsRcf(byte[] Bytes) { MemoryStream Data = ByteToStream(Bytes); String header = ReadString(Data, 5); if (header.StartsWith("RCSF")) { return true; } else { return false; } } public static bool IsGim(byte[] Bytes) { MemoryStream Data = ByteToStream(Bytes); String header = ReadString(Data,4); if(header.StartsWith("MIG")) { return true; } else { return false; } } public static bool IsZlib(byte[] Bytes) { if(Bytes[0] == 0x78) { if (Bytes[1] == 0x01) return true; if (Bytes[1] == 0x9C) return true; if (Bytes[1] == 0xDA) return true; } return false; } public static bool IsImage(byte[] Bytes) { try { GetBitmap(Bytes); } catch(Exception) { return false; } return true; } public static Bitmap GetBitmap(byte[] BitmapBytes) { MemoryStream ms = ByteToStream(BitmapBytes); Bitmap bmp = new Bitmap(ms); ms.Dispose(); return bmp; } public static String ReadStringAt(Stream ms, int location) { long ogPos = ms.Position; ms.Seek(location, SeekOrigin.Begin); String str = ReadString(ms); ms.Seek(ogPos, SeekOrigin.Begin); return str; } public static int ReadIntAt(Stream ms, int location) { BinaryReader BinReader = new BinaryReader(ms); long ogPos = ms.Position; ms.Seek(location, SeekOrigin.Begin); int i = BinReader.ReadInt32(); ms.Seek(ogPos, SeekOrigin.Begin); return i; } public static int ReadInt(Stream ms) { BinaryReader BinReader = new BinaryReader(ms); int i = BinReader.ReadInt32(); return i; } public static String ReadString(Stream ms, int limit = -1) { int i = 0xFF; MemoryStream StringStream = new MemoryStream(); do { i = ms.ReadByte(); if (i == 0 && i != limit) break; StringStream.WriteByte((byte)i); } while (true); byte[] StringData = StringStream.ToArray(); String str = Encoding.UTF8.GetString(StringData); return str; } } class Parser { private static bool _checkMagicNumber() { String Magic = Tools.ReadStringAt(InfoFile, 0x00); if (Magic.StartsWith("PSMA")) { return true; } else if(Magic.StartsWith("RCOF")) { return true; } else if (Magic.StartsWith("RCSF")) { return true; } else { return false; } } static FileStream InfoFile; static MemoryStream TreeTable; static MemoryStream StringIDTable; static MemoryStream IntIDTable; static MemoryStream StringTable; static MemoryStream CharTable; static MemoryStream StylesIDTable; static MemoryStream IntArrayTable; static MemoryStream FloatArrayTable; static MemoryStream FileTable; static BinaryReader bTreeTable; static BinaryReader bIntIDTable; static BinaryReader bFloatArrayTable; static BinaryReader bIntArrayTable; static BinaryReader bStylesIDTable; static XmlWriter XMLFile; public static void Init(string Path, bool CheckMagic = true) { InfoFile = File.Open(Path, FileMode.Open, FileAccess.Read); if(CheckMagic) { if (!_checkMagicNumber()) { throw new Exception("Incorrect magic number."); } } TreeTable = Tools.ByteToStream(GetTreeTable()); StringIDTable = Tools.ByteToStream(GetStringIDTable()); IntIDTable = Tools.ByteToStream(GetIntIDTable()); StringTable = Tools.ByteToStream(GetStringTable()); CharTable = Tools.ByteToStream(GetCharTable()); StylesIDTable = Tools.ByteToStream(GetStyleIDTable()); IntArrayTable = Tools.ByteToStream(GetIntArrayTable()); FloatArrayTable = Tools.ByteToStream(GetFloatArrayTable()); FileTable = Tools.ByteToStream(GetFileTable()); bTreeTable = new BinaryReader(TreeTable); bIntIDTable = new BinaryReader(IntIDTable); bFloatArrayTable = new BinaryReader(FloatArrayTable); bIntArrayTable = new BinaryReader(IntArrayTable); bStylesIDTable = new BinaryReader(StylesIDTable); return; } public static void Term() { InfoFile.Close(); TreeTable.Close(); StringIDTable.Close(); IntIDTable.Close(); StringTable.Close(); CharTable.Close(); StylesIDTable.Close(); IntArrayTable.Close(); FloatArrayTable.Close(); FileTable.Close(); bTreeTable.Close(); bIntIDTable.Close(); bFloatArrayTable.Close(); bIntArrayTable.Close(); bStylesIDTable.Close(); } public static int GetTreeTableOffset() { return Tools.ReadIntAt(InfoFile,0x8); } public static int GetTreeTableSize() { return Tools.ReadIntAt(InfoFile, 0xC); } public static int GetIDStringTableOffset() { return Tools.ReadIntAt(InfoFile, 0x10); } public static int GetIDStringTableSize() { return Tools.ReadIntAt(InfoFile, 0x14); } public static int GetIDIntTableOffset() { return Tools.ReadIntAt(InfoFile, 0x18); } public static int GetIDIntTableSize() { return Tools.ReadIntAt(InfoFile, 0x1C); } public static int GetStringTableOffset() { return Tools.ReadIntAt(InfoFile, 0x20); } public static int GetStringTableSize() { return Tools.ReadIntAt(InfoFile, 0x24); } public static int GetCharTableOffset() { return Tools.ReadIntAt(InfoFile, 0x28); } public static int GetCharTableSize() { return Tools.ReadIntAt(InfoFile, 0x2C); } public static int GetStyleIDTableOffset() { return Tools.ReadIntAt(InfoFile, 0x30); } public static int GetStyleIDTableSize() { return Tools.ReadIntAt(InfoFile, 0x34); } public static int GetIntArrayTableOffset() { return Tools.ReadIntAt(InfoFile, 0x38); } public static int GetIntArrayTableSize() { return Tools.ReadIntAt(InfoFile, 0x3C); } public static int GetFloatArrayTableOffset() { return Tools.ReadIntAt(InfoFile, 0x40); } public static int GetFloatArrayTableSize() { return Tools.ReadIntAt(InfoFile, 0x44); } public static int GetFileTableOffset() { return Tools.ReadIntAt(InfoFile, 0x48); } public static int GetFileTableSize() { return Tools.ReadIntAt(InfoFile, 0x4C); } public static byte[] GetTreeTable() { int TableOffset = GetTreeTableOffset(); int TableSize = GetTreeTableSize(); InfoFile.Seek(TableOffset, SeekOrigin.Begin); byte[] Table = new byte[TableSize]; InfoFile.Read(Table, 0x00, TableSize); return Table; } public static byte[] GetStringIDTable() { int IDStrTableOffset = GetIDStringTableOffset(); int IDStrTableSize = GetIDStringTableSize(); InfoFile.Seek(IDStrTableOffset, SeekOrigin.Begin); byte[] IDStringTable = new byte[IDStrTableSize]; InfoFile.Read(IDStringTable, 0x00, IDStrTableSize); return IDStringTable; } public static byte[] GetIntIDTable() { int IDIntTableOffset = GetIDIntTableOffset(); int IDIntTableSize = GetIDIntTableSize(); InfoFile.Seek(IDIntTableOffset, SeekOrigin.Begin); byte[] IDIntTable = new byte[IDIntTableSize]; InfoFile.Read(IDIntTable, 0x00, IDIntTableSize); return IDIntTable; } public static byte[] GetStringTable() { int StringTableOffset = GetStringTableOffset(); int StringTableSize = GetStringTableSize(); InfoFile.Seek(StringTableOffset, SeekOrigin.Begin); byte[] StringTable = new byte[StringTableSize]; InfoFile.Read(StringTable, 0x00, StringTableSize); return StringTable; } public static byte[] GetCharTable() { int CharTableOffset = GetCharTableOffset(); int CharTableSize = GetCharTableSize(); InfoFile.Seek(CharTableOffset, SeekOrigin.Begin); byte[] CharTable = new byte[CharTableSize]; InfoFile.Read(CharTable, 0x00, CharTableSize); return CharTable; } public static byte[] GetStyleIDTable() { int StyleIDTableOffset = GetStyleIDTableOffset(); int StyleIDTableSize = GetStyleIDTableSize(); InfoFile.Seek(StyleIDTableOffset, SeekOrigin.Begin); byte[] StyleIDTable = new byte[StyleIDTableSize]; InfoFile.Read(StyleIDTable, 0x00, StyleIDTableSize); return StyleIDTable; } public static byte[] GetIntArrayTable() { int IntArrayTableOffset = GetIntArrayTableOffset(); int IntArrayTableSize = GetIntArrayTableSize(); InfoFile.Seek(IntArrayTableOffset, SeekOrigin.Begin); byte[] IntArrayTable = new byte[IntArrayTableSize]; InfoFile.Read(IntArrayTable, 0x00, IntArrayTableSize); return IntArrayTable; } public static byte[] GetFloatArrayTable() { int FloatArrayTableOffset = GetFloatArrayTableOffset(); int FloatArrayTableSize = GetFloatArrayTableSize(); InfoFile.Seek(FloatArrayTableOffset, SeekOrigin.Begin); byte[] FloatArrayTable = new byte[FloatArrayTableSize]; InfoFile.Read(FloatArrayTable, 0x00, FloatArrayTableSize); return FloatArrayTable; } public static byte[] GetFileTable() { int DataOffset = GetFileTableOffset(); int DataLength = GetFileTableSize(); InfoFile.Seek(DataOffset, SeekOrigin.Begin); byte[] FileTable = new byte[DataLength]; InfoFile.Read(FileTable, 0x00, DataLength); return FileTable; } public static void DecompileCXML(String CXMLFile, bool force = false) { if(Directory.Exists("files")) Directory.Delete("files", true); Term(); Init(CXMLFile,force); XmlWriterSettings XMLSettings = new XmlWriterSettings(); XMLSettings.Indent = true; XMLFile = XmlWriter.Create(Path.GetFileNameWithoutExtension(CXMLFile)+".xml", XMLSettings); XMLFile.WriteStartDocument(); ReadElement(); XMLFile.WriteEndDocument(); XMLFile.Flush(); Term(); } public static void ReadAttribute(String ElementName = "") { int AttributePtr = bTreeTable.ReadInt32(); AttributeType Type = (AttributeType)bTreeTable.ReadInt32(); String AttributeName = Tools.ReadStringAt(StringTable, AttributePtr); object AttributeValue = ""; Console.WriteLine("AttributeType: " + Type.ToString() + " - "+ TreeTable.Position.ToString()); switch (Type) { case AttributeType.TYPE_NONE: Console.WriteLine("UNSUPPORTED TYPE @ " + TreeTable.Position); Console.ReadKey(); break; case AttributeType.TYPE_INT: AttributeValue = bTreeTable.ReadInt32(); TreeTable.Seek(4, SeekOrigin.Current); break; case AttributeType.TYPE_FLOAT: float FloatValue = bTreeTable.ReadSingle(); AttributeValue = Tools.RemoveDecimals(FloatValue); TreeTable.Seek(4, SeekOrigin.Current); break; case AttributeType.TYPE_STRING: int StringOffset = bTreeTable.ReadInt32(); int StringLen = bTreeTable.ReadInt32(); byte[] StringBytes = new byte[StringLen]; StringTable.Seek(StringOffset, 0x00); StringTable.Read(StringBytes, 0x00, StringLen); AttributeValue = Encoding.UTF8.GetString(StringBytes); break; case AttributeType.TYPE_CHAR: int CharOffset = bTreeTable.ReadInt32() * 2; int CharLen = bTreeTable.ReadInt32(); byte[] CharBytes = new byte[CharLen]; CharTable.Seek(CharOffset, 0x00); CharTable.Read(CharBytes, 0x00, CharLen); AttributeValue = Encoding.Unicode.GetString(CharBytes); break; case AttributeType.TYPE_STYLE_ID: int StyleTableOffset = bTreeTable.ReadInt32(); int StyleTableSize = bTreeTable.ReadInt32(); byte[] StyleTableData = new byte[StyleTableSize]; StylesIDTable.Seek(StyleTableOffset, SeekOrigin.Begin); StylesIDTable.Read(StyleTableData, 0x00, StyleTableSize); AttributeValue = BitConverter.ToString(StyleTableData).Replace("-",""); break; case AttributeType.TYPE_INTEGER_ARRAY: int IntArrayOffset = bTreeTable.ReadInt32(); int IntArraySize = bTreeTable.ReadInt32(); IntArrayTable.Seek(IntArrayOffset, SeekOrigin.Begin); List IntList = new List(); for (int i = 0; i < IntArraySize; i++) { int IntValue = bIntArrayTable.ReadInt32(); IntList.Add(IntValue); } int[] IntArray = IntList.ToArray(); AttributeValue = String.Join(", ", IntArray); break; case AttributeType.TYPE_FLOAT_ARRAY: int FloatArrayOffset = bTreeTable.ReadInt32(); int FloatArraySize = bTreeTable.ReadInt32(); FloatArrayTable.Seek(FloatArrayOffset, SeekOrigin.Begin); List StrList = new List(); for(int i = 0; i < FloatArraySize; i++) { FloatValue = bFloatArrayTable.ReadSingle(); StrList.Add(Tools.RemoveDecimals(FloatValue)); } string[] StrArray = StrList.ToArray(); AttributeValue = String.Join(", ", StrArray); break; case AttributeType.TYPE_FILE: int FilePtr = bTreeTable.ReadInt32(); int FileSz = bTreeTable.ReadInt32(); String FileName = ""; Byte[] FileData = new Byte[FileSz]; FileTable.Seek(FilePtr, SeekOrigin.Begin); FileTable.Read(FileData, 0, FileSz); if (!Directory.Exists("files")) Directory.CreateDirectory("files"); int count = 0; string CounterStr = count.ToString(); do { String Extension = Tools.GetFileExtension(FileData); CounterStr = count.ToString(); if (count == 0) CounterStr = ""; FileName = "files/" + ElementName + "-" + AttributeName + CounterStr + Extension; count++; } while (File.Exists(FileName)); Console.WriteLine("Writing: " + FileName); if(Path.GetExtension(FileName) == ".zlib") { Console.WriteLine("Decompressing " + FileName); Byte[] DecompressedData = ZlibStream.UncompressBuffer(FileData); String Extension = Tools.GetFileExtension(DecompressedData); if (!Directory.Exists("files/decompressed")) Directory.CreateDirectory("files/decompressed"); String DecompressedFilename = "files/decompressed/" + ElementName + "-" + AttributeName + CounterStr + Extension; Console.WriteLine("Decompressed file written to: " + DecompressedFilename); File.WriteAllBytes(DecompressedFilename, DecompressedData); if(Extension == ".gim") { if(File.Exists("GimConv/GimConv.exe")) { if (!Directory.Exists("files/converted")) Directory.CreateDirectory("files/converted"); Console.WriteLine("Decoding GIM."); Process Proc = new Process(); Proc.StartInfo.FileName = "GimConv/GimConv.exe"; Proc.StartInfo.Arguments = Path.GetFileName(DecompressedFilename) + " -o " + "../converted/" + ElementName + "-" + AttributeName + CounterStr + ".png"; Proc.StartInfo.RedirectStandardOutput = true; Proc.StartInfo.RedirectStandardError = true; Proc.StartInfo.UseShellExecute = false; Proc.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory()+ "/files/decompressed"; Proc.Start(); Proc.WaitForExit(); Console.WriteLine(Proc.StandardOutput.ReadToEnd()); Console.WriteLine("Done!"); } } } File.WriteAllBytes(FileName, FileData); AttributeValue = FileName; break; case AttributeType.TYPE_ID_STRING_LOOPBACK: int StringIdTableOffset = bTreeTable.ReadInt32(); AttributeValue = Tools.ReadStringAt(StringIDTable, StringIdTableOffset + 0x4); TreeTable.Seek(4, SeekOrigin.Current); break; case AttributeType.TYPE_ID_STRING: Console.WriteLine("UNSUPPORTED TYPE @ " + TreeTable.Position); Console.ReadKey(); break; case AttributeType.TYPE_ID_INT_LOOPBACK: int IntIdTableOffset = bTreeTable.ReadInt32(); IntIDTable.Seek(IntIdTableOffset, SeekOrigin.Begin); int Loopback = bIntIDTable.ReadInt32(); int IDValue = bIntIDTable.ReadInt32(); int StringPtr = Tools.ReadIntAt(TreeTable, Loopback); String LoopbackAttribute = Tools.ReadStringAt(StringTable, StringPtr); Console.WriteLine("Loopback: " + LoopbackAttribute); AttributeValue = IDValue.ToString("X8"); TreeTable.Seek(4, SeekOrigin.Current); break; case AttributeType.TYPE_ID_INT: IntIdTableOffset = bTreeTable.ReadInt32(); IntIDTable.Seek(IntIdTableOffset + 4, SeekOrigin.Begin); IDValue = bIntIDTable.ReadInt32(); AttributeValue = IDValue.ToString("X8"); TreeTable.Seek(4, SeekOrigin.Current); break; default: Console.WriteLine("UNKNOWN TYPE @ " + TreeTable.Position); break; }; Console.WriteLine(AttributeName + "=" + AttributeValue.ToString()); XMLFile.WriteAttributeString(AttributeName, AttributeValue.ToString()); XMLFile.Flush(); } public static void ReadElement() { int ElementPtr = bTreeTable.ReadInt32(); int NumAttributes = bTreeTable.ReadInt32(); int ParentPtr = bTreeTable.ReadInt32(); int PrevSibling = bTreeTable.ReadInt32(); int NextSibling = bTreeTable.ReadInt32(); int FirstChild = bTreeTable.ReadInt32(); int LastChild = bTreeTable.ReadInt32(); String ElementName = Tools.ReadStringAt(StringTable, ElementPtr); Console.WriteLine("Creating Element: " + ElementName); Console.WriteLine("Attribute Count: " + NumAttributes); Console.WriteLine("ParentPtr: " + ParentPtr); Console.WriteLine("PrevSibling: " + PrevSibling); Console.WriteLine("NextSibling: " + NextSibling); Console.WriteLine("FirstChild: " + FirstChild); Console.WriteLine("LastChild: " + LastChild); XMLFile.WriteStartElement(ElementName); if(NumAttributes > 0) { for (int i = 0; i < NumAttributes; i++) { ReadAttribute(ElementName); } } if (FirstChild != -1) { TreeTable.Seek(FirstChild, SeekOrigin.Begin); ReadElement(); } XMLFile.WriteEndElement(); XMLFile.Flush(); if (NextSibling != -1) { TreeTable.Seek(NextSibling, SeekOrigin.Begin); ReadElement(); } } } }