diff --git a/AppInfoCli/CXML.cs b/AppInfoCli/CXML.cs index 27a671f..603e326 100644 --- a/AppInfoCli/CXML.cs +++ b/AppInfoCli/CXML.cs @@ -31,7 +31,7 @@ namespace CXML class CXMLParser { - private static bool _checkMagicNumber() + private bool _checkMagicNumber() { String Magic = Tools.ReadStringAt(InfoFile, 0x00); @@ -54,35 +54,48 @@ namespace CXML } } - private static string _lastFileName = ""; - static String MainDir = ""; - static String FileDir = ""; - static String XMLFilename = ""; - - static FileStream InfoFile; + String MainDir = ""; + String FileDir = ""; + String XMLFilename = ""; - static MemoryStream TreeTable; - static MemoryStream StringIDTable; - static MemoryStream IntIDTable; - static MemoryStream StringTable; - static MemoryStream CharTable; - static MemoryStream HashIDTable; - static MemoryStream IntArrayTable; - static MemoryStream FloatArrayTable; - static MemoryStream FileTable; - - static BinaryReader bTreeTable; - static BinaryReader bIntIDTable; - static BinaryReader bFloatArrayTable; - static BinaryReader bIntArrayTable; - static BinaryReader bHashIDTable; - static BinaryReader bStringIDTable; + FileStream InfoFile; - static XmlWriter XMLFile; + MemoryStream TreeTable; + MemoryStream StringIDTable; + MemoryStream IntIDTable; + MemoryStream StringTable; + MemoryStream CharTable; + MemoryStream HashIDTable; + MemoryStream IntArrayTable; + MemoryStream FloatArrayTable; + MemoryStream FileTable; - public static void Init(string path, bool CheckMagic = true) + 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); if(CheckMagic) @@ -94,7 +107,6 @@ namespace CXML } - TreeTable = Tools.ByteToStream(GetTreeTable()); StringIDTable = Tools.ByteToStream(GetStringIDTable()); IntIDTable = Tools.ByteToStream(GetIntIDTable()); @@ -116,16 +128,12 @@ namespace CXML FileDir = Path.Combine(MainDir, "files"); XMLFilename = Path.GetFileNameWithoutExtension(path) + ".xml"; - if (Directory.Exists(MainDir)) - Directory.Delete(MainDir, true); - - if (!Directory.Exists(FileDir)) - Directory.CreateDirectory(FileDir); + IsInitalized = true; return; } - public static void Term() + public void Term() { InfoFile.Close(); TreeTable.Close(); @@ -143,99 +151,102 @@ namespace CXML bFloatArrayTable.Close(); bIntArrayTable.Close(); bHashIDTable.Close(); + + FileList.Clear(); + IsInitalized = false; } - public static int GetTreeTableOffset() + public int GetTreeTableOffset() { return Tools.ReadIntAt(InfoFile,0x8); } - public static int GetTreeTableSize() + public int GetTreeTableSize() { return Tools.ReadIntAt(InfoFile, 0xC); } - public static int GetIDStringTableOffset() + public int GetIDStringTableOffset() { return Tools.ReadIntAt(InfoFile, 0x10); } - public static int GetIDStringTableSize() + public int GetIDStringTableSize() { return Tools.ReadIntAt(InfoFile, 0x14); } - public static int GetIDIntTableOffset() + public int GetIDIntTableOffset() { return Tools.ReadIntAt(InfoFile, 0x18); } - public static int GetIDIntTableSize() + public int GetIDIntTableSize() { return Tools.ReadIntAt(InfoFile, 0x1C); } - public static int GetStringTableOffset() + public int GetStringTableOffset() { return Tools.ReadIntAt(InfoFile, 0x20); } - public static int GetStringTableSize() + public int GetStringTableSize() { return Tools.ReadIntAt(InfoFile, 0x24); } - public static int GetCharTableOffset() + public int GetCharTableOffset() { return Tools.ReadIntAt(InfoFile, 0x28); } - public static int GetCharTableSize() + public int GetCharTableSize() { return Tools.ReadIntAt(InfoFile, 0x2C); } - public static int GetHashIDTableOffset() + public int GetHashIDTableOffset() { return Tools.ReadIntAt(InfoFile, 0x30); } - public static int GetHashIDTableSize() + public int GetHashIDTableSize() { return Tools.ReadIntAt(InfoFile, 0x34); } - public static int GetIntArrayTableOffset() + public int GetIntArrayTableOffset() { return Tools.ReadIntAt(InfoFile, 0x38); } - public static int GetIntArrayTableSize() + public int GetIntArrayTableSize() { return Tools.ReadIntAt(InfoFile, 0x3C); } - public static int GetFloatArrayTableOffset() + public int GetFloatArrayTableOffset() { return Tools.ReadIntAt(InfoFile, 0x40); } - public static int GetFloatArrayTableSize() + public int GetFloatArrayTableSize() { return Tools.ReadIntAt(InfoFile, 0x44); } - public static int GetFileTableOffset() + public int GetFileTableOffset() { return Tools.ReadIntAt(InfoFile, 0x48); } - public static int GetFileTableSize() + public int GetFileTableSize() { return Tools.ReadIntAt(InfoFile, 0x4C); } - public static byte[] GetTreeTable() + public byte[] GetTreeTable() { int TableOffset = GetTreeTableOffset(); int TableSize = GetTreeTableSize(); @@ -245,7 +256,7 @@ namespace CXML return Table; } - public static byte[] GetStringIDTable() + public byte[] GetStringIDTable() { int IDStrTableOffset = GetIDStringTableOffset(); int IDStrTableSize = GetIDStringTableSize(); @@ -255,7 +266,7 @@ namespace CXML return IDStringTable; } - public static byte[] GetIntIDTable() + public byte[] GetIntIDTable() { int IDIntTableOffset = GetIDIntTableOffset(); int IDIntTableSize = GetIDIntTableSize(); @@ -265,7 +276,7 @@ namespace CXML return IDIntTable; } - public static byte[] GetStringTable() + public byte[] GetStringTable() { int StringTableOffset = GetStringTableOffset(); int StringTableSize = GetStringTableSize(); @@ -275,7 +286,7 @@ namespace CXML return StringTable; } - public static byte[] GetCharTable() + public byte[] GetCharTable() { int CharTableOffset = GetCharTableOffset(); int CharTableSize = GetCharTableSize(); @@ -285,7 +296,7 @@ namespace CXML return CharTable; } - public static byte[] GetHashIDTable() + public byte[] GetHashIDTable() { int HashIDTableOffset = GetHashIDTableOffset(); int HashIDTableSize = GetHashIDTableSize(); @@ -295,7 +306,7 @@ namespace CXML return HashIDTable; } - public static byte[] GetIntArrayTable() + public byte[] GetIntArrayTable() { int IntArrayTableOffset = GetIntArrayTableOffset(); int IntArrayTableSize = GetIntArrayTableSize(); @@ -305,7 +316,7 @@ namespace CXML return IntArrayTable; } - public static byte[] GetFloatArrayTable() + public byte[] GetFloatArrayTable() { int FloatArrayTableOffset = GetFloatArrayTableOffset(); int FloatArrayTableSize = GetFloatArrayTableSize(); @@ -315,7 +326,7 @@ namespace CXML return FloatArrayTable; } - public static byte[] GetFileTable() + public byte[] GetFileTable() { int DataOffset = GetFileTableOffset(); int DataLength = GetFileTableSize(); @@ -325,56 +336,96 @@ namespace CXML return FileTable; } - public static void DecompileCXML(String CXMLFile, bool force = false) + public void DecompileCXML(String CXMLFile, bool force = false) { - Directory.CreateDirectory(MainDir); + if (!IsInitalized) + Init(CXMLFile,force); - Term(); - 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; - - XMLFile = XmlWriter.Create(Path.Combine(MainDir, XMLFilename), XMLSettings); + string XMLPath = Path.Combine(MainDir, XMLFilename); + XMLFile = XmlWriter.Create(XMLPath, XMLSettings); XMLFile.WriteStartDocument(); 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) + { + Console.WriteLine("Replacing: " + lpHandler.OldFileName + " With " + lpHandler.FileName); + XmlData = XmlData.Replace(lpHandler.OldFileName, lpHandler.FileName); + } + if(ProcessFiles) + ProcessFile(Path.Combine(MainDir, lpHandler.FileName)); + } + File.WriteAllText(XMLPath, XmlData); Term(); } - public static void ProcessFile(String FileName) + 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"; - - if (!Directory.Exists(FileDir+"/converted/VAGtoWAV")) - Directory.CreateDirectory(FileDir + "/converted/VAGtoWAV"); + String DirectoryName = Path.Combine(FileDir , "converted", "VAGtoWAV"); - File.WriteAllBytes(FileDir + "/converted/VAGtoWAV/" + WaveName, WaveData); + 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("GimConv/GimConv.exe")) + if (File.Exists(Path.Combine("GimConv", "GimConv.exe"))) { - if (!Directory.Exists(FileDir + "/converted/GIMtoPNG")) - Directory.CreateDirectory(FileDir + "/converted/GIMtoPNG"); + 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 = "GimConv/GimConv.exe"; - Proc.StartInfo.Arguments = Path.GetFileName(FileName) + " -o " + "../converted/GIMtoPNG/" + Path.GetFileName(Path.ChangeExtension(FileName, "png")); + 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; @@ -387,17 +438,18 @@ namespace CXML } } - if (Extension == ".zlib") + 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(FileDir + "/decompressed")) - Directory.CreateDirectory(FileDir + "/decompressed"); + if (!Directory.Exists(DirectoryName)) + Directory.CreateDirectory(DirectoryName); - String DecompressedFilename = FileDir + "/decompressed /" + Path.GetFileNameWithoutExtension(FileName) + Extension; + String DecompressedFilename = Path.Combine(DirectoryName, Path.ChangeExtension(Path.GetFileNameWithoutExtension(FileName), Extension)); Console.WriteLine("Decompressed file written to: " + DecompressedFilename); File.WriteAllBytes(DecompressedFilename, DecompressedData); @@ -405,8 +457,28 @@ namespace CXML } } + public void ChangeFilename(Int64 ElementPtr, String NewName) + { + Console.WriteLine("Correcting for Loopback: 0x" + ElementPtr.ToString("X8") + " (" + NewName + ")"); + for(int i = 0; i < FileList.Count; i++) + { + if(FileList[i].FilePointer == ElementPtr) + { + string oldName = FileList[i].FileName; + string extension = Path.GetExtension(oldName); + string folderPath = Path.GetDirectoryName(FileList[i].FileName); + string newPath = Path.ChangeExtension(Path.Combine(folderPath, NewName), extension); + Console.WriteLine("Changed " + Path.Combine(MainDir, oldName) + " => " + Path.Combine(MainDir, newPath)); + File.Move(Path.Combine(MainDir, oldName), Path.Combine(MainDir, newPath)); + FileList[i].FileName = newPath; + FileList[i].OldFileName = oldName; + return; + } + } + Console.WriteLine("Nothing to correct!"); + } - public static void ReadAttribute(String ElementName = "") + public void ReadAttribute(String ElementName = "", Int64 ElementPtr = 0) { int AttributePtr = bTreeTable.ReadInt32(); AttributeType Type = (AttributeType)bTreeTable.ReadInt32(); @@ -448,7 +520,7 @@ namespace CXML CharTable.Seek(CharOffset, 0x00); CharTable.Read(CharBytes, 0x00, CharLen); - AttributeValue = Encoding.Unicode.GetString(CharBytes); + AttributeValue = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(Encoding.Unicode.GetString(CharBytes))); break; case AttributeType.TYPE_HASH_ID: int HashTableOffset = bTreeTable.ReadInt32(); @@ -499,7 +571,8 @@ namespace CXML String Extension = Tools.GetFileExtension(FileData); String FileName = Path.Combine(FileDir, ElementName, FileHash + Extension); - if(!File.Exists(FileName)) + String finalName = Path.Combine(Path.GetFileName(FileDir), ElementName, FileHash + Extension); + if (!File.Exists(FileName)) { Console.WriteLine("Writing: " + FileName); @@ -507,14 +580,18 @@ namespace CXML Directory.CreateDirectory(Path.GetDirectoryName(FileName)); File.WriteAllBytes(FileName, FileData); - ProcessFile(FileName); + LoopbackHandler lpHandler = new LoopbackHandler(); + lpHandler.FileName = finalName; + lpHandler.OldFileName = null; + lpHandler.FilePointer = ElementPtr; + FileList.Add(lpHandler); } else { Console.WriteLine("File allready extracted \n(theres a VERY low chance that it acturally is a different file that has the same hash.)"); } - - AttributeValue = Path.Combine("files", ElementName, FileHash + Extension); + + AttributeValue = finalName; break; case AttributeType.TYPE_ID_STRING_LOOPBACK: int StringIdTableOffset = bTreeTable.ReadInt32(); @@ -524,9 +601,10 @@ namespace CXML int StringPtr = Tools.ReadIntAt(TreeTable, LoopbackPtr); string LoopbackAttribute = Tools.ReadStringAt(StringTable, StringPtr); - Console.WriteLine("Loopback: " + LoopbackAttribute); - + Console.WriteLine("Loopback: " + LoopbackAttribute +" "+ LoopbackPtr.ToString("X")+" ("+ElementPtr.ToString("X")+")"); + AttributeValue = Tools.ReadString(StringIDTable); + ChangeFilename(LoopbackPtr, AttributeValue.ToString()); TreeTable.Seek(4, SeekOrigin.Current); break; @@ -543,11 +621,11 @@ namespace CXML int IDValue = bIntIDTable.ReadInt32(); - LoopbackAttribute = Tools.ReadStringAt(StringTable, StringPtr); - Console.WriteLine("Loopback: " + LoopbackAttribute); - + Console.WriteLine("Loopback: " + LoopbackAttribute + " " + LoopbackPtr.ToString("X") + " (" + ElementPtr.ToString("X") + ")"); + AttributeValue = IDValue.ToString("X8"); + ChangeFilename(LoopbackPtr, AttributeValue.ToString()); TreeTable.Seek(4, SeekOrigin.Current); break; @@ -570,9 +648,9 @@ namespace CXML XMLFile.Flush(); } - public static void ReadElements() + public void ReadElements() { - + Int64 ElementLocation = TreeTable.Position; int ElementPtr = bTreeTable.ReadInt32(); int NumAttributes = bTreeTable.ReadInt32(); @@ -600,7 +678,7 @@ namespace CXML { for (int i = 0; i < NumAttributes; i++) { - ReadAttribute(ElementName); + ReadAttribute(ElementName, ElementLocation); } } diff --git a/AppInfoCli/Program.cs b/AppInfoCli/Program.cs index dd27cb0..a5798ab 100644 --- a/AppInfoCli/Program.cs +++ b/AppInfoCli/Program.cs @@ -32,6 +32,8 @@ namespace CXMLCli Console.WriteLine("\t-iat --dump-int-array Dump int array table."); Console.WriteLine("\t-fat --dump-float-array Dump float array table."); Console.WriteLine("\t-ft --dump-file Dump file table."); + Console.WriteLine("\t-p --process-files Process Extracted Files"); + Console.WriteLine("\t-w --wait-exit Wait for keypress before exiting"); Console.WriteLine("\t-d --decompile Decompile CXML."); Console.WriteLine("Example: " + Path.GetFileName(Assembly.GetEntryAssembly().Location) + " app.info -f -s -t -f -d"); Console.WriteLine("Default functonality is to Decompile,\ntThis is canceled if any other arguments passed."); @@ -50,9 +52,13 @@ namespace CXMLCli File.WriteAllBytes(FileName, WaveData); return; } + CXML.CXMLParser cxmlParser = new CXML.CXMLParser(); + cxmlParser.Init(path, Check); - CXML.CXMLParser.Init(path, Check); - + if (args.Length == 1) + { + ArgsFull += "-d -p -w"; + } if (ArgsFull.Contains("-f") || ArgsFull.Contains("--force")) { @@ -67,68 +73,79 @@ namespace CXMLCli if (ArgsFull.Contains("-tt") || ArgsFull.Contains("--dump-tree")) { Console.WriteLine("Dumping tree table."); - File.WriteAllBytes("tree-table.bin", CXML.CXMLParser.GetTreeTable()); + File.WriteAllBytes("tree-table.bin", cxmlParser.GetTreeTable()); } if (ArgsFull.Contains("-ist") || ArgsFull.Contains("--dump-string-id")) { Console.WriteLine("Dumping string ID table."); - File.WriteAllBytes("string-id-table.bin", CXML.CXMLParser.GetStringIDTable()); + File.WriteAllBytes("string-id-table.bin", cxmlParser.GetStringIDTable()); } if (ArgsFull.Contains("-iit") || ArgsFull.Contains("--dump-int-id")) { Console.WriteLine("Dumping int ID table."); - File.WriteAllBytes("int-id-table.bin", CXML.CXMLParser.GetIntIDTable()); + File.WriteAllBytes("int-id-table.bin", cxmlParser.GetIntIDTable()); } if (ArgsFull.Contains("-st") || ArgsFull.Contains("--dump-string")) { Console.WriteLine("Dumping string table."); - File.WriteAllBytes("string-table.bin",CXML.CXMLParser.GetStringTable()); + File.WriteAllBytes("string-table.bin",cxmlParser.GetStringTable()); } if (ArgsFull.Contains("-ct") || ArgsFull.Contains("--dump-char")) { Console.WriteLine("Dumping char table."); - File.WriteAllBytes("char-table.bin", CXML.CXMLParser.GetCharTable()); + File.WriteAllBytes("char-table.bin", cxmlParser.GetCharTable()); } if ((ArgsFull.Contains("-sit") || ArgsFull.Contains("--dump-style-id")) /*kept for backwards comp*/ || (ArgsFull.Contains("-hit") || ArgsFull.Contains("--dump-hash-id"))) { Console.WriteLine("Dumping hash ID table."); - File.WriteAllBytes("hash-id-table.bin", CXML.CXMLParser.GetHashIDTable()); + File.WriteAllBytes("hash-id-table.bin", cxmlParser.GetHashIDTable()); } if (ArgsFull.Contains("-iat") || ArgsFull.Contains("--dump-int-array")) { Console.WriteLine("Dumping int array table."); - File.WriteAllBytes("int-array-table.bin", CXML.CXMLParser.GetIntArrayTable()); + File.WriteAllBytes("int-array-table.bin", cxmlParser.GetIntArrayTable()); } if (ArgsFull.Contains("-fat") || ArgsFull.Contains("--dump-float-array")) { Console.WriteLine("Dumping float array table."); - File.WriteAllBytes("float-array-table.bin", CXML.CXMLParser.GetFloatArrayTable()); + File.WriteAllBytes("float-array-table.bin", cxmlParser.GetFloatArrayTable()); } if (ArgsFull.Contains("-ft") || ArgsFull.Contains("--dump-tree")) { Console.WriteLine("Dumping file table."); - File.WriteAllBytes("file-table.bin", CXML.CXMLParser.GetTreeTable()); + File.WriteAllBytes("file-table.bin", cxmlParser.GetTreeTable()); } - if (ArgsFull.Contains("-d") || ArgsFull.Contains("--decompile") || args.Length == 1) + if (ArgsFull.Contains("-p") || ArgsFull.Contains("--process-files")) + { + cxmlParser.ProcessFiles = true; + } + + if (ArgsFull.Contains("-w") || ArgsFull.Contains("--wait-exit")) + { + cxmlParser.WaitExit = true; + } + + if (ArgsFull.Contains("-d") || ArgsFull.Contains("--decompile")) { Console.WriteLine("Decompiling."); - CXML.CXMLParser.DecompileCXML(path, Check); + cxmlParser.DecompileCXML(path, Check); Console.WriteLine("\n\nDECOMPILATION COMPLETE!"); - Console.ReadKey(); + if(cxmlParser.WaitExit) + Console.ReadKey(); } } } diff --git a/AppInfoCli/Tools.cs b/AppInfoCli/Tools.cs index d0a2cb3..7297f12 100644 --- a/AppInfoCli/Tools.cs +++ b/AppInfoCli/Tools.cs @@ -42,7 +42,7 @@ namespace General } else if (IsZlib(Bytes)) { - return ".zlib"; + return ".z"; } else if (IsRcf(Bytes)) { @@ -208,8 +208,7 @@ namespace General { SHA1 sha = SHA1.Create(); byte[] ShaBytes = sha.ComputeHash(Data); - int Hash = BitConverter.ToInt32(ShaBytes, 0); - return Hash.ToString("X8"); + return BitConverter.ToString(ShaBytes).Replace("-", "").ToUpper(); } public static String ReadString(Stream ms, int limit = -1)