From f04a6de542e2ca1446b13007f684aa559f5390d6 Mon Sep 17 00:00:00 2001 From: Kurt Date: Thu, 22 Jan 2015 19:14:58 -0800 Subject: [PATCH] Initial RomFS code commit (RomFS-Builder) --- pk3DS/RomFS.cs | 769 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 769 insertions(+) create mode 100644 pk3DS/RomFS.cs diff --git a/pk3DS/RomFS.cs b/pk3DS/RomFS.cs new file mode 100644 index 0000000..55b5a61 --- /dev/null +++ b/pk3DS/RomFS.cs @@ -0,0 +1,769 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Security.Cryptography; +using System.Windows.Forms; +using System.Threading; + +namespace MetaDataBuilder +{ + class MetaDataBuilder + { + private const int PADDING_ALIGN = 16; + private string ROOT_DIR; + private string TempFile; + private const uint ROMFS_UNUSED_ENTRY = 0xFFFFFFFF; + + private void BuildRomFS(string path, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + ROOT_DIR = path; + FileNameTable FNT = new FileNameTable(ROOT_DIR); + RomfsFile[] RomFiles = new RomfsFile[FNT.NumFiles]; + LayoutManager.Input[] In = new LayoutManager.Input[FNT.NumFiles]; + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("Creating Layout..."))); + for (int i = 0; i < FNT.NumFiles; i++) + { + In[i] = new LayoutManager.Input(); + In[i].FilePath = FNT.NameEntryTable[i].FullName; + In[i].AlignmentSize = 0x10; + } + LayoutManager.Output[] Out = LayoutManager.Create(In); + for (int i = 0; i < Out.Length; i++) + { + RomFiles[i] = new RomfsFile(); + RomFiles[i].Offset = Out[i].Offset; + RomFiles[i].PathName = Out[i].FilePath.Replace(Path.GetFullPath(ROOT_DIR), "").Replace("\\", "/"); + RomFiles[i].FullName = Out[i].FilePath; + RomFiles[i].Size = Out[i].Size; + } + using (MemoryStream memoryStream = new MemoryStream()) + { + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("Creating RomFS MetaData..."))); + MetaDataBuilder mdb = new MetaDataBuilder(); + mdb.BuildRomFSHeader(memoryStream, RomFiles, ROOT_DIR); + MakeRomFSData(RomFiles, memoryStream); + } + } + + public static ulong Align(ulong input, ulong alignsize) + { + ulong output = input; + if (output % alignsize != 0) + { + output += (alignsize - (output % alignsize)); + } + return output; + } + + private void MakeRomFSData(RomfsFile[] RomFiles, MemoryStream metadata, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + TempFile = Path.GetRandomFileName(); + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("Computing IVFC Header Data..."))); + IVFCInfo ivfc = new IVFCInfo(); + ivfc.Levels = new IVFCLevel[3]; + for (int i = 0; i < ivfc.Levels.Length; i++) + { + ivfc.Levels[i] = new IVFCLevel(); + ivfc.Levels[i].BlockSize = 0x1000; + } + ivfc.Levels[2].DataLength = RomfsFile.GetDataBlockLength(RomFiles, (ulong)metadata.Length); + ivfc.Levels[1].DataLength = (Align(ivfc.Levels[2].DataLength, ivfc.Levels[2].BlockSize) / ivfc.Levels[2].BlockSize) * 0x20; //0x20 per SHA256 hash + ivfc.Levels[0].DataLength = (Align(ivfc.Levels[1].DataLength, ivfc.Levels[1].BlockSize) / ivfc.Levels[1].BlockSize) * 0x20; //0x20 per SHA256 hash + ulong MasterHashLen = (Align(ivfc.Levels[0].DataLength, ivfc.Levels[0].BlockSize) / ivfc.Levels[0].BlockSize) * 0x20; + ulong lofs = 0; + for (int i = 0; i < ivfc.Levels.Length; i++) + { + ivfc.Levels[i].HashOffset = lofs; + lofs += Align(ivfc.Levels[i].DataLength, ivfc.Levels[i].BlockSize); + } + uint IVFC_MAGIC = 0x43465649; //IVFC + uint RESERVED = 0x0; + uint HeaderLen = 0x5C; + uint MEDIA_UNIT_SIZE = 0x200; + byte[] SuperBlockHash = new byte[0x20]; + FileStream OutFileStream = new FileStream(TempFile, FileMode.Create, FileAccess.ReadWrite); + try + { + OutFileStream.Seek(0, SeekOrigin.Begin); + OutFileStream.Write(BitConverter.GetBytes(IVFC_MAGIC), 0, 0x4); + OutFileStream.Write(BitConverter.GetBytes(0x10000), 0, 0x4); + OutFileStream.Write(BitConverter.GetBytes(MasterHashLen), 0, 0x4); + for (int i = 0; i < ivfc.Levels.Length; i++) + { + OutFileStream.Write(BitConverter.GetBytes(ivfc.Levels[i].HashOffset), 0, 0x8); + OutFileStream.Write(BitConverter.GetBytes(ivfc.Levels[i].DataLength), 0, 0x8); + OutFileStream.Write(BitConverter.GetBytes((int)(Math.Log(ivfc.Levels[i].BlockSize, 2))), 0, 0x4); + OutFileStream.Write(BitConverter.GetBytes(RESERVED), 0, 0x4); + } + OutFileStream.Write(BitConverter.GetBytes(HeaderLen), 0, 0x4); + //IVFC Header is Written. + OutFileStream.Seek((long)Align(MasterHashLen + 0x60, ivfc.Levels[0].BlockSize), SeekOrigin.Begin); + byte[] metadataArray = metadata.ToArray(); + OutFileStream.Write(metadataArray, 0, metadataArray.Length); + long baseOfs = OutFileStream.Position; + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("Writing Level 2 Data..."))); + PB_Show.Invoke((Action)(() => + { + PB_Show.Minimum = 0; + PB_Show.Maximum = RomFiles.Length; + PB_Show.Value = 0; + PB_Show.Step = 1; + })); + for (int i = 0; i < RomFiles.Length; i++) + { + OutFileStream.Seek((long)(baseOfs + (long)RomFiles[i].Offset), SeekOrigin.Begin); + using (FileStream inStream = new FileStream(RomFiles[i].FullName, FileMode.Open, FileAccess.Read)) + { + while (inStream.Position < inStream.Length) + { + byte[] buffer = new byte[inStream.Length - inStream.Position > 0x100000 ? 0x100000 : inStream.Length - inStream.Position]; + inStream.Read(buffer, 0, buffer.Length); + OutFileStream.Write(buffer, 0, buffer.Length); + } + } + PB_Show.Invoke((Action)(() => PB_Show.PerformStep())); + } + long hashBaseOfs = (long)Align((ulong)OutFileStream.Position, ivfc.Levels[2].BlockSize); + long hOfs = (long)Align(MasterHashLen, ivfc.Levels[0].BlockSize); + long cOfs = hashBaseOfs + (long)ivfc.Levels[1].HashOffset; + SHA256Managed sha = new SHA256Managed(); + for (int i = ivfc.Levels.Length - 1; i >= 0; i--) + { + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("Computing Level " + i + " Hashes..."))); + byte[] buffer = new byte[(int)ivfc.Levels[i].BlockSize]; + PB_Show.Invoke((Action)(() => + { + PB_Show.Minimum = 0; + PB_Show.Maximum = (int)(ivfc.Levels[i].DataLength / ivfc.Levels[i].BlockSize); + PB_Show.Value = 0; + PB_Show.Step = 1; + })); + for (long ofs = 0; ofs < (long)ivfc.Levels[i].DataLength; ofs += ivfc.Levels[i].BlockSize) + { + OutFileStream.Seek(hOfs, SeekOrigin.Begin); + OutFileStream.Read(buffer, 0, (int)ivfc.Levels[i].BlockSize); + hOfs = OutFileStream.Position; + byte[] hash = sha.ComputeHash(buffer); + OutFileStream.Seek(cOfs, SeekOrigin.Begin); + OutFileStream.Write(hash, 0, hash.Length); + cOfs = OutFileStream.Position; + PB_Show.Invoke((Action)(() => PB_Show.PerformStep())); + } + if (i == 2) + { + long len = OutFileStream.Position; + if (len % 0x1000 != 0) + { + len = (long)Align((ulong)len, 0x1000); + byte[] buf = new byte[len - OutFileStream.Position]; + OutFileStream.Write(buf, 0, buf.Length); + } + } + if (i > 0) + { + hOfs = hashBaseOfs + (long)ivfc.Levels[i - 1].HashOffset; + if (i > 1) + { + cOfs = hashBaseOfs + (long)ivfc.Levels[i - 2].HashOffset; + } + else + { + cOfs = (long)Align(HeaderLen, PADDING_ALIGN); + } + } + } + OutFileStream.Seek(0, SeekOrigin.Begin); + uint SuperBlockLen = (uint)Align(MasterHashLen + 0x60, MEDIA_UNIT_SIZE); + byte[] MasterHashes = new byte[SuperBlockLen]; + OutFileStream.Read(MasterHashes, 0, (int)SuperBlockLen); + SuperBlockHash = sha.ComputeHash(MasterHashes); + } + finally + { + if (OutFileStream != null) + OutFileStream.Dispose(); + } + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("RomFS Super Block Hash: " + ByteArrayToString(SuperBlockHash)))); + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("Prompting to Save..."))); + SaveFileDialog sfd = new SaveFileDialog(); + TB_Progress.Invoke((Action)(() => + { + if (sfd.ShowDialog() == DialogResult.OK) + { + + TB_Progress.Invoke((Action)(() => UpdateTB_Progress("Writing Binary to " + sfd.FileName + "..."))); + Thread thread = new Thread(() => WriteBinary(TempFile, sfd.FileName)); + thread.IsBackground = true; + thread.Start(); + + } + })); + } + + public void WriteBinary(string tempFile, string outFile, RichTextBox TB_Progress = null, ProgressBar PB_Show = null) + { + using (FileStream fs = new FileStream(outFile, FileMode.Create)) + { + using (BinaryWriter writer = new BinaryWriter(fs)) + { + using (FileStream fileStream = new FileStream(tempFile, FileMode.Open, FileAccess.Read)) + { + uint BUFFER_SIZE = 0x100000; + PB_Show.Invoke((Action)(() => + { + PB_Show.Minimum = 0; + PB_Show.Maximum = (int)(fileStream.Length / BUFFER_SIZE); + PB_Show.Value = 0; + PB_Show.Step = 1; + })); + byte[] buffer = new byte[BUFFER_SIZE]; + while (true) + { + int count = fileStream.Read(buffer, 0, buffer.Length); + if (count != 0) + { + writer.Write(buffer, 0, count); + PB_Show.Invoke((Action)(() => PB_Show.PerformStep())); + } + else + break; + } + } + writer.Flush(); + } + } + File.Delete(TempFile); + TB_Progress.Invoke((Action)(() => + { + MessageBox.Show("Wrote RomFS to " + outFile + "."); + })); + } + + public static string ByteArrayToString(byte[] input) + { + StringBuilder sb = new StringBuilder(); + foreach (byte b in input) + { + sb.Append(b.ToString("X2") + " "); + } + return sb.ToString(); + } + + public void UpdateTB_Progress(string text, RichTextBox TB_Progress = null) + { + TB_Progress.Text += text + Environment.NewLine; + } + public void BuildRomFSHeader(MemoryStream romfs_stream, RomfsFile[] Entries, string DIR) + { + ROOT_DIR = DIR; + + Romfs_MetaData MetaData = new Romfs_MetaData(); + + InitializeMetaData(MetaData); + + CalcRomfsSize(MetaData); + + PopulateRomfs(MetaData, Entries); + + WriteMetaDataToStream(MetaData, romfs_stream); + } + private void InitializeMetaData(Romfs_MetaData MetaData) + { + MetaData.InfoHeader = new Romfs_InfoHeader(); + MetaData.DirTable = new Romfs_DirTable(); + MetaData.DirTableLen = 0; + MetaData.M_DirTableLen = 0; + MetaData.FileTable = new Romfs_FileTable(); + MetaData.FileTableLen = 0; + MetaData.DirTable.DirectoryTable = new List(); + MetaData.FileTable.FileTable = new List(); + MetaData.InfoHeader.HeaderLength = 0x28; + MetaData.InfoHeader.Sections = new Romfs_SectionHeader[4]; + MetaData.DirUTable = new List(); + MetaData.FileUTable = new List(); + } + private void CalcRomfsSize(Romfs_MetaData MetaData) + { + MetaData.DirNum = 1; + DirectoryInfo Root_DI = new DirectoryInfo(ROOT_DIR); + CalcDirSize(MetaData, Root_DI); + + MetaData.M_DirUTableEntry = 3; + if (MetaData.DirNum > 3) + { + MetaData.M_DirUTableEntry += (uint)Align((ulong)MetaData.DirNum - 3, 2); + } + + MetaData.M_FileUTableEntry = 3; + if (MetaData.FileNum > 3) + { + MetaData.M_FileUTableEntry += (uint)Align((ulong)MetaData.FileNum - 3, 2); + } + + + uint MetaDataSize = (uint)Align((ulong)(0x28 + MetaData.M_DirUTableEntry * 4 + MetaData.M_DirTableLen + MetaData.M_FileUTableEntry * 4 + MetaData.M_FileTableLen), PADDING_ALIGN); + for (int i = 0; i < MetaData.M_DirUTableEntry; i++) + { + MetaData.DirUTable.Add(ROMFS_UNUSED_ENTRY); + } + for (int i = 0; i < MetaData.M_FileUTableEntry; i++) + { + MetaData.FileUTable.Add(ROMFS_UNUSED_ENTRY); + } + uint Pos = MetaData.InfoHeader.HeaderLength; + for (int i = 0; i < 4; i++) + { + MetaData.InfoHeader.Sections[i].Offset = Pos; + uint size = 0; + switch (i) + { + case 0: + size = MetaData.M_DirUTableEntry * 4; + break; + case 1: + size = MetaData.M_DirTableLen; + break; + case 2: + size = MetaData.M_FileUTableEntry * 4; + break; + case 3: + size = MetaData.M_FileTableLen; + break; + } + MetaData.InfoHeader.Sections[i].Size = size; + Pos += size; + } + MetaData.InfoHeader.DataOffset = MetaDataSize; + } + private void CalcDirSize(Romfs_MetaData MetaData, DirectoryInfo dir) + { + if (MetaData.M_DirTableLen == 0) + { + MetaData.M_DirTableLen = 0x18; + } + else + { + MetaData.M_DirTableLen += 0x18 + (uint)Align((ulong)dir.Name.Length * 2, 4); + } + + FileInfo[] files = dir.GetFiles(); + for (int i = 0; i < files.Length; i++) + { + MetaData.M_FileTableLen += 0x20 + (uint)Align((ulong)files[i].Name.Length * 2, 4); + } + + DirectoryInfo[] SubDirectories = dir.GetDirectories(); + for (int i = 0; i < SubDirectories.Length; i++) + { + CalcDirSize(MetaData, SubDirectories[i]); + } + + MetaData.FileNum += (uint)files.Length; + MetaData.DirNum += (uint)SubDirectories.Length; + } + private void PopulateRomfs(Romfs_MetaData MetaData, RomfsFile[] Entries) + { + //Recursively Add All Directories to DirectoryTable + AddDir(MetaData, new DirectoryInfo(ROOT_DIR), 0, ROMFS_UNUSED_ENTRY); + + //Iteratively Add All Files to FileTable + AddFiles(MetaData, Entries); + + //Set Weird Offsets, Buld DirUTable+FileUTable + CalculateWeirdOffsets(MetaData); + + //Thats it. + } + private void CalculateWeirdOffsets(Romfs_MetaData MetaData) + { + for (int i = 0; i < MetaData.DirTable.DirectoryTable.Count; i++) + { + AddDirHashKey(MetaData, i); + } + for (int i = 0; i < MetaData.FileTable.FileTable.Count; i++) + { + AddFileHashKey(MetaData, i); + } + } + + private void AddDirHashKey(Romfs_MetaData MetaData, int index) + { + uint parent = MetaData.DirTable.DirectoryTable[index].ParentOffset; + string Name = MetaData.DirTable.DirectoryTable[index].Name; + byte[] NArr = (index == 0) ? Encoding.Unicode.GetBytes("") : Encoding.Unicode.GetBytes(Name); + uint hash = CalcPathHash(parent, NArr, 0, NArr.Length); + int ind2 = (int)(hash % MetaData.M_DirUTableEntry); + if (MetaData.DirUTable[ind2] == ROMFS_UNUSED_ENTRY) + { + MetaData.DirUTable[ind2] = MetaData.DirTable.DirectoryTable[index].Offset; + } + else + { + int i = GetRomfsDirEntry(MetaData, MetaData.DirUTable[ind2]); + while (true) + { + if (MetaData.DirTable.DirectoryTable[i].WeirdOffset == ROMFS_UNUSED_ENTRY) + { + MetaData.DirTable.DirectoryTable[i].WeirdOffset = MetaData.DirTable.DirectoryTable[index].Offset; + break; + } + else + { + i = GetRomfsDirEntry(MetaData, MetaData.DirTable.DirectoryTable[i].WeirdOffset); + } + } + } + } + private void AddFileHashKey(Romfs_MetaData MetaData, int index) + { + uint parent = MetaData.FileTable.FileTable[index].ParentDirOffset; + string Name = MetaData.FileTable.FileTable[index].Name; + byte[] NArr = Encoding.Unicode.GetBytes(Name); + uint hash = CalcPathHash(parent, NArr, 0, NArr.Length); + int ind2 = (int)(hash % MetaData.M_FileUTableEntry); + if (MetaData.FileUTable[ind2] == ROMFS_UNUSED_ENTRY) + { + MetaData.FileUTable[ind2] = MetaData.FileTable.FileTable[index].Offset; + } + else + { + int i = GetRomfsFileEntry(MetaData, MetaData.FileUTable[ind2]); + while (true) + { + if (MetaData.FileTable.FileTable[i].WeirdOffset == ROMFS_UNUSED_ENTRY) + { + MetaData.FileTable.FileTable[i].WeirdOffset = MetaData.FileTable.FileTable[index].Offset; + break; + } + else + { + i = GetRomfsFileEntry(MetaData, MetaData.FileTable.FileTable[i].WeirdOffset); + } + } + } + } + + private uint CalcPathHash(uint ParentOffset, byte[] NameArray, int start, int len) + { + uint hash = ParentOffset ^ 123456789; + for (int i = 0; i < NameArray.Length; i += 2) + { + hash = (uint)((hash >> 5) | (hash << 27)); + hash ^= (ushort)((NameArray[start + i]) | (NameArray[start + i + 1] << 8)); + } + return hash; + } + + private void AddDir(Romfs_MetaData MetaData, DirectoryInfo Dir, uint parent, uint sibling) + { + uint CurrentDir = MetaData.DirTableLen; + Romfs_DirEntry Entry = new Romfs_DirEntry(); + Entry.ParentOffset = parent; + Entry.ChildOffset = Entry.WeirdOffset = Entry.FileOffset = ROMFS_UNUSED_ENTRY; + Entry.SiblingOffset = sibling; + Entry.FullName = Dir.FullName; + Entry.Name = (Entry.FullName == ROOT_DIR) ? "" : Dir.Name; + Entry.Offset = CurrentDir; + MetaData.DirTable.DirectoryTable.Add(Entry); + MetaData.DirTableLen += (CurrentDir == 0) ? 0x18 : 0x18 + (uint)Align((ulong)Dir.Name.Length * 2, 4); + DirectoryInfo[] SubDirectories = Dir.GetDirectories(); + int ParentIndex = GetRomfsDirEntry(MetaData, Dir.FullName); + uint poff = MetaData.DirTable.DirectoryTable[ParentIndex].Offset; + if (SubDirectories.Length > 0) + { + MetaData.DirTable.DirectoryTable[ParentIndex].ChildOffset = MetaData.DirTableLen; + } + for (int i = 0; i < SubDirectories.Length; i++) + { + AddDir(MetaData, SubDirectories[i], Entry.Offset, sibling); + if (i > 0) + { + string PrevFullName = SubDirectories[i - 1].FullName; + string ThisName = SubDirectories[i].FullName; + int PrevIndex = GetRomfsDirEntry(MetaData, PrevFullName); + int ThisIndex = GetRomfsDirEntry(MetaData, ThisName); + MetaData.DirTable.DirectoryTable[PrevIndex].SiblingOffset = MetaData.DirTable.DirectoryTable[ThisIndex].Offset; + } + } + } + private void AddFiles(Romfs_MetaData MetaData, RomfsFile[] Entries) + { + string PrevDirPath = ""; + for (int i = 0; i < Entries.Length; i++) + { + FileInfo file = new FileInfo(Entries[i].FullName); + Romfs_FileEntry Entry = new Romfs_FileEntry(); + string DirPath = Path.GetDirectoryName(Entries[i].FullName); + int ParentIndex = GetRomfsDirEntry(MetaData, DirPath); + Entry.FullName = Entries[i].FullName; + Entry.Offset = MetaData.FileTableLen; + Entry.ParentDirOffset = MetaData.DirTable.DirectoryTable[ParentIndex].Offset; + Entry.SiblingOffset = ROMFS_UNUSED_ENTRY; + if (DirPath == PrevDirPath) + { + MetaData.FileTable.FileTable[i - 1].SiblingOffset = Entry.Offset; + } + if (MetaData.DirTable.DirectoryTable[ParentIndex].FileOffset == ROMFS_UNUSED_ENTRY) + { + MetaData.DirTable.DirectoryTable[ParentIndex].FileOffset = Entry.Offset; + } + Entry.WeirdOffset = ROMFS_UNUSED_ENTRY; + Entry.NameSize = (uint)file.Name.Length * 2; + Entry.Name = file.Name; + Entry.DataOffset = Entries[i].Offset; + Entry.DataSize = Entries[i].Size; + MetaData.FileTable.FileTable.Add(Entry); + MetaData.FileTableLen += 0x20 + (uint)Align((ulong)file.Name.Length * 2, 4); + PrevDirPath = DirPath; + } + } + + private void WriteMetaDataToStream(Romfs_MetaData MetaData, MemoryStream stream) + { + //First, InfoHeader. + stream.Write(BitConverter.GetBytes(MetaData.InfoHeader.HeaderLength), 0, 4); + foreach (Romfs_SectionHeader SH in MetaData.InfoHeader.Sections) + { + stream.Write(BitConverter.GetBytes(SH.Offset), 0, 4); + stream.Write(BitConverter.GetBytes(SH.Size), 0, 4); + } + stream.Write(BitConverter.GetBytes(MetaData.InfoHeader.DataOffset), 0, 4); + + //DirUTable + foreach (uint u in MetaData.DirUTable) + { + stream.Write(BitConverter.GetBytes(u), 0, 4); + } + + //DirTable + foreach (Romfs_DirEntry dir in MetaData.DirTable.DirectoryTable) + { + stream.Write(BitConverter.GetBytes(dir.ParentOffset), 0, 4); + stream.Write(BitConverter.GetBytes(dir.SiblingOffset), 0, 4); + stream.Write(BitConverter.GetBytes(dir.ChildOffset), 0, 4); + stream.Write(BitConverter.GetBytes(dir.FileOffset), 0, 4); + stream.Write(BitConverter.GetBytes(dir.WeirdOffset), 0, 4); + uint nlen = (uint)dir.Name.Length * 2; + stream.Write(BitConverter.GetBytes(nlen), 0, 4); + byte[] NameArray = new byte[(int)Align((ulong)nlen, 4)]; + Array.Copy(Encoding.Unicode.GetBytes(dir.Name), 0, NameArray, 0, nlen); + stream.Write(NameArray, 0, NameArray.Length); + } + + //FileUTable + foreach (uint u in MetaData.FileUTable) + { + stream.Write(BitConverter.GetBytes(u), 0, 4); + } + + //FileTable + foreach (Romfs_FileEntry file in MetaData.FileTable.FileTable) + { + stream.Write(BitConverter.GetBytes(file.ParentDirOffset), 0, 4); + stream.Write(BitConverter.GetBytes(file.SiblingOffset), 0, 4); + stream.Write(BitConverter.GetBytes(file.DataOffset), 0, 8); + stream.Write(BitConverter.GetBytes(file.DataSize), 0, 8); + stream.Write(BitConverter.GetBytes(file.WeirdOffset), 0, 4); + uint nlen = (uint)file.Name.Length * 2; + stream.Write(BitConverter.GetBytes(nlen), 0, 4); + byte[] NameArray = new byte[(int)Align((ulong)nlen, 4)]; + Array.Copy(Encoding.Unicode.GetBytes(file.Name), 0, NameArray, 0, nlen); + stream.Write(NameArray, 0, NameArray.Length); + } + //All Done. + } + + //GetRomfs[...]Entry Functions are all O(n) + + private int GetRomfsDirEntry(Romfs_MetaData MetaData, string FullName) + { + for (int i = 0; i < MetaData.DirTable.DirectoryTable.Count; i++) + { + if (MetaData.DirTable.DirectoryTable[i].FullName == FullName) + { + return i; + } + } + return -1; + } + private int GetRomfsDirEntry(Romfs_MetaData MetaData, uint Offset) + { + for (int i = 0; i < MetaData.DirTable.DirectoryTable.Count; i++) + { + if (MetaData.DirTable.DirectoryTable[i].Offset == Offset) + { + return i; + } + } + return -1; + } + private int GetRomfsFileEntry(Romfs_MetaData MetaData, uint Offset) + { + for (int i = 0; i < MetaData.FileTable.FileTable.Count; i++) + { + if (MetaData.FileTable.FileTable[i].Offset == Offset) + { + return i; + } + } + return -1; + } + + public class Romfs_MetaData + { + public Romfs_InfoHeader InfoHeader; + public uint DirNum; + public uint FileNum; + public List DirUTable; + public uint M_DirUTableEntry; + public Romfs_DirTable DirTable; + public uint DirTableLen; + public uint M_DirTableLen; + public List FileUTable; + public uint M_FileUTableEntry; + public Romfs_FileTable FileTable; + public uint FileTableLen; + public uint M_FileTableLen; + } + public struct Romfs_SectionHeader + { + public uint Offset; + public uint Size; + } + public struct Romfs_InfoHeader + { + public uint HeaderLength; + public Romfs_SectionHeader[] Sections; + public uint DataOffset; + } + public class Romfs_DirTable + { + public List DirectoryTable; + } + public class Romfs_FileTable + { + public List FileTable; + } + public class Romfs_DirEntry + { + public uint ParentOffset; + public uint SiblingOffset; + public uint ChildOffset; + public uint FileOffset; + public uint WeirdOffset; + public string Name; + public string FullName; + public uint Offset; + } + public class Romfs_FileEntry + { + public uint ParentDirOffset; + public uint SiblingOffset; + public ulong DataOffset; + public ulong DataSize; + public uint WeirdOffset; + public uint NameSize; + public string Name; + public string FullName; + public uint Offset; + } + + public class RomfsFile + { + public string PathName; + public ulong Offset; + public ulong Size; + public string FullName; + + public static ulong GetDataBlockLength(RomfsFile[] files, ulong PreData) + { + return (files.Length == 0) ? PreData : PreData + files[files.Length - 1].Offset + files[files.Length - 1].Size; + } + } + public class IVFCInfo + { + public IVFCLevel[] Levels; + } + public class IVFCLevel + { + public ulong HashOffset; + public ulong DataLength; + public uint BlockSize; + } + public class FileNameTable + { + public List NameEntryTable { get; private set; } + + public int NumFiles + { + get + { + return this.NameEntryTable.Count; + } + } + + internal FileNameTable(string rootPath) + { + this.NameEntryTable = new List(); + this.AddDirectory(new DirectoryInfo(rootPath)); + } + + internal void AddDirectory(DirectoryInfo dir) + { + foreach (FileInfo fileInfo in dir.GetFiles()) + { + this.NameEntryTable.Add(fileInfo); + } + foreach (DirectoryInfo subdir in dir.GetDirectories()) + { + this.AddDirectory(subdir); + } + } + } + public class LayoutManager + { + public static LayoutManager.Output[] Create(LayoutManager.Input[] Input) + { + List list = new List(); + ulong Len = 0; + foreach (LayoutManager.Input input in Input) + { + LayoutManager.Output output = new LayoutManager.Output(); + FileInfo fileInfo = new FileInfo(input.FilePath); + ulong ofs = Align(Len, input.AlignmentSize); + output.FilePath = input.FilePath; + output.Offset = ofs; + output.Size = (ulong)fileInfo.Length; + list.Add(output); + Len = ofs + (ulong)fileInfo.Length; + } + return list.ToArray(); + } + public static ulong Align(ulong input, ulong alignsize) + { + ulong output = input; + if (output % alignsize != 0) + { + output += (alignsize - (output % alignsize)); + } + return output; + } + public class Input + { + public string FilePath; + public uint AlignmentSize; + } + + public class Output + { + public string FilePath; + public ulong Offset; + public ulong Size; + } + } + } +} \ No newline at end of file