884 lines
34 KiB
C#
884 lines
34 KiB
C#
using Ionic.Zlib;
|
|
using General;
|
|
using CXML;
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Globalization;
|
|
|
|
namespace CXMLDecompiler
|
|
{
|
|
class CXMLReader
|
|
{
|
|
private bool CheckMagicNumber()
|
|
{
|
|
|
|
String Magic = Tools.ReadStringAt(InfoFile, 0x00);
|
|
MagicNumber = Magic.Substring(0,4);
|
|
|
|
if (Magic.StartsWith("PSMA")) // PlayStation Mobile "app.info"
|
|
{
|
|
return true;
|
|
}
|
|
else if(Magic.StartsWith("RCOF")) // Vita/Ps4/Ps3 RCO
|
|
{
|
|
return true;
|
|
}
|
|
else if (Magic.StartsWith("RCSF")) // Found inside RCO
|
|
{
|
|
return true;
|
|
}
|
|
else if (Magic.StartsWith("P3TF")) // ps3 theme file
|
|
{
|
|
return true;
|
|
}
|
|
else if (Magic.StartsWith("CXML")) // cxml.py default
|
|
{
|
|
return true;
|
|
}
|
|
else if (Magic.StartsWith("TEST")) // ps3 cxml tools example
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
String MainDir = "";
|
|
String FileDir = "";
|
|
String XMLFilename = "";
|
|
String MagicNumber = "";
|
|
|
|
List<TypingInformation> SilicaTypingInformationList = new List<TypingInformation>();
|
|
|
|
String MagicReplacePattern = Tools.GenerateReplacePattern();
|
|
String SilicaTypingInformation = "SilicaTypingInformation{{[[";
|
|
FileStream InfoFile;
|
|
|
|
MemoryStream TreeTable;
|
|
MemoryStream IDTable;
|
|
MemoryStream HashIDTable;
|
|
MemoryStream StringTable;
|
|
MemoryStream WStringTable;
|
|
MemoryStream HashTable;
|
|
MemoryStream IntArrayTable;
|
|
MemoryStream FloatArrayTable;
|
|
MemoryStream FileTable;
|
|
|
|
Tools tools;
|
|
|
|
Boolean IsInitalized = false;
|
|
|
|
CXML.Version FileVersion;
|
|
|
|
public Endianness FileEndainness = Endianness.LITTLE_ENDIAN;
|
|
|
|
List<LoopbackHandler> FileList = new List<LoopbackHandler>();
|
|
|
|
XmlWriter XMLWriter;
|
|
|
|
public bool ps3 = false;
|
|
public bool ProcessFiles = false;
|
|
public bool NoRecursive = false;
|
|
public void Init(string path, string outPath, bool CheckMagic = true, bool forcePs3 = false, Endianness forceEndainness = Endianness.UNCHANGED)
|
|
{
|
|
InfoFile = File.Open(path, FileMode.Open, FileAccess.Read);
|
|
bool MagicValid = CheckMagicNumber();
|
|
if (CheckMagic)
|
|
{
|
|
if (!MagicValid)
|
|
{
|
|
throw new Exception("Incorrect magic number.");
|
|
}
|
|
|
|
}
|
|
// This is a bit of a hack, but what can you do?
|
|
FileVersion = ReadVersionEnum();
|
|
if (FileVersion == CXML.Version.PS3)
|
|
{
|
|
ps3 = true;
|
|
FileEndainness = Endianness.BIG_ENDIAN;
|
|
}
|
|
else
|
|
{
|
|
FileEndainness = Endianness.LITTLE_ENDIAN;
|
|
}
|
|
|
|
if (forcePs3)
|
|
ps3 = true;
|
|
|
|
if (forceEndainness != Endianness.UNCHANGED)
|
|
FileEndainness = forceEndainness;
|
|
|
|
tools = new Tools(FileEndainness == Endianness.BIG_ENDIAN);
|
|
|
|
TreeTable = Tools.ByteToStream(GetTreeTable());
|
|
StringTable = Tools.ByteToStream(GetStringTable());
|
|
IntArrayTable = Tools.ByteToStream(GetIntArrayTable());
|
|
FloatArrayTable = Tools.ByteToStream(GetFloatArrayTable());
|
|
IDTable = Tools.ByteToStream(GetIDTable());
|
|
FileTable = Tools.ByteToStream(GetFileTable());
|
|
|
|
// psvita, ps4 exclusive
|
|
if (!ps3)
|
|
{
|
|
HashTable = Tools.ByteToStream(GetHashTable());
|
|
HashIDTable = Tools.ByteToStream(GetHashIDTable());
|
|
WStringTable = Tools.ByteToStream(GetWStringTable());
|
|
}
|
|
else
|
|
{
|
|
HashTable = new MemoryStream();
|
|
HashIDTable = new MemoryStream();
|
|
WStringTable = new MemoryStream();
|
|
}
|
|
|
|
MainDir = Path.GetDirectoryName(outPath);
|
|
FileDir = Path.Combine(MainDir, Path.GetFileNameWithoutExtension(outPath) + "_files");
|
|
XMLFilename = Path.GetFileName(outPath);
|
|
|
|
IsInitalized = true;
|
|
|
|
return;
|
|
}
|
|
|
|
public void Term()
|
|
{
|
|
InfoFile.Close();
|
|
TreeTable.Close();
|
|
IDTable.Close();
|
|
HashIDTable.Close();
|
|
StringTable.Close();
|
|
WStringTable.Close();
|
|
HashTable.Close();
|
|
IntArrayTable.Close();
|
|
FloatArrayTable.Close();
|
|
FileTable.Close();
|
|
|
|
FileList.Clear();
|
|
IsInitalized = false;
|
|
}
|
|
|
|
public int GetTreeTableOffset()
|
|
{
|
|
return tools.ReadIntAt(InfoFile,0x8);
|
|
}
|
|
|
|
public int GetTreeTableSize()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0xC);
|
|
}
|
|
|
|
public int GetIDTableOffset()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x10);
|
|
}
|
|
|
|
public int GetIDTableSize()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x14);
|
|
}
|
|
|
|
public int GetHashIDTableOffset()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x18);
|
|
}
|
|
|
|
public int GetHashIDTableSize()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x1C);
|
|
}
|
|
|
|
public int GetStringTableOffset()
|
|
{
|
|
if (ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x18);
|
|
else
|
|
return tools.ReadIntAt(InfoFile, 0x20);
|
|
}
|
|
|
|
public int GetStringTableSize()
|
|
{
|
|
if (ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x1C);
|
|
else
|
|
return tools.ReadIntAt(InfoFile, 0x24);
|
|
}
|
|
|
|
public int GetWStringTableOffset()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x28);
|
|
}
|
|
|
|
public int GetWStringTableSize()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x2C);
|
|
}
|
|
|
|
public int GetHashTableOffset()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x30);
|
|
}
|
|
|
|
public int GetHashTableSize()
|
|
{
|
|
return tools.ReadIntAt(InfoFile, 0x34);
|
|
}
|
|
public int GetIntArrayTableOffset()
|
|
{
|
|
if(ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x20);
|
|
else
|
|
return tools.ReadIntAt(InfoFile, 0x38);
|
|
}
|
|
|
|
public int GetIntArrayTableSize()
|
|
{
|
|
if (ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x24);
|
|
else
|
|
return tools.ReadIntAt(InfoFile, 0x3C);
|
|
}
|
|
|
|
public int GetFloatArrayTableOffset()
|
|
{
|
|
if (ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x28);
|
|
else
|
|
return tools.ReadIntAt(InfoFile, 0x40);
|
|
}
|
|
|
|
public int GetFloatArrayTableSize()
|
|
{
|
|
if (ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x2C);
|
|
else
|
|
return tools.ReadIntAt(InfoFile, 0x44);
|
|
}
|
|
|
|
public int GetFileTableOffset()
|
|
{
|
|
if (ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x30);
|
|
else
|
|
return tools.ReadIntAt(InfoFile, 0x48);
|
|
}
|
|
|
|
public int GetFileTableSize()
|
|
{
|
|
if (ps3)
|
|
return tools.ReadIntAt(InfoFile, 0x34);
|
|
else
|
|
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[] GetIDTable()
|
|
{
|
|
int IDOffset = GetIDTableOffset();
|
|
int IDTableSize = GetIDTableSize();
|
|
InfoFile.Seek(IDOffset, SeekOrigin.Begin);
|
|
byte[] IDTable = new byte[IDTableSize];
|
|
InfoFile.Read(IDTable, 0x00, IDTableSize);
|
|
return IDTable;
|
|
}
|
|
|
|
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[] 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[] GetWStringTable()
|
|
{
|
|
int CharTableOffset = GetWStringTableOffset();
|
|
int CharTableSize = GetWStringTableSize();
|
|
InfoFile.Seek(CharTableOffset, SeekOrigin.Begin);
|
|
byte[] CharTable = new byte[CharTableSize];
|
|
InfoFile.Read(CharTable, 0x00, CharTableSize);
|
|
return CharTable;
|
|
}
|
|
|
|
public byte[] GetHashTable()
|
|
{
|
|
int HashTableOffset = GetHashTableOffset();
|
|
int HashTableSize = GetHashTableSize();
|
|
InfoFile.Seek(HashTableOffset, SeekOrigin.Begin);
|
|
byte[] HashTable = new byte[HashTableSize];
|
|
InfoFile.Read(HashTable, 0x00, HashTableSize);
|
|
return HashTable;
|
|
}
|
|
|
|
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 CXML.Version ReadVersionEnum()
|
|
{
|
|
try
|
|
{
|
|
return (CXML.Version)ReadVersionInt();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return CXML.Version.UNKNOWN;
|
|
}
|
|
}
|
|
public int ReadVersionInt()
|
|
{
|
|
return Tools.ReadLittleEndainIntAt(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, String XMLOutputFile)
|
|
{
|
|
|
|
if (!IsInitalized)
|
|
Init(CXMLFile, XMLOutputFile);
|
|
|
|
XmlWriterSettings XMLSettings = new XmlWriterSettings();
|
|
XMLSettings.Indent = true;
|
|
XMLSettings.Encoding = Encoding.UTF8;
|
|
|
|
string XMLPath = Path.Combine(MainDir, XMLFilename);
|
|
|
|
StreamWriter sw = new StreamWriter(new MemoryStream(), Encoding.UTF8);
|
|
|
|
|
|
XMLWriter = XmlWriter.Create(sw, XMLSettings);
|
|
XMLWriter.WriteStartDocument();
|
|
XMLWriter.WriteComment(MagicReplacePattern);
|
|
AddTypingInfo("MAGIC", MagicNumber);
|
|
AddTypingInfo("VERSION", "0x"+ReadVersionInt().ToString("X"));
|
|
AddTypingInfo("ENDAINESS", FileEndainness.ToString());
|
|
AddTypingInfo("PS3", ps3 ? "true" : "false");
|
|
ReadElements();
|
|
XMLWriter.WriteEndDocument();
|
|
XMLWriter.Flush();
|
|
XMLWriter.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";
|
|
byte[] XMLBytes = new byte[sw.BaseStream.Length];
|
|
|
|
sw.Flush();
|
|
sw.BaseStream.Seek(0x00, SeekOrigin.Begin);
|
|
sw.BaseStream.Read(XMLBytes, 0x00, XMLBytes.Length);
|
|
sw.Close();
|
|
|
|
string XMLData = Encoding.UTF8.GetString(XMLBytes);
|
|
|
|
// Resolve Replace Patterns
|
|
Console.WriteLine("Resolving Patterns ...");
|
|
XMLData = XMLData.Replace(MagicReplacePattern, SilicaTypingInformation);
|
|
|
|
foreach (LoopbackHandler lpHandler in FileList)
|
|
{
|
|
if (lpHandler.ReplacePattern != null && lpHandler.OldFileName != null)
|
|
{
|
|
|
|
string replacePattern = lpHandler.ReplacePattern;
|
|
string oldName = lpHandler.OldFileName;
|
|
string extension = Path.GetExtension(oldName);
|
|
string folderPath = Path.GetDirectoryName(oldName);
|
|
byte[] fileData = lpHandler.FileData;
|
|
|
|
if (lpHandler.FileName != null)
|
|
{
|
|
|
|
string newPath = Path.ChangeExtension(Path.Combine(folderPath, lpHandler.FileName), extension);
|
|
|
|
if (!Directory.Exists(Path.GetDirectoryName(newPath)))
|
|
Directory.CreateDirectory(Path.GetDirectoryName(newPath));
|
|
|
|
File.WriteAllBytes(newPath, fileData);
|
|
string XMLRelNewPath = Path.Combine(Path.GetFileName(FileDir), "original", Path.GetFileName(newPath));
|
|
Console.WriteLine("Resolved " + replacePattern + " => " + XMLRelNewPath);
|
|
XMLData = XMLData.Replace(replacePattern, XMLRelNewPath);
|
|
|
|
if (ProcessFiles)
|
|
ProcessFile(newPath);
|
|
}
|
|
else
|
|
{
|
|
if (!Directory.Exists(Path.GetDirectoryName(oldName)))
|
|
Directory.CreateDirectory(Path.GetDirectoryName(oldName));
|
|
|
|
File.WriteAllBytes(oldName, fileData);
|
|
|
|
string xmlRelNewPath = Path.Combine(Path.GetFileName(FileDir), "original", Path.GetFileName(oldName));
|
|
Console.WriteLine("Resolved " + replacePattern + " => " + xmlRelNewPath);
|
|
XMLData = XMLData.Replace(replacePattern, xmlRelNewPath);
|
|
|
|
|
|
if (ProcessFiles)
|
|
ProcessFile(oldName);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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");
|
|
if (!Directory.Exists(DirectoryName))
|
|
Directory.CreateDirectory(DirectoryName);
|
|
|
|
try
|
|
{
|
|
string outputXML = Path.ChangeExtension(Path.GetFileNameWithoutExtension(FileName), ".xml");
|
|
CXMLReader cxmlParser = new CXMLReader();
|
|
cxmlParser.Init(FileName, Path.Combine(DirectoryName, outputXML));
|
|
cxmlParser.ProcessFiles = this.ProcessFiles;
|
|
cxmlParser.DecompileCXML(FileName, outputXML);
|
|
}
|
|
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")
|
|
{
|
|
String DirectoryName = Path.Combine(FileDir, "converted", "GIMtoPNG");
|
|
|
|
if (!Directory.Exists(DirectoryName))
|
|
Directory.CreateDirectory(DirectoryName);
|
|
|
|
String OutputGim = Path.Combine(DirectoryName, Path.GetFileName(Path.ChangeExtension(FileName, "png")));
|
|
GimConv.ConvertGimToPng(FileName, OutputGim);
|
|
}
|
|
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 ReplacePattern=null, byte[] FileData=null, 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;
|
|
lpHandler.FileData = FileData;
|
|
lpHandler.ReplacePattern = ReplacePattern;
|
|
}
|
|
else
|
|
{
|
|
lpHandler.FileName = NewName;
|
|
}
|
|
lpHandler.FilePointer = ElementPtr;
|
|
FileList.Add(lpHandler);
|
|
return;
|
|
}
|
|
public AttributeType ReadAttributeType(Stream ms)
|
|
{
|
|
if(!ps3)
|
|
{
|
|
AttributeType type = (AttributeType)tools.ReadInt32(ms);
|
|
return type;
|
|
}
|
|
else
|
|
{
|
|
AttributeTypePs3 type = (AttributeTypePs3)tools.ReadInt32(ms);
|
|
return (AttributeType)Enum.Parse(typeof(AttributeType), type.ToString());
|
|
}
|
|
}
|
|
|
|
public void ReadAttribute(String ElementName = "", Int64 ElementPtr = 0)
|
|
{
|
|
int AttributePtr = tools.ReadInt32(TreeTable);
|
|
AttributeType Type = ReadAttributeType(TreeTable);
|
|
|
|
String AttributeName = Tools.ReadStringAt(StringTable, AttributePtr);
|
|
object AttributeValue = "";
|
|
|
|
Console.WriteLine("AttributeType: " + Type.ToString() + " - "+ TreeTable.Position.ToString());
|
|
switch (Type)
|
|
{
|
|
case AttributeType.TYPE_INT:
|
|
AttributeValue = tools.ReadInt32(TreeTable);
|
|
|
|
int sz = tools.ReadInt32(TreeTable);
|
|
Console.WriteLine("Int - Value: " + AttributeValue.ToString() + " sz:" + sz);
|
|
break;
|
|
case AttributeType.TYPE_FLOAT:
|
|
Double FloatValue = (Double)tools.ReadSingle(TreeTable);
|
|
string FloatStr = FloatValue.ToString("G9", CultureInfo.InvariantCulture);
|
|
if (!FloatStr.Contains("."))
|
|
FloatStr += ".0";
|
|
AttributeValue = FloatStr;
|
|
sz = tools.ReadInt32(TreeTable);
|
|
Console.WriteLine("Float: " + AttributeValue.ToString() + " sz:" + sz);
|
|
break;
|
|
case AttributeType.TYPE_STRING:
|
|
int StringOffset = tools.ReadInt32(TreeTable);
|
|
int StringLen = tools.ReadInt32(TreeTable);
|
|
|
|
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_WSTRING:
|
|
int WStrOffset = tools.ReadInt32(TreeTable) * 2;
|
|
int WStrLen = tools.ReadInt32(TreeTable);
|
|
|
|
byte[] WStrBytes = new byte[WStrLen];
|
|
WStringTable.Seek(WStrOffset, 0x00);
|
|
WStringTable.Read(WStrBytes, 0x00, WStrLen);
|
|
|
|
AttributeValue = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(Encoding.Unicode.GetString(WStrBytes)));
|
|
Console.WriteLine("WString: " + AttributeValue);
|
|
break;
|
|
case AttributeType.TYPE_HASH:
|
|
int HashTableOffset = tools.ReadInt32(TreeTable);
|
|
int HashTableSize = tools.ReadInt32(TreeTable);
|
|
HashTable.Seek(HashTableOffset * 4, SeekOrigin.Begin);
|
|
|
|
int Hash = tools.ReadInt32(HashTable);
|
|
|
|
AttributeValue = CXMLSymbols.LookupKey(Hash.ToString("X8", CultureInfo.InvariantCulture));
|
|
|
|
Console.WriteLine("Hash: " + AttributeValue.ToString() + " sz: " + HashTableSize);
|
|
break;
|
|
case AttributeType.TYPE_INTEGER_ARRAY:
|
|
int IntArrayOffset = tools.ReadInt32(TreeTable);
|
|
int IntArraySize = tools.ReadInt32(TreeTable);
|
|
IntArrayTable.Seek(IntArrayOffset*4, SeekOrigin.Begin);
|
|
|
|
List<int> IntList = new List<int>();
|
|
|
|
for (int i = 0; i < IntArraySize; i++)
|
|
{
|
|
int IntValue = tools.ReadInt32(IntArrayTable);
|
|
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 = tools.ReadInt32(TreeTable);
|
|
int FloatArraySize = tools.ReadInt32(TreeTable);
|
|
FloatArrayTable.Seek(FloatArrayOffset*4, SeekOrigin.Begin);
|
|
|
|
List<string> StrList = new List<string>();
|
|
|
|
for(int i = 0; i < FloatArraySize; i++)
|
|
{
|
|
FloatValue = (Double)tools.ReadSingle(FloatArrayTable);
|
|
FloatStr = FloatValue.ToString("G9", CultureInfo.InvariantCulture);
|
|
if (!FloatStr.Contains("."))
|
|
FloatStr += ".0";
|
|
StrList.Add(FloatStr);
|
|
}
|
|
string[] StrArray = StrList.ToArray();
|
|
AttributeValue = String.Join(", ", StrArray);
|
|
Console.WriteLine("Float Array: " + AttributeValue.ToString());
|
|
break;
|
|
case AttributeType.TYPE_FILE:
|
|
int FilePtr = tools.ReadInt32(TreeTable);
|
|
int FileSz = tools.ReadInt32(TreeTable);
|
|
|
|
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;
|
|
String ReplacePattern = Tools.GenerateReplacePattern();
|
|
|
|
RegisterFile(ElementPtr, FileName, true, ReplacePattern, FileData, IdealName);
|
|
|
|
AttributeValue = ReplacePattern;
|
|
break;
|
|
case AttributeType.TYPE_ID_REF:
|
|
int StringIdTableOffset = tools.ReadInt32(TreeTable);
|
|
IDTable.Seek(StringIdTableOffset, SeekOrigin.Begin);
|
|
|
|
int LoopbackPtr = tools.ReadInt32(IDTable);
|
|
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(IDTable);
|
|
RegisterFile(LoopbackPtr, AttributeValue.ToString(), false);
|
|
|
|
sz = tools.ReadInt32(TreeTable);
|
|
|
|
Console.WriteLine("ID Ref: " + StringIdTableOffset + " sz: " + sz);
|
|
break;
|
|
case AttributeType.TYPE_ID:
|
|
StringIdTableOffset = tools.ReadInt32(TreeTable);
|
|
IDTable.Seek(StringIdTableOffset + 4, SeekOrigin.Begin);
|
|
|
|
AttributeValue = Tools.ReadString(IDTable);
|
|
sz = tools.ReadInt32(TreeTable);
|
|
Console.WriteLine("ID : " + StringIdTableOffset + " sz: " + sz);
|
|
break;
|
|
case AttributeType.TYPE_ID_HASH_REF:
|
|
int IntIdTableOffset = tools.ReadInt32(TreeTable);
|
|
HashIDTable.Seek(IntIdTableOffset, SeekOrigin.Begin);
|
|
|
|
LoopbackPtr = tools.ReadInt32(HashIDTable);
|
|
StringPtr = tools.ReadIntAt(TreeTable, LoopbackPtr);
|
|
|
|
int IDValue = tools.ReadInt32(HashIDTable);
|
|
|
|
LoopbackAttribute = Tools.ReadStringAt(StringTable, StringPtr);
|
|
Console.WriteLine("Int Loopback: " + LoopbackAttribute + " " + LoopbackPtr.ToString("X", CultureInfo.InvariantCulture) + " (" + ElementPtr.ToString("X") + ")");
|
|
|
|
AttributeValue = CXMLSymbols.LookupKey(IDValue.ToString("X8", CultureInfo.InvariantCulture));
|
|
RegisterFile(LoopbackPtr, AttributeValue.ToString(), false);
|
|
|
|
sz = tools.ReadInt32(TreeTable);
|
|
Console.WriteLine("Hash Ref: " + IntIdTableOffset + " sz: " + sz);
|
|
break;
|
|
case AttributeType.TYPE_ID_HASH:
|
|
IntIdTableOffset = tools.ReadInt32(TreeTable);
|
|
HashIDTable.Seek(IntIdTableOffset + 4, SeekOrigin.Begin);
|
|
IDValue = tools.ReadInt32(HashIDTable);
|
|
|
|
AttributeValue = CXMLSymbols.LookupKey(IDValue.ToString("X8", CultureInfo.InvariantCulture));
|
|
sz = tools.ReadInt32(TreeTable);
|
|
Console.WriteLine("Hash Id: " + IntIdTableOffset + " sz: " + sz);
|
|
break;
|
|
case AttributeType.TYPE_NONE:
|
|
default:
|
|
new NotImplementedException("UNKNOWN TYPE @ " + TreeTable.Position);
|
|
break;
|
|
};
|
|
|
|
AddTypingInfo(ElementName + ":" + AttributeName, Type.ToString());
|
|
Console.WriteLine(AttributeName + "=" + AttributeValue.ToString());
|
|
XMLWriter.WriteAttributeString(AttributeName, AttributeValue.ToString());
|
|
XMLWriter.Flush();
|
|
}
|
|
|
|
public void ReadElements()
|
|
{
|
|
Int64 ElementLocation = TreeTable.Position;
|
|
int ElementPtr = tools.ReadInt32(TreeTable);
|
|
int NumAttributes = tools.ReadInt32(TreeTable);
|
|
|
|
int ParentPtr = tools.ReadInt32(TreeTable);
|
|
int PrevSibling = tools.ReadInt32(TreeTable);
|
|
int NextSibling = tools.ReadInt32(TreeTable);
|
|
|
|
int FirstChild = tools.ReadInt32(TreeTable);
|
|
int LastChild = tools.ReadInt32(TreeTable);
|
|
|
|
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);
|
|
|
|
XMLWriter.WriteStartElement(ElementName);
|
|
|
|
if(NumAttributes > 0)
|
|
{
|
|
for (int i = 0; i < NumAttributes; i++)
|
|
{
|
|
ReadAttribute(ElementName, ElementLocation);
|
|
}
|
|
}
|
|
|
|
if (FirstChild != -1)
|
|
{
|
|
TreeTable.Seek(FirstChild, SeekOrigin.Begin);
|
|
ReadElements();
|
|
}
|
|
|
|
|
|
XMLWriter.WriteEndElement();
|
|
XMLWriter.Flush();
|
|
|
|
if (NextSibling != -1)
|
|
{
|
|
TreeTable.Seek(NextSibling, SeekOrigin.Begin);
|
|
ReadElements();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
};
|