829 lines
32 KiB
C#
829 lines
32 KiB
C#
using Ionic.Zlib;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using General;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Globalization;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
public class TypingInformation
|
|
{
|
|
public string key;
|
|
public string value;
|
|
}
|
|
String MainDir = "";
|
|
String FileDir = "";
|
|
String XMLFilename = "";
|
|
String MagicNumber = "";
|
|
|
|
List<TypingInformation> SilicaTypingInformationList = new List<TypingInformation>();
|
|
|
|
String SilicaTypingInformation = "SilicaTypingInformation{{[[";
|
|
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<LoopbackHandler> FileList = new List<LoopbackHandler>();
|
|
|
|
XmlWriter XMLFile;
|
|
|
|
public bool ProcessFiles = false;
|
|
public bool WaitExit = false;
|
|
public bool NoRecursive = 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 int ReadVersion()
|
|
{
|
|
return Tools.ReadIntAt(InfoFile, 0x4);
|
|
}
|
|
public void AddTypingInfo(string key, object value)
|
|
{
|
|
foreach(TypingInformation tInfo in SilicaTypingInformationList)
|
|
{
|
|
if(tInfo.key == key)
|
|
{
|
|
if (tInfo.value.ToString() != value.ToString())
|
|
throw new Exception("Very Weird CXML");
|
|
return;
|
|
}
|
|
}
|
|
TypingInformation typingInfo = new TypingInformation();
|
|
typingInfo.key = key;
|
|
typingInfo.value = value.ToString();
|
|
SilicaTypingInformationList.Add(typingInfo);
|
|
}
|
|
public void Align(Stream s, int align)
|
|
{
|
|
|
|
int size = Convert.ToInt32(s.Position);
|
|
if (size % align != 0)
|
|
{
|
|
int totalAlign = (align - (size % align));
|
|
for (int i = 0; i < totalAlign; i++)
|
|
{
|
|
s.Seek(0x1, SeekOrigin.Current);
|
|
}
|
|
}
|
|
}
|
|
public void GotoEnd()
|
|
{
|
|
InfoFile.Seek(GetFileTableOffset(), SeekOrigin.Begin);
|
|
InfoFile.Seek(GetFileTableSize(), SeekOrigin.Current);
|
|
Align(InfoFile, 0x10);
|
|
}
|
|
|
|
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("REPLACE_WITH_SILICATOKEN");
|
|
AddTypingInfo("MAGIC", MagicNumber);
|
|
AddTypingInfo("VERSION", ReadVersion());
|
|
ReadElements();
|
|
XMLFile.WriteEndDocument();
|
|
XMLFile.Flush();
|
|
XMLFile.Close();
|
|
|
|
GotoEnd();
|
|
int bytesRemaining = Convert.ToInt32(InfoFile.Length - InfoFile.Position);
|
|
if (bytesRemaining > 0) // Some RCS files have unexplainable random XML Data at the end of them, Dont ask me. im just the messanger
|
|
{
|
|
Console.WriteLine(bytesRemaining.ToString("X8", CultureInfo.InvariantCulture) + " Excess Bytes Found, Shoving it in SilicaTypingInformation.");
|
|
byte[] ExcessData = new byte[bytesRemaining];
|
|
InfoFile.Read(ExcessData, 0x00, bytesRemaining);
|
|
byte[] Compressed = ZlibStream.CompressBuffer(ExcessData);
|
|
string base64 = Convert.ToBase64String(Compressed).Replace("=", "*");
|
|
AddTypingInfo("EXCESS", base64);
|
|
}
|
|
for(int i = 0; i < SilicaTypingInformationList.Count; i++)
|
|
{
|
|
TypingInformation tinfo = SilicaTypingInformationList[i];
|
|
SilicaTypingInformation += tinfo.key + "=" + tinfo.value;
|
|
if(i+1 != SilicaTypingInformationList.Count)
|
|
{
|
|
SilicaTypingInformation += ",";
|
|
}
|
|
}
|
|
|
|
SilicaTypingInformation += "]]}}SilicaTypingInformation";
|
|
|
|
// Make corrections
|
|
string XmlData = File.ReadAllText(XMLPath, Encoding.UTF8);
|
|
XmlData = XmlData.Replace("REPLACE_WITH_SILICATOKEN", SilicaTypingInformation);
|
|
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);
|
|
|
|
if(!File.Exists(newPath))
|
|
{
|
|
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("NOT MOVING: " + 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
|
|
{
|
|
if (NoRecursive)
|
|
return;
|
|
|
|
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 RegisterFile(Int64 ElementPtr, String NewName, Boolean FileEntry, String IdealName=null)
|
|
{
|
|
Console.WriteLine("Adding Entry for Loopback: 0x" + ElementPtr.ToString("X8", CultureInfo.InvariantCulture) + " (" + 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;
|
|
FileList[i].FileName = IdealName;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
FileList[i].FileName = NewName;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
LoopbackHandler lpHandler = new LoopbackHandler();
|
|
if (FileEntry)
|
|
{
|
|
lpHandler.OldFileName = NewName;
|
|
lpHandler.FileName = IdealName;
|
|
}
|
|
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("G9", CultureInfo.InvariantCulture) + "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);
|
|
Console.WriteLine("String: " + AttributeValue);
|
|
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)));
|
|
Console.WriteLine("Char: " + AttributeValue);
|
|
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(CultureInfo.InvariantCulture) + " size: " + HashTableSize);
|
|
|
|
int HashId = bHashIDTable.ReadInt32();
|
|
|
|
AttributeValue = HashId.ToString("X8", CultureInfo.InvariantCulture);
|
|
|
|
break;
|
|
case AttributeType.TYPE_INTEGER_ARRAY:
|
|
int IntArrayOffset = bTreeTable.ReadInt32();
|
|
int IntArraySize = bTreeTable.ReadInt32();
|
|
IntArrayTable.Seek(IntArrayOffset*4, SeekOrigin.Begin);
|
|
|
|
List<int> IntList = new List<int>();
|
|
|
|
for (int i = 0; i < IntArraySize; i++)
|
|
{
|
|
int IntValue = bIntArrayTable.ReadInt32();
|
|
IntList.Add(IntValue);
|
|
}
|
|
int[] IntArray = IntList.ToArray();
|
|
AttributeValue = "[" + String.Join(", ", IntArray) + "]";
|
|
Console.WriteLine("Int Array: " + AttributeValue.ToString());
|
|
break;
|
|
case AttributeType.TYPE_FLOAT_ARRAY:
|
|
int FloatArrayOffset = bTreeTable.ReadInt32();
|
|
int FloatArraySize = bTreeTable.ReadInt32();
|
|
FloatArrayTable.Seek(FloatArrayOffset*4, SeekOrigin.Begin);
|
|
|
|
List<string> StrList = new List<string>();
|
|
|
|
for(int i = 0; i < FloatArraySize; i++)
|
|
{
|
|
FloatValue = bFloatArrayTable.ReadSingle();
|
|
StrList.Add(FloatValue.ToString("G9", CultureInfo.InvariantCulture) +"f");
|
|
}
|
|
string[] StrArray = StrList.ToArray();
|
|
AttributeValue = "[" + String.Join(", ", StrArray) + "]";
|
|
Console.WriteLine("Float Array: " + AttributeValue.ToString());
|
|
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 FileSmallHash = Tools.GenerateShortHash(FileData);
|
|
String Extension = Tools.GetFileExtension(FileData);
|
|
String FileName = Path.Combine(FileDir, "original", FilePtr.ToString("X", CultureInfo.InvariantCulture) +"-"+FileHash + Extension);
|
|
String IdealName = ElementName + "-" + AttributeName + "-" + FileSmallHash + Extension;
|
|
if (!File.Exists(FileName))
|
|
{
|
|
Console.WriteLine("Writing: " + FileName);
|
|
|
|
if (!Directory.Exists(Path.GetDirectoryName(FileName)))
|
|
Directory.CreateDirectory(Path.GetDirectoryName(FileName));
|
|
|
|
File.WriteAllBytes(FileName, FileData);
|
|
RegisterFile(ElementPtr, FileName, true, IdealName);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("File allready extracted.");
|
|
}
|
|
|
|
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);
|
|
RegisterFile(LoopbackPtr, AttributeValue.ToString(), false);
|
|
|
|
Console.WriteLine("Loopback ID String: " + StringIdTableOffset + " sz: " + bTreeTable.ReadInt32());
|
|
break;
|
|
case AttributeType.TYPE_ID_STRING: // This is probably right, tbh
|
|
StringIdTableOffset = bTreeTable.ReadInt32();
|
|
StringIDTable.Seek(StringIdTableOffset + 4, SeekOrigin.Begin);
|
|
|
|
AttributeValue = Tools.ReadString(StringIDTable);
|
|
|
|
Console.WriteLine("ID String: " + StringIdTableOffset + " sz: " + bTreeTable.ReadInt32());
|
|
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("Int Loopback: " + LoopbackAttribute + " " + LoopbackPtr.ToString("X", CultureInfo.InvariantCulture) + " (" + ElementPtr.ToString("X") + ")");
|
|
|
|
AttributeValue = IDValue.ToString("X8", CultureInfo.InvariantCulture);
|
|
RegisterFile(LoopbackPtr, AttributeValue.ToString(), 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", CultureInfo.InvariantCulture);
|
|
Console.WriteLine("Int Id: " + IntIdTableOffset + " sz: " + bTreeTable.ReadInt32());
|
|
break;
|
|
default:
|
|
Console.WriteLine("UNKNOWN TYPE @ " + TreeTable.Position);
|
|
Console.ReadKey();
|
|
break;
|
|
};
|
|
|
|
AddTypingInfo(ElementName + ":" + AttributeName, ((int)Type).ToString(CultureInfo.InvariantCulture));
|
|
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();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
};
|