/// <summary> /// Process the extended header, if it exists /// </summary> /// <param name="ncchHeader">NCCH header representing the partition</param> /// <param name="reader">BinaryReader representing the input stream</param> /// <param name="writer">BinaryWriter representing the output stream</param> private void ProcessExeFSFileEntries(NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); ExeFSHeader exefsHeader = ExeFSHeader.Read(reader); // If the header failed to read, log and return if (exefsHeader == null) { Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS header could not be read. Skipping..."); return; } foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders) { // Only decrypt a file if it's a code binary if (!fileHeader.IsCodeBinary) { continue; } uint datalenM = ((fileHeader.FileSize) / (1024 * 1024)); uint datalenB = ((fileHeader.FileSize) % (1024 * 1024)); uint ctroffset = ((fileHeader.FileOffset + mediaUnitSize) / 0x10); byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV, (int)ctroffset); var firstCipher = CreateAESCipher(ncchHeader.NormalKey, exefsIVWithOffsetForHeader, decryptArgs.Encrypt); var secondCipher = CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffsetForHeader, !decryptArgs.Encrypt); reader.BaseStream.Seek((((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); writer.BaseStream.Seek((((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); if (datalenM > 0) { for (int i = 0; i < datalenM; i++) { writer.Write(secondCipher.ProcessBytes(firstCipher.ProcessBytes(reader.ReadBytes(1024 * 1024)))); writer.Flush(); Console.Write($"\rPartition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.ReadableFileName}... {i} / {datalenM + 1} mb..."); } } if (datalenB > 0) { writer.Write(secondCipher.DoFinal(firstCipher.DoFinal(reader.ReadBytes((int)datalenB)))); writer.Flush(); } Console.Write($"\rPartition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.ReadableFileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n"); } }
static void Main(string[] args) { // string path = @"D:\Games\3DS\LEGO Star Wars III (USA).3ds"; string path = args[0]; // string path = @"D:\Games\3DS\Mario Kart 7 (USA).3ds"; FileStream fs = File.Open(path, FileMode.Open); NCSDHeader ncsdHeader = new NCSDHeader(IO.ReadData(fs, 512)); System.Console.WriteLine(ncsdHeader); int partition = 1; if (args.Length > 1) { partition = int.Parse(args[1]); } else { System.Console.Write("Partition: "); partition = int.Parse(System.Console.ReadLine()); } long offset = ncsdHeader.PartitionTable.partitions[partition].Offset; fs.Seek(offset, SeekOrigin.Begin); NCCHHeader ncchHeader = new NCCHHeader(IO.ReadData(fs, 512), offset); System.Console.WriteLine(ncchHeader); fs.Seek(ncchHeader.ExeFSOffset, SeekOrigin.Begin); ExeFSHeader exeFSHeader = new ExeFSHeader(IO.ReadData(fs, 512), ncchHeader.ExeFSOffset); System.Console.WriteLine($"\nExeFS Header\n{exeFSHeader}"); fs.Seek(ncchHeader.RomFSOffset, SeekOrigin.Begin); IVFCHeader ivfcHeader = new IVFCHeader(IO.ReadData(fs, 92), ncchHeader.RomFSOffset); System.Console.WriteLine($"\nIVFC Header (RomFS)\n{ivfcHeader}"); L3Partition l3Partition = new L3Partition(fs, ncchHeader.RomFSOffset + 0x1000); System.Console.WriteLine($"\nL3 Header (RomFS)\n{l3Partition.Header}"); // System.Console.WriteLine($"\nL3 Partition Directory Metadata Table (RomFS)\n{l3Partition.DirectoryMetadataTable}"); // System.Console.WriteLine($"\nL3 Partition Files Metadata Table (RomFS)\n{l3Partition.FileMetadataTable}"); System.Console.WriteLine($"\nL3 Partition Structure (RomFS)\n{l3Partition}"); }