internal static void WriteMetaDataToStream(RomfsMetaData metaData, MemoryStream stream) { //First, InfoHeader. stream.Write(BitConverter.GetBytes(metaData.InfoHeader.HeaderLength), 0, 4); foreach (RomfsSectionHeader 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); //DirHashTable foreach (uint u in metaData.DirHashTable) { stream.Write(BitConverter.GetBytes(u), 0, 4); } //DirTable foreach (RomfsDirEntry 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.HashKeyPointer), 0, 4); uint nlen = (uint)dir.Name.Length * 2; stream.Write(BitConverter.GetBytes(nlen), 0, 4); byte[] nameArray = new byte[(int)RomFS.Align(nlen, 4)]; Array.Copy(Encoding.Unicode.GetBytes(dir.Name), 0, nameArray, 0, nlen); stream.Write(nameArray, 0, nameArray.Length); } //FileHashTable foreach (uint u in metaData.FileHashTable) { stream.Write(BitConverter.GetBytes(u), 0, 4); } //FileTable foreach (RomfsFileEntry 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.HashKeyPointer), 0, 4); uint nlen = (uint)file.Name.Length * 2; stream.Write(BitConverter.GetBytes(nlen), 0, 4); byte[] nameArray = new byte[(int)RomFS.Align(nlen, 4)]; Array.Copy(Encoding.Unicode.GetBytes(file.Name), 0, nameArray, 0, nlen); stream.Write(nameArray, 0, nameArray.Length); } //Padding while (stream.Position % PaddingAlign != 0) { stream.Write(new byte[PaddingAlign - stream.Position % 0x10], 0, (int)(PaddingAlign - stream.Position % 0x10)); } //All Done. }
internal static void BuildRomFS(string infile, string outfile) { OutFile = outfile; RootDir = infile; if (File.Exists(TempFile)) { File.Delete(TempFile); } FileNameTable fnt = new FileNameTable(RootDir); RomfsFile[] romFiles = new RomfsFile[fnt.NumFiles]; LayoutManager.Input[] In = new LayoutManager.Input[fnt.NumFiles]; for (int i = 0; i < fnt.NumFiles; i++) { In[i] = new LayoutManager.Input { FilePath = fnt.NameEntryTable[i].FullName, AlignmentSize = 0x10 }; } LayoutManager.Output[] Out = LayoutManager.Create(In); for (int i = 0; i < Out.Length; i++) { romFiles[i] = new RomfsFile { Offset = Out[i].Offset, PathName = Out[i].FilePath.Replace(Path.GetFullPath(RootDir), "").Replace("\\", "/"), FullName = Out[i].FilePath, Size = Out[i].Size }; } using (MemoryStream memoryStream = new MemoryStream()) { RomFS.BuildRomFSHeader(memoryStream, romFiles, RootDir); RomFS.MakeRomFSData(romFiles, memoryStream); } }
internal static void AddFiles(RomfsMetaData metaData, RomfsFile[] entries) { string prevDirPath = ""; for (int i = 0; i < entries.Length; i++) { FileInfo file = new FileInfo(entries[i].FullName); RomfsFileEntry entry = new RomfsFileEntry(); string dirPath = Path.GetDirectoryName(entries[i].FullName); int parentIndex = RomFS.GetRomfsDirEntry(metaData, dirPath); entry.FullName = entries[i].FullName; entry.Offset = metaData.FileTableLen; entry.ParentDirOffset = metaData.DirTable.DirectoryTable[parentIndex].Offset; entry.SiblingOffset = RomfsUnusedEntry; if (dirPath == prevDirPath) { metaData.FileTable.FileTable[i - 1].SiblingOffset = entry.Offset; } if (metaData.DirTable.DirectoryTable[parentIndex].FileOffset == RomfsUnusedEntry) { metaData.DirTable.DirectoryTable[parentIndex].FileOffset = entry.Offset; } entry.HashKeyPointer = RomfsUnusedEntry; 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)RomFS.Align((ulong)file.Name.Length * 2, 4); prevDirPath = dirPath; } }
internal static void CalcDirSize(RomfsMetaData metaData, DirectoryInfo dir) { if (metaData.MDirTableLen == 0) { metaData.MDirTableLen = 0x18; } else { metaData.MDirTableLen += 0x18 + (uint)RomFS.Align((ulong)dir.Name.Length * 2, 4); } FileInfo[] files = dir.GetFiles(); foreach (FileInfo t in files) { metaData.MFileTableLen += 0x20 + (uint)RomFS.Align((ulong)t.Name.Length * 2, 4); } DirectoryInfo[] subDirectories = dir.GetDirectories(); foreach (DirectoryInfo t in subDirectories) { RomFS.CalcDirSize(metaData, t); } metaData.FileNum += (uint)files.Length; metaData.DirNum += (uint)subDirectories.Length; }
internal static void AddFileHashKey(RomfsMetaData 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 = RomFS.CalcPathHash(parent, nArr, 0); int ind2 = (int)(hash % metaData.MFileHashTableEntry); if (metaData.FileHashTable[ind2] == RomfsUnusedEntry) { metaData.FileHashTable[ind2] = metaData.FileTable.FileTable[index].Offset; } else { int i = RomFS.GetRomfsFileEntry(metaData, metaData.FileHashTable[ind2]); int tempindex = index; metaData.FileHashTable[ind2] = metaData.FileTable.FileTable[index].Offset; while (true) { if (metaData.FileTable.FileTable[tempindex].HashKeyPointer == RomfsUnusedEntry) { metaData.FileTable.FileTable[tempindex].HashKeyPointer = metaData.FileTable.FileTable[i].Offset; break; } i = tempindex; tempindex = RomFS.GetRomfsFileEntry(metaData, metaData.FileTable.FileTable[i].HashKeyPointer); } } }
// Main wrapper that assembles the ROM based on the following specifications: internal static bool BuildROM(bool card2, string logoName, string exefsPath, string romfsPath, string exheaderPath, string serialText, string savePath) { // Sanity check the input files. if (! ((File.Exists(exefsPath) || Directory.Exists(exefsPath)) && (File.Exists(romfsPath) || Directory.Exists(romfsPath)) && File.Exists(exheaderPath))) { return(false); } // If ExeFS and RomFS are not built, build. if (!File.Exists(exefsPath) && Directory.Exists(exefsPath)) { ExeFS.Set(Directory.GetFiles(exefsPath), exefsPath = "exefs.bin"); } if (!File.Exists(romfsPath) && Directory.Exists(romfsPath)) { RomFS.BuildRomFS(romfsPath, romfsPath = "romfs.bin"); } Ncch ncch = Ctr.SetNcch(exefsPath, romfsPath, exheaderPath, serialText, logoName); Ncsd ncsd = Ctr.SetNcsd(ncch, card2); bool success = Ctr.WriteROM(ncsd, savePath); return(success); }
internal static void BuildRomFSHeader(MemoryStream romfsStream, RomfsFile[] entries, string dir) { RootDir = dir; RomfsMetaData metaData = new RomfsMetaData(); RomFS.InitializeMetaData(metaData); RomFS.CalcRomfsSize(metaData); RomFS.PopulateRomfs(metaData, entries); RomFS.WriteMetaDataToStream(metaData, romfsStream); }
internal static void PopulateHashTables(RomfsMetaData metaData) { for (int i = 0; i < metaData.DirTable.DirectoryTable.Count; i++) { RomFS.AddDirHashKey(metaData, i); } for (int i = 0; i < metaData.FileTable.FileTable.Count; i++) { RomFS.AddFileHashKey(metaData, i); } }
internal static void PopulateRomfs(RomfsMetaData metaData, RomfsFile[] entries) { //Recursively Add All Directories to DirectoryTable RomFS.AddDir(metaData, new DirectoryInfo(RootDir), 0, RomfsUnusedEntry); //Iteratively Add All Files to FileTable RomFS.AddFiles(metaData, entries); //Set Weird Offsets, Build HashKeyPointers, Build HashTables RomFS.PopulateHashTables(metaData); //Thats it. }
internal static void CalcRomfsSize(RomfsMetaData metaData) { metaData.DirNum = 1; DirectoryInfo rootDi = new DirectoryInfo(RootDir); RomFS.CalcDirSize(metaData, rootDi); metaData.MDirHashTableEntry = RomFS.GetHashTableEntryCount(metaData.DirNum); metaData.MFileHashTableEntry = RomFS.GetHashTableEntryCount(metaData.FileNum); uint metaDataSize = (uint)RomFS.Align(0x28 + metaData.MDirHashTableEntry * 4 + metaData.MDirTableLen + metaData.MFileHashTableEntry * 4 + metaData.MFileTableLen, PaddingAlign); for (int i = 0; i < metaData.MDirHashTableEntry; i++) { metaData.DirHashTable.Add(RomfsUnusedEntry); } for (int i = 0; i < metaData.MFileHashTableEntry; i++) { metaData.FileHashTable.Add(RomfsUnusedEntry); } 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.MDirHashTableEntry * 4; break; case 1: size = metaData.MDirTableLen; break; case 2: size = metaData.MFileHashTableEntry * 4; break; case 3: size = metaData.MFileTableLen; break; } metaData.InfoHeader.Sections[i].Size = size; pos += size; } metaData.InfoHeader.DataOffset = metaDataSize; }
internal static void MakeRomFSData(RomfsFile[] romFiles, MemoryStream metadata) { IvfcInfo ivfc = new IvfcInfo { Levels = new IvfcLevel[3] }; for (int i = 0; i < ivfc.Levels.Length; i++) { ivfc.Levels[i] = new IvfcLevel { BlockSize = 0x1000 }; } ivfc.Levels[2].DataLength = RomfsFile.GetDataBlockLength(romFiles, (ulong)metadata.Length); ivfc.Levels[1].DataLength = RomFS.Align(ivfc.Levels[2].DataLength, ivfc.Levels[2].BlockSize) / ivfc.Levels[2].BlockSize * 0x20; //0x20 per SHA256 hash ivfc.Levels[0].DataLength = RomFS.Align(ivfc.Levels[1].DataLength, ivfc.Levels[1].BlockSize) / ivfc.Levels[1].BlockSize * 0x20; //0x20 per SHA256 hash ulong masterHashLen = RomFS.Align(ivfc.Levels[0].DataLength, ivfc.Levels[0].BlockSize) / ivfc.Levels[0].BlockSize * 0x20; ulong lofs = 0; foreach (IvfcLevel t in ivfc.Levels) { t.HashOffset = lofs; lofs += RomFS.Align(t.DataLength, t.BlockSize); } const uint ivfcMagic = 0x43465649; //IVFC const uint reserved = 0x0; const uint headerLen = 0x5C; const uint mediaUnitSize = 0x200; FileStream outFileStream = new FileStream(TempFile, FileMode.Create, FileAccess.ReadWrite); try { outFileStream.Seek(0, SeekOrigin.Begin); outFileStream.Write(BitConverter.GetBytes(ivfcMagic), 0, 0x4); outFileStream.Write(BitConverter.GetBytes(0x10000), 0, 0x4); outFileStream.Write(BitConverter.GetBytes(masterHashLen), 0, 0x4); foreach (IvfcLevel t in ivfc.Levels) { outFileStream.Write(BitConverter.GetBytes(t.HashOffset), 0, 0x8); outFileStream.Write(BitConverter.GetBytes(t.DataLength), 0, 0x8); outFileStream.Write(BitConverter.GetBytes((int)Math.Log(t.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)RomFS.Align(masterHashLen + 0x60, ivfc.Levels[0].BlockSize), SeekOrigin.Begin); byte[] metadataArray = metadata.ToArray(); outFileStream.Write(metadataArray, 0, metadataArray.Length); long baseOfs = outFileStream.Position; foreach (RomfsFile t in romFiles) { outFileStream.Seek(baseOfs + (long)t.Offset, SeekOrigin.Begin); using (FileStream inStream = new FileStream(t.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); } } } long hashBaseOfs = (long)RomFS.Align((ulong)outFileStream.Position, ivfc.Levels[2].BlockSize); long hOfs = (long)RomFS.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--) { byte[] buffer = new byte[(int)ivfc.Levels[i].BlockSize]; 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; } if (i <= 0) { continue; } if (i == 2) { long len = outFileStream.Position; if (len % 0x1000 != 0) { len = (long)RomFS.Align((ulong)len, 0x1000); byte[] buf = new byte[len - outFileStream.Position]; outFileStream.Write(buf, 0, buf.Length); } } hOfs = hashBaseOfs + (long)ivfc.Levels[i - 1].HashOffset; if (i > 1) { cOfs = hashBaseOfs + (long)ivfc.Levels[i - 2].HashOffset; } else { cOfs = (long)RomFS.Align(headerLen, PaddingAlign); } } outFileStream.Seek(0, SeekOrigin.Begin); uint superBlockLen = (uint)RomFS.Align(masterHashLen + 0x60, mediaUnitSize); byte[] masterHashes = new byte[superBlockLen]; outFileStream.Read(masterHashes, 0, (int)superBlockLen); sha.ComputeHash(masterHashes); } finally { outFileStream.Dispose(); } if (OutFile == TempFile) { return; } if (File.Exists(OutFile)) { File.Delete(OutFile); } File.Move(TempFile, OutFile); }
internal static void AddDir(RomfsMetaData metaData, DirectoryInfo dir, uint parent, uint sibling, bool doSubs) { DirectoryInfo[] subDirectories = dir.GetDirectories(); if (!doSubs) { uint currentDir = metaData.DirTableLen; RomfsDirEntry entry = new RomfsDirEntry { ParentOffset = parent }; entry.ChildOffset = entry.HashKeyPointer = entry.FileOffset = RomfsUnusedEntry; entry.SiblingOffset = sibling; entry.FullName = dir.FullName; entry.Name = entry.FullName == RootDir ? "" : dir.Name; entry.Offset = currentDir; metaData.DirTable.DirectoryTable.Add(entry); metaData.DirTableLen += currentDir == 0 ? 0x18 : 0x18 + (uint)RomFS.Align((ulong)dir.Name.Length * 2, 4); // int ParentIndex = GetRomfsDirEntry(MetaData, Dir.FullName); // uint poff = MetaData.DirTable.DirectoryTable[ParentIndex].Offset; } else { int curIndex = RomFS.GetRomfsDirEntry(metaData, dir.FullName); uint currentDir = metaData.DirTable.DirectoryTable[curIndex].Offset; for (int i = 0; i < subDirectories.Length; i++) { RomFS.AddDir(metaData, subDirectories[i], currentDir, sibling, false); if (i <= 0) { continue; } string prevFullName = subDirectories[i - 1].FullName; string thisName = subDirectories[i].FullName; int prevIndex = RomFS.GetRomfsDirEntry(metaData, prevFullName); int thisIndex = RomFS.GetRomfsDirEntry(metaData, thisName); metaData.DirTable.DirectoryTable[prevIndex].SiblingOffset = metaData.DirTable.DirectoryTable[thisIndex].Offset; } foreach (DirectoryInfo t in subDirectories) { RomFS.AddDir(metaData, t, currentDir, sibling, true); } } if (subDirectories.Length <= 0) { return; } int curindex = RomFS.GetRomfsDirEntry(metaData, dir.FullName); int childindex = RomFS.GetRomfsDirEntry(metaData, subDirectories[0].FullName); if (curindex > -1 && childindex > -1) { metaData.DirTable.DirectoryTable[curindex].ChildOffset = metaData.DirTable.DirectoryTable[childindex].Offset; } }
internal static void AddDir(RomfsMetaData metaData, DirectoryInfo dir, uint parent, uint sibling) { RomFS.AddDir(metaData, dir, parent, sibling, false); RomFS.AddDir(metaData, dir, parent, sibling, true); }