private void AddFiles(List <RomfsFile> Entries) { string PrevDirPath = ""; for (int i = 0; i < Entries.Count; i++) { var fileName = Path.GetFileName(Entries[i].FullName); Romfs_FileEntry Entry = new Romfs_FileEntry(); string DirPath = Path.GetDirectoryName(Entries[i].FullName); int ParentIndex = GetRomfsDirEntry(DirPath); Entry.FullName = Entries[i].FullName; Entry.Offset = this.FileTableLen; Entry.ParentDirOffset = this.DirTable[ParentIndex].Offset; Entry.SiblingOffset = ROMFS_UNUSED_ENTRY; if (DirPath == PrevDirPath) { this.FileTable[i - 1].SiblingOffset = Entry.Offset; } if (this.DirTable[ParentIndex].FileOffset == ROMFS_UNUSED_ENTRY) { this.DirTable[ParentIndex].FileOffset = Entry.Offset; } Entry.HashKeyPointer = ROMFS_UNUSED_ENTRY; Entry.NameSize = fileName.Length * 2; Entry.Name = fileName; Entry.DataOffset = Entries[i].Offset; Entry.DataSize = Entries[i].Size; this.FileTable.Add(Entry); this.FileTableLen += 0x20 + BitMath.Align(fileName.Length * 2, 4); PrevDirPath = DirPath; } }
public async Task Initalize() { var headerSize = await CiaData.ReadInt32Async(0); CiaHeader = new CiaHeader(await CiaData.ReadArrayAsync(0, headerSize)); var certOffset = BitMath.Align(headerSize, 64); var ticketOffset = BitMath.Align(certOffset + CiaHeader.CertificateChainSize, 64); var tmdOffset = BitMath.Align(ticketOffset + CiaHeader.TicketSize, 64); var contentOffset = BitMath.Align(tmdOffset + CiaHeader.TmdFileSize, 64); var metaOffset = BitMath.Align(contentOffset + CiaHeader.ContentSize, 64); TmdMetadata = await TmdMetadata.Load(CiaData.GetReadOnlyDataReference(tmdOffset, CiaHeader.TmdFileSize)); Partitions = new NcchPartition[TmdMetadata.ContentChunkRecords.Length]; long partitionStart = contentOffset; for (var i = 0; i < TmdMetadata.ContentChunkRecords.Length; i++) { var chunkRecord = TmdMetadata.ContentChunkRecords[i]; var partitionLength = chunkRecord.ContentSize; int contentIndex = chunkRecord.ContentIndex; Partitions[i] = await NcchPartition.Load(CiaData.GetReadOnlyDataReference(partitionStart, partitionLength)); partitionStart += partitionLength; } IsDlcContainer = TmdMetadata.TitleId >> 32 == 0x0004008C; }
private void CalcDirSize(string rootDirectory, IFileSystem fileSystem) { if (this.M_DirTableLen == 0) { this.M_DirTableLen = 0x18; } else { this.M_DirTableLen += 0x18 + BitMath.Align(Path.GetFileName(rootDirectory.TrimEnd('/')).Length * 2, 4); } var filePaths = fileSystem.GetFiles(rootDirectory, "*", true); foreach (var filePath in filePaths) { var filename = Path.GetFileName(filePath); this.M_FileTableLen += 0x20 + BitMath.Align(filename.Length * 2, 4); } var dirPaths = fileSystem.GetDirectories(rootDirectory, true); foreach (var dirPath in dirPaths) { CalcDirSize(dirPath, fileSystem); } this.FileNum += filePaths.Length; this.DirNum += dirPaths.Length; }
private void AddDir(string root, string directory, IFileSystem fileSystem, int parent, int sibling, bool DoSubs) { var dirName = Path.GetFileName(directory.TrimEnd('/')); var SubDirectories = fileSystem.GetDirectories(directory, true); if (!DoSubs) { int CurrentDir = this.DirTableLen; Romfs_DirEntry Entry = new Romfs_DirEntry { ParentOffset = parent, ChildOffset = ROMFS_UNUSED_ENTRY, HashKeyPointer = ROMFS_UNUSED_ENTRY, FileOffset = ROMFS_UNUSED_ENTRY, SiblingOffset = sibling, FullName = directory, Name = (directory == root) ? "" : dirName, Offset = CurrentDir }; this.DirTable.Add(Entry); this.DirTableLen += (CurrentDir == 0) ? 0x18 : 0x18 + BitMath.Align(dirName.Length * 2, 4); int ParentIndex = GetRomfsDirEntry(directory); int poff = this.DirTable[ParentIndex].Offset; } else { int CurIndex = GetRomfsDirEntry(directory); int CurrentDir = this.DirTable[CurIndex].Offset; for (int i = 0; i < SubDirectories.Length; i++) { AddDir(root, SubDirectories[i], fileSystem, CurrentDir, sibling, false); if (i > 0) { string PrevFullName = SubDirectories[i - 1]; string ThisName = SubDirectories[i]; int PrevIndex = GetRomfsDirEntry(PrevFullName); int ThisIndex = GetRomfsDirEntry(ThisName); this.DirTable[PrevIndex].SiblingOffset = this.DirTable[ThisIndex].Offset; } } for (int i = 0; i < SubDirectories.Length; i++) { AddDir(root, SubDirectories[i], fileSystem, CurrentDir, sibling, true); } } if (SubDirectories.Length > 0) { int curindex = GetRomfsDirEntry(directory); int childindex = GetRomfsDirEntry(SubDirectories[0]); if (curindex > -1 && childindex > -1) { this.DirTable[curindex].ChildOffset = this.DirTable[childindex].Offset; } } }
private void CalcRomfsSize(string rootDirectory, IFileSystem fileSystem) { this.DirNum = 1; CalcDirSize(rootDirectory, fileSystem); this.M_DirHashTableEntry = GetHashTableEntryCount(this.DirNum); this.M_FileHashTableEntry = GetHashTableEntryCount(this.FileNum); int MetaDataSize = BitMath.Align(0x28 + this.M_DirHashTableEntry * 4 + this.M_DirTableLen + this.M_FileHashTableEntry * 4 + this.M_FileTableLen, PADDING_ALIGN); for (int i = 0; i < this.M_DirHashTableEntry; i++) { this.DirHashTable.Add(ROMFS_UNUSED_ENTRY); } for (int i = 0; i < this.M_FileHashTableEntry; i++) { this.FileHashTable.Add(ROMFS_UNUSED_ENTRY); } int Pos = this.InfoHeader.HeaderLength; for (int i = 0; i < 4; i++) { this.InfoHeader.Sections[i].Offset = Pos; int size = 0; switch (i) { case 0: size = this.M_DirHashTableEntry * 4; break; case 1: size = this.M_DirTableLen; break; case 2: size = this.M_FileHashTableEntry * 4; break; case 3: size = this.M_FileTableLen; break; } this.InfoHeader.Sections[i].Size = size; Pos += size; } this.InfoHeader.DataOffset = MetaDataSize; }
public static List <RomfsFile> LoadFromFileSystem(string rootDirectory, IFileSystem fileSystem) { var list = new List <RomfsFile>(); long Len = 0; foreach (var filePath in fileSystem.GetFiles(rootDirectory, "*", false)) { Len = BitMath.Align(Len, 0x10); var output = new RomfsFile { FullName = filePath, PathName = filePath.Replace(Path.GetFullPath(rootDirectory), "").Replace("\\", "/"), Offset = Len, Size = fileSystem.GetFileLength(filePath) }; list.Add(output); Len += output.Size; } return(list); }
/// <param name="data">The raw data. Note: This will be disposed when RomFs is disposed</param> public RomFs(IReadOnlyBinaryDataAccessor data, RomFsHeader header) { Data = data ?? throw new ArgumentNullException(nameof(data)); Header = header ?? throw new ArgumentNullException(nameof(header)); LevelLocations = new IvfcLevelLocation[] { new IvfcLevelLocation { HashBlockSize = 1 << header.Level1BlockSize, HashOffset = 0x60 }, new IvfcLevelLocation { HashBlockSize = 1 << header.Level2BlockSize }, new IvfcLevelLocation { HashBlockSize = 1 << header.Level3BlockSize } }; BodyOffset = BitMath.Align(LevelLocations[0].HashOffset + header.MasterHashSize, LevelLocations[2].HashBlockSize); BodySize = header.Level3HashDataSize; LevelLocations[2].DataOffset = BodyOffset; LevelLocations[2].DataSize = BitMath.Align(BodySize, LevelLocations[2].HashBlockSize); LevelLocations[1].HashOffset = BitMath.Align(BodyOffset + BodySize, LevelLocations[2].HashBlockSize); LevelLocations[2].HashOffset = LevelLocations[1].HashOffset + header.Level2LogicalOffset - header.Level1LogicalOffset; LevelLocations[1].DataOffset = LevelLocations[2].HashOffset; LevelLocations[1].DataSize = BitMath.Align(header.Level2HashDataSize, LevelLocations[1].HashBlockSize); LevelLocations[0].DataOffset = LevelLocations[2].HashOffset; LevelLocations[0].DataSize = BitMath.Align(header.Level1HashDataSize, LevelLocations[0].HashBlockSize); // To-do: verify hashes }
private static void MakeRomFSData(Stream outputStream, IFileSystem fileSystem, List <RomfsFile> RomFiles, byte[] metadata, ProcessingProgressedToken?progressToken = null) { // Computing IVFC Header Data... var ivfcLevels = new IvfcLevelLocation[3]; for (int i = 0; i < ivfcLevels.Length; i++) { ivfcLevels[i] = new IvfcLevelLocation { HashBlockSize = 0x1000 }; } ivfcLevels[2].DataSize = RomfsFile.GetDataBlockLength(RomFiles, metadata.Length); ivfcLevels[1].DataSize = (BitMath.Align(ivfcLevels[2].DataSize, ivfcLevels[2].HashBlockSize) / ivfcLevels[2].HashBlockSize) * 0x20; //0x20 per SHA256 hash ivfcLevels[0].DataSize = (BitMath.Align(ivfcLevels[1].DataSize, ivfcLevels[1].HashBlockSize) / ivfcLevels[1].HashBlockSize) * 0x20; //0x20 per SHA256 hash long MasterHashLen = (BitMath.Align(ivfcLevels[0].DataSize, ivfcLevels[0].HashBlockSize) / ivfcLevels[0].HashBlockSize) * 0x20; long lofs = 0; for (int i = 0; i < ivfcLevels.Length; i++) { ivfcLevels[i].HashOffset = lofs; lofs += BitMath.Align(ivfcLevels[i].DataSize, ivfcLevels[i].HashBlockSize); } int IVFC_MAGIC = 0x43465649; //IVFC int RESERVED = 0x0; int HeaderLen = 0x5C; int MEDIA_UNIT_SIZE = 0x200; byte[] SuperBlockHash = new byte[0x20]; outputStream.Seek(0, SeekOrigin.Begin); outputStream.Write(BitConverter.GetBytes(IVFC_MAGIC), 0, 0x4); outputStream.Write(BitConverter.GetBytes(0x10000), 0, 0x4); outputStream.Write(BitConverter.GetBytes(MasterHashLen), 0, 0x4); for (int i = 0; i < ivfcLevels.Length; i++) { outputStream.Write(BitConverter.GetBytes(ivfcLevels[i].HashOffset), 0, 0x8); outputStream.Write(BitConverter.GetBytes(ivfcLevels[i].DataSize), 0, 0x8); outputStream.Write(BitConverter.GetBytes((int)(Math.Log(ivfcLevels[i].HashBlockSize, 2))), 0, 0x4); outputStream.Write(BitConverter.GetBytes(RESERVED), 0, 0x4); } outputStream.Write(BitConverter.GetBytes(HeaderLen), 0, 0x4); //IVFC Header is Written. outputStream.Seek(BitMath.Align(MasterHashLen + 0x60, ivfcLevels[0].HashBlockSize), SeekOrigin.Begin); outputStream.Write(metadata, 0, metadata.Length); long baseOfs = outputStream.Position; // Initialize progress token // The maximum will be the total file count and each ivfc block if (progressToken != null) { progressToken.ProcessedFileCount = 0; progressToken.TotalFileCount = RomFiles.Count + ivfcLevels.Select(l => (int)(l.DataSize / l.HashBlockSize)).Sum(); } // Writing Level 2 Data... for (int i = 0; i < RomFiles.Count; i++) { outputStream.Seek((long)(baseOfs + (long)RomFiles[i].Offset), SeekOrigin.Begin); using (var inStream = fileSystem.OpenFileReadOnly(RomFiles[i].FullName)) { 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); outputStream.Write(buffer, 0, buffer.Length); } } if (progressToken != null) { progressToken.ProcessedFileCount += 1; } } long hashBaseOfs = BitMath.Align(outputStream.Position, ivfcLevels[2].HashBlockSize); long hOfs = BitMath.Align(MasterHashLen, ivfcLevels[0].HashBlockSize); long cOfs = hashBaseOfs + ivfcLevels[1].HashOffset; SHA256Managed sha = new SHA256Managed(); for (int i = ivfcLevels.Length - 1; i >= 0; i--) { // Computing Level {i} Hashes... byte[] buffer = new byte[(int)ivfcLevels[i].HashBlockSize]; if (progressToken != null) { progressToken.ProcessedFileCount = 0; progressToken.TotalFileCount = (int)(ivfcLevels[i].DataSize / ivfcLevels[i].HashBlockSize); } for (long ofs = 0; ofs < (long)ivfcLevels[i].DataSize; ofs += ivfcLevels[i].HashBlockSize) { outputStream.Seek(hOfs, SeekOrigin.Begin); outputStream.Read(buffer, 0, (int)ivfcLevels[i].HashBlockSize); hOfs = outputStream.Position; byte[] hash = sha.ComputeHash(buffer); outputStream.Seek(cOfs, SeekOrigin.Begin); outputStream.Write(hash, 0, hash.Length); cOfs = outputStream.Position; if (progressToken != null) { progressToken.ProcessedFileCount += 1; } } if (i == 2) { long len = outputStream.Position; if (len % 0x1000 != 0) { len = BitMath.Align(len, 0x1000); byte[] buf = new byte[len - outputStream.Position]; outputStream.Write(buf, 0, buf.Length); } } if (i > 0) { hOfs = hashBaseOfs + (long)ivfcLevels[i - 1].HashOffset; if (i > 1) { cOfs = hashBaseOfs + (long)ivfcLevels[i - 2].HashOffset; } else { cOfs = BitMath.Align(HeaderLen, PADDING_ALIGN); } } } outputStream.Seek(0, SeekOrigin.Begin); var SuperBlockLen = BitMath.Align(MasterHashLen + 0x60, MEDIA_UNIT_SIZE); byte[] MasterHashes = new byte[SuperBlockLen]; outputStream.Read(MasterHashes, 0, (int)SuperBlockLen); SuperBlockHash = sha.ComputeHash(MasterHashes); }
private byte[] WriteMetaDataToStream() { using (var stream = new MemoryStream()) { //First, InfoHeader. stream.Write(BitConverter.GetBytes(this.InfoHeader.HeaderLength), 0, 4); foreach (Romfs_SectionHeader SH in this.InfoHeader.Sections) { stream.Write(BitConverter.GetBytes(SH.Offset), 0, 4); stream.Write(BitConverter.GetBytes(SH.Size), 0, 4); } stream.Write(BitConverter.GetBytes(this.InfoHeader.DataOffset), 0, 4); //DirHashTable foreach (uint u in this.DirHashTable) { stream.Write(BitConverter.GetBytes(u), 0, 4); } //DirTable foreach (Romfs_DirEntry dir in this.DirTable) { 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[BitMath.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 this.FileHashTable) { stream.Write(BitConverter.GetBytes(u), 0, 4); } //FileTable foreach (Romfs_FileEntry file in this.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[BitMath.Align(nlen, 4)]; Array.Copy(Encoding.Unicode.GetBytes(file.Name), 0, NameArray, 0, nlen); stream.Write(NameArray, 0, NameArray.Length); } //Padding while (stream.Position % PADDING_ALIGN != 0) { stream.Write(new byte[PADDING_ALIGN - (stream.Position % 0x10)], 0, (int)(PADDING_ALIGN - (stream.Position % 0x10))); } //All Done. return(stream.ToArray()); } }