using Ionic.Zlib; using System; using System.Collections.Generic; using System.Diagnostics; using General; using System.IO; using System.Text; using System.Xml; namespace CXML { enum AttributeType { TYPE_NONE, TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_CHAR, TYPE_HASH_ID, TYPE_INTEGER_ARRAY, TYPE_FLOAT_ARRAY, TYPE_FILE, TYPE_ID_STRING_LOOPBACK, TYPE_ID_STRING, TYPE_ID_INT_LOOPBACK, TYPE_ID_INT }; class CXMLParser { private bool _checkMagicNumber() { String Magic = Tools.ReadStringAt(InfoFile, 0x00); MagicNumber = Magic.Substring(0,4); if (Magic.StartsWith("PSMA")) { return true; } else if(Magic.StartsWith("RCOF")) { return true; } else if (Magic.StartsWith("RCSF")) { return true; } else { return false; } } String MainDir = ""; String FileDir = ""; String XMLFilename = ""; String MagicNumber = ""; FileStream InfoFile; MemoryStream TreeTable; MemoryStream StringIDTable; MemoryStream IntIDTable; MemoryStream StringTable; MemoryStream CharTable; MemoryStream HashIDTable; MemoryStream IntArrayTable; MemoryStream FloatArrayTable; MemoryStream FileTable; Boolean IsInitalized = false; BinaryReader bTreeTable; BinaryReader bIntIDTable; BinaryReader bFloatArrayTable; BinaryReader bIntArrayTable; BinaryReader bHashIDTable; BinaryReader bStringIDTable; public class LoopbackHandler { public String FileName; public String OldFileName; public Int64 FilePointer; } List FileList = new List(); XmlWriter XMLFile; public bool ProcessFiles = false; public bool WaitExit = false; public void Init(string path, bool CheckMagic = true) { InfoFile = File.Open(path, FileMode.Open, FileAccess.Read); bool MagicValid = _checkMagicNumber(); if (CheckMagic) { if (!MagicValid) { 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()); HashIDTable = Tools.ByteToStream(GetHashIDTable()); 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); bHashIDTable = new BinaryReader(HashIDTable); bStringIDTable = new BinaryReader(StringIDTable); MainDir = Path.GetFileNameWithoutExtension(path); FileDir = Path.Combine(MainDir, "files"); XMLFilename = Path.GetFileNameWithoutExtension(path) + ".xml"; IsInitalized = true; return; } 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(); FileList.Clear(); IsInitalized = false; } public int GetTreeTableOffset() { return Tools.ReadIntAt(InfoFile,0x8); } public int GetTreeTableSize() { return Tools.ReadIntAt(InfoFile, 0xC); } public int GetIDStringTableOffset() { return Tools.ReadIntAt(InfoFile, 0x10); } public int GetIDStringTableSize() { return Tools.ReadIntAt(InfoFile, 0x14); } public int GetIDIntTableOffset() { return Tools.ReadIntAt(InfoFile, 0x18); } public int GetIDIntTableSize() { return Tools.ReadIntAt(InfoFile, 0x1C); } public int GetStringTableOffset() { return Tools.ReadIntAt(InfoFile, 0x20); } public int GetStringTableSize() { return Tools.ReadIntAt(InfoFile, 0x24); } public int GetCharTableOffset() { return Tools.ReadIntAt(InfoFile, 0x28); } public int GetCharTableSize() { return Tools.ReadIntAt(InfoFile, 0x2C); } public int GetHashIDTableOffset() { return Tools.ReadIntAt(InfoFile, 0x30); } public int GetHashIDTableSize() { return Tools.ReadIntAt(InfoFile, 0x34); } public int GetIntArrayTableOffset() { return Tools.ReadIntAt(InfoFile, 0x38); } public int GetIntArrayTableSize() { return Tools.ReadIntAt(InfoFile, 0x3C); } public int GetFloatArrayTableOffset() { return Tools.ReadIntAt(InfoFile, 0x40); } public int GetFloatArrayTableSize() { return Tools.ReadIntAt(InfoFile, 0x44); } public int GetFileTableOffset() { return Tools.ReadIntAt(InfoFile, 0x48); } public int GetFileTableSize() { return Tools.ReadIntAt(InfoFile, 0x4C); } public 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 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 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 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 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 byte[] GetHashIDTable() { int HashIDTableOffset = GetHashIDTableOffset(); int HashIDTableSize = GetHashIDTableSize(); InfoFile.Seek(HashIDTableOffset, SeekOrigin.Begin); byte[] HashIDTable = new byte[HashIDTableSize]; InfoFile.Read(HashIDTable, 0x00, HashIDTableSize); return HashIDTable; } public 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 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 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 void DecompileCXML(String CXMLFile, bool force = false) { if (!IsInitalized) Init(CXMLFile,force); if (Directory.Exists(MainDir)) Directory.Delete(MainDir, true); if (!Directory.Exists(FileDir)) Directory.CreateDirectory(FileDir); XmlWriterSettings XMLSettings = new XmlWriterSettings(); XMLSettings.Indent = true; string XMLPath = Path.Combine(MainDir, XMLFilename); XMLFile = XmlWriter.Create(XMLPath, XMLSettings); XMLFile.WriteStartDocument(); XMLFile.WriteComment("Decompiled with CXML Decompiler v6 By SilicaAndPina (Magic: \"" + MagicNumber + "\")"); ReadElements(); XMLFile.WriteEndDocument(); XMLFile.Flush(); XMLFile.Close(); // Make corrections string XmlData = File.ReadAllText(XMLPath, Encoding.UTF8); foreach(LoopbackHandler lpHandler in FileList) { if(lpHandler.OldFileName != null) { if(lpHandler.FileName != null) { string oldName = Path.Combine(lpHandler.OldFileName); string extension = Path.GetExtension(oldName); string folderPath = Path.GetDirectoryName(oldName); string newPath = Path.ChangeExtension(Path.Combine(folderPath, lpHandler.FileName), extension); File.Move(oldName, newPath); string xmlRelOldPath = oldName.Substring(Tools.GetRootFolder(oldName).Length + 1); string xmlRelNewPath = newPath.Substring(Tools.GetRootFolder(newPath).Length + 1); Console.WriteLine("Moved " + xmlRelOldPath + " => " + xmlRelNewPath); XmlData = XmlData.Replace(xmlRelOldPath, xmlRelNewPath); if (ProcessFiles) ProcessFile(newPath); } else { Console.WriteLine("Generated Filename used for " + lpHandler.OldFileName); if (ProcessFiles) ProcessFile(lpHandler.OldFileName); } } } File.WriteAllText(XMLPath, XmlData); Term(); } public void ProcessFile(String FileName) { String Extension = Path.GetExtension(FileName); if (Extension == ".rcs") // Recursive Decompile, remove this if statement to remove RCS processing { Console.WriteLine("Decompiling " + Path.GetFileName(FileName)); string DirectoryName = Path.Combine(FileDir, "converted", "RCStoXML", Path.GetFileNameWithoutExtension(FileName)); if (!Directory.Exists(DirectoryName)) Directory.CreateDirectory(DirectoryName); try { CXMLParser cxmlParser = new CXMLParser(); cxmlParser.Init(FileName); cxmlParser.ProcessFiles = this.ProcessFiles; cxmlParser.MainDir = DirectoryName; cxmlParser.FileDir = Path.Combine(cxmlParser.MainDir,"files"); cxmlParser.DecompileCXML(FileName); } catch (Exception) { }; } if (Extension == ".vag") //Remove this IF statment if you dont want VAG Conversion. { Console.WriteLine("Decoding: " + Path.GetFileName(FileName)); byte[] WaveData = VAG.VAGAudio.Vag2Wav(FileName); String WaveName = Path.GetFileNameWithoutExtension(FileName) + "-" + VAG.VAGAudio.GetFilename(FileName) + ".wav"; String DirectoryName = Path.Combine(FileDir , "converted", "VAGtoWAV"); if (!Directory.Exists(DirectoryName)) Directory.CreateDirectory(DirectoryName); File.WriteAllBytes(Path.Combine(DirectoryName, WaveName), WaveData); Console.WriteLine("Decoded file written to: " + WaveName); } if (Extension == ".gim") //Remove this IF statement if you dont want GIM Conversion. { if (File.Exists(Path.Combine("GimConv", "GimConv.exe"))) { String DirectoryName = Path.Combine(FileDir, "converted", "GIMtoPNG"); if (!Directory.Exists(DirectoryName)) Directory.CreateDirectory(DirectoryName); Console.WriteLine("Decoding GIM."); Process Proc = new Process(); Proc.StartInfo.FileName = Path.Combine("GimConv", "GimConv.exe"); Proc.StartInfo.Arguments = Path.GetFileName(FileName) + " -o " + Path.Combine(Environment.CurrentDirectory, DirectoryName, Path.GetFileName(Path.ChangeExtension(FileName, "png"))); Proc.StartInfo.RedirectStandardOutput = true; Proc.StartInfo.RedirectStandardError = true; Proc.StartInfo.UseShellExecute = false; Proc.StartInfo.WorkingDirectory = Path.GetDirectoryName(FileName); Proc.Start(); Proc.WaitForExit(); Console.WriteLine(Proc.StandardOutput.ReadToEnd()); Console.WriteLine("Done!"); } } if (Extension == ".z") { Console.WriteLine("Decompressing " + FileName); Byte[] FileData = File.ReadAllBytes(FileName); Byte[] DecompressedData = ZlibStream.UncompressBuffer(FileData); Extension = Tools.GetFileExtension(DecompressedData); String DirectoryName = Path.Combine(FileDir, "decompressed"); if (!Directory.Exists(DirectoryName)) Directory.CreateDirectory(DirectoryName); String DecompressedFilename = Path.Combine(DirectoryName, Path.ChangeExtension(Path.GetFileNameWithoutExtension(FileName), Extension)); Console.WriteLine("Decompressed file written to: " + DecompressedFilename); File.WriteAllBytes(DecompressedFilename, DecompressedData); ProcessFile(DecompressedFilename); } } public void ChangeFilename(Int64 ElementPtr, String NewName, Boolean FileEntry) { Console.WriteLine("Adding Entry for Loopback: 0x" + ElementPtr.ToString("X8") + " (" + NewName + ")"); for(int i = 0; i < FileList.Count; i++) { if(FileList[i].FilePointer == ElementPtr) { if (FileEntry) { if (FileList[i].OldFileName != null) break; FileList[i].OldFileName = NewName; return; } else { FileList[i].FileName = NewName; return; } } } LoopbackHandler lpHandler = new LoopbackHandler(); if (FileEntry) { lpHandler.OldFileName = NewName; } else { lpHandler.FileName = NewName; } lpHandler.FilePointer = ElementPtr; FileList.Add(lpHandler); return; } public void ReadAttribute(String ElementName = "", Int64 ElementPtr = 0) { 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(); Console.WriteLine("Int - Value: " + AttributeValue.ToString() + " sz:" + bTreeTable.ReadInt32()); break; case AttributeType.TYPE_FLOAT: float FloatValue = bTreeTable.ReadSingle(); AttributeValue = FloatValue.ToString()+"f"; Console.WriteLine("Float - Value: " + AttributeValue.ToString() + " sz:" + bTreeTable.ReadInt32()); 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.UTF8.GetString(Encoding.UTF8.GetBytes(Encoding.Unicode.GetString(CharBytes))); break; case AttributeType.TYPE_HASH_ID: int HashTableOffset = bTreeTable.ReadInt32(); int HashTableSize = bTreeTable.ReadInt32(); HashIDTable.Seek(HashTableOffset * 4, SeekOrigin.Begin); Console.WriteLine("Hash ID Offset:" + HashTableOffset.ToString() + " size: " + HashTableSize); int HashId = bHashIDTable.ReadInt32(); AttributeValue = "@"+HashId.ToString("X8"); break; case AttributeType.TYPE_INTEGER_ARRAY: int IntArrayOffset = bTreeTable.ReadInt32(); int IntArraySize = bTreeTable.ReadInt32(); IntArrayTable.Seek(IntArrayOffset*4, 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*4, SeekOrigin.Begin); List StrList = new List(); for(int i = 0; i < FloatArraySize; i++) { FloatValue = bFloatArrayTable.ReadSingle(); StrList.Add(FloatValue.ToString()+"f"); } string[] StrArray = StrList.ToArray(); AttributeValue = "[" + String.Join(", ", StrArray) + "]"; break; case AttributeType.TYPE_FILE: int FilePtr = bTreeTable.ReadInt32(); int FileSz = bTreeTable.ReadInt32(); Byte[] FileData = new Byte[FileSz]; FileTable.Seek(FilePtr, SeekOrigin.Begin); FileTable.Read(FileData, 0, FileSz); string FileHash = Tools.GenerateHash(FileData); String Extension = Tools.GetFileExtension(FileData); String FileName = Path.Combine(FileDir, ElementName, FileHash + Extension); if (!File.Exists(FileName)) { Console.WriteLine("Writing: " + FileName); if (!Directory.Exists(Path.GetDirectoryName(FileName))) Directory.CreateDirectory(Path.GetDirectoryName(FileName)); File.WriteAllBytes(FileName, FileData); ChangeFilename(ElementPtr, FileName, true); } else { Console.WriteLine("File allready extracted \n(theres a VERY low chance that it acturally is a different file that has the same hash.)"); } string xmlRelPath = FileName.Substring(Tools.GetRootFolder(FileName).Length + 1); AttributeValue = xmlRelPath; break; case AttributeType.TYPE_ID_STRING_LOOPBACK: int StringIdTableOffset = bTreeTable.ReadInt32(); StringIDTable.Seek(StringIdTableOffset, SeekOrigin.Begin); int LoopbackPtr = bStringIDTable.ReadInt32(); int StringPtr = Tools.ReadIntAt(TreeTable, LoopbackPtr); string LoopbackAttribute = Tools.ReadStringAt(StringTable, StringPtr); Console.WriteLine("Loopback: " + LoopbackAttribute +" "+ LoopbackPtr.ToString("X")+" ("+ElementPtr.ToString("X")+")"); AttributeValue = Tools.ReadString(StringIDTable); ChangeFilename(LoopbackPtr, AttributeValue.ToString(), false); Console.WriteLine("Loopback String: " + StringIdTableOffset + " sz: " + bTreeTable.ReadInt32()); 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); LoopbackPtr = bIntIDTable.ReadInt32(); StringPtr = Tools.ReadIntAt(TreeTable, LoopbackPtr); int IDValue = bIntIDTable.ReadInt32(); LoopbackAttribute = Tools.ReadStringAt(StringTable, StringPtr); Console.WriteLine("Loopback: " + LoopbackAttribute + " " + LoopbackPtr.ToString("X") + " (" + ElementPtr.ToString("X") + ")"); AttributeValue = "$"+IDValue.ToString("X8"); ChangeFilename(LoopbackPtr, IDValue.ToString("X8"), false); Console.WriteLine("Loopback Int: " + IntIdTableOffset + " sz: " + bTreeTable.ReadInt32()); break; case AttributeType.TYPE_ID_INT: IntIdTableOffset = bTreeTable.ReadInt32(); IntIDTable.Seek(IntIdTableOffset + 4, SeekOrigin.Begin); IDValue = bIntIDTable.ReadInt32(); AttributeValue = "#"+IDValue.ToString("X8"); Console.WriteLine("Int Id: " + IntIdTableOffset + " sz: " + bTreeTable.ReadInt32()); break; default: Console.WriteLine("UNKNOWN TYPE @ " + TreeTable.Position); break; }; Console.WriteLine(AttributeName + "=" + AttributeValue.ToString()); XMLFile.WriteAttributeString(AttributeName, AttributeValue.ToString()); XMLFile.Flush(); } public void ReadElements() { Int64 ElementLocation = TreeTable.Position; 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, ElementLocation); } } if (FirstChild != -1) { TreeTable.Seek(FirstChild, SeekOrigin.Begin); ReadElements(); } XMLFile.WriteEndElement(); XMLFile.Flush(); if (NextSibling != -1) { TreeTable.Seek(NextSibling, SeekOrigin.Begin); ReadElements(); } } } }