public static void ExtractRomFS(string inFile, string outDirPath, Keyset keyset, Output Out) { using (var file = new FileStream(inFile, FileMode.Open, FileAccess.Read)) { var pfs = new PartitionFileSystem(file.AsStorage()); var OutDirFs = new LocalFileSystem(outDirPath); IDirectory sourceRoot = pfs.OpenDirectory("/", OpenDirectoryMode.All); IFileSystem sourceFs = sourceRoot.ParentFileSystem; Out.Log(pfs.Print()); foreach (var entry in FileIterator(sourceRoot)) { if (entry.Name.EndsWith(".nca")) { var fullOutDirPath = $"{outDirPath}/{entry.Name}"; Out.Log($"Extracting {entry.Name}...\r\n"); using (IFile srcFile = sourceFs.OpenFile(entry.Name, OpenMode.Read)) { ProcessNca.Extract(srcFile.AsStream(), fullOutDirPath, true, keyset, Out); } } else if (entry.Name.EndsWith(".nca.nsz")) { var fullOutDirPath = $"{outDirPath}/{entry.Name}"; Out.Log($"Extracting {entry.Name}...\r\n"); using (IFile srcFile = sourceFs.OpenFile(entry.Name, OpenMode.Read)) using (var decompressedFile = new DecompressionStorage(srcFile)) { ProcessNca.Extract(decompressedFile.AsStream(), fullOutDirPath, true, keyset, Out, true); // Header can't be patched for now due to OpenSection // and ValidateMasterHash needs to know if AesCtrEx // so Nca.cs was patched and now accepts isDecryptedNca // as constructor argument which disables decryption /* * var DecryptedHeader = new byte[0xC00]; * decompressedFile.AsStream().Read(DecryptedHeader, 0, 0xC00); * DecryptedHeader[1028] = (int)NcaEncryptionType.None; * DecryptedHeader[1540] = (int)NcaEncryptionType.None; * DecryptedHeader[2052] = (int)NcaEncryptionType.None; * DecryptedHeader[2564] = (int)NcaEncryptionType.None; * var HeaderKey1 = new byte[16]; * var HeaderKey2 = new byte[16]; * Buffer.BlockCopy(keyset.HeaderKey, 0, HeaderKey1, 0, 16); * Buffer.BlockCopy(keyset.HeaderKey, 16, HeaderKey2, 0, 16); * var headerEncrypted = CryptoInitialisers.AES_XTS(HeaderKey1, HeaderKey2, 0x200, DecryptedHeader, 0); * var ncaStorageList = new List<IStorage>() { new MemoryStorage(headerEncrypted), decompressedFile.Slice(0xC00) }; * var cleanDecryptedNca = new ConcatenationStorage(ncaStorageList, true); * ProcessNca.Extract(cleanDecryptedNca.AsStream(), fullOutDirPath, true, keyset, Out); */ } } } } }
private static void Process(string inputFilePath, string outDirPath, XciTaskType taskType, Keyset keyset, Output Out, bool verifyBeforeDecrypting = true) { using (var inputFile = File.Open(inputFilePath, FileMode.Open, FileAccess.Read).AsStorage()) using (var outputFile = File.Open($"{outDirPath}/xciMeta.dat", FileMode.Create)) { var OutDirFs = new LocalFileSystem(outDirPath); IDirectory destRoot = OutDirFs.OpenDirectory("/", OpenDirectoryMode.All); IFileSystem destFs = destRoot.ParentFileSystem; var header = new byte[] { 0x6e, 0x73, 0x5a, 0x69, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x58, 0x43, 0x49, 0x00 }; outputFile.Write(header, 0, header.Length); var xci = new Xci(keyset, inputFile); var xciHeaderData = new byte[0x200]; var xciCertData = new byte[0x200]; inputFile.Read(xciHeaderData, 0); inputFile.Read(xciCertData, 0x7000); outputFile.Write(xciHeaderData, 0, 0x200); outputFile.Write(xciCertData, 0, 0x200); Out.Log(Print.PrintXci(xci)); var root = xci.OpenPartition(XciPartitionType.Root); if (root == null) { throw new InvalidDataException("Could not find root partition"); } ProcessXci.GetTitleKeys(xci, keyset, Out); foreach (var sub in root.Files) { outputFile.WriteByte(0x0A); outputFile.WriteByte(0x0A); var subDirNameChar = Encoding.ASCII.GetBytes(sub.Name); outputFile.Write(subDirNameChar, 0, subDirNameChar.Length); var subPfs = new PartitionFileSystem(new FileStorage(root.OpenFile(sub, OpenMode.Read))); foreach (var subPfsFile in subPfs.Files) { outputFile.WriteByte(0x0A); var subPfsFileNameChar = Encoding.ASCII.GetBytes(subPfsFile.Name); outputFile.Write(subPfsFileNameChar, 0, subPfsFileNameChar.Length); using (IFile srcFile = subPfs.OpenFile(subPfsFile.Name, OpenMode.Read)) { if (taskType == XciTaskType.extractRomFS && subPfsFile.Name.EndsWith(".nca")) { var fullOutDirPath = $"{outDirPath}/{sub.Name}/{subPfsFile.Name}"; Out.Log($"Extracting {subPfsFile.Name}...\r\n"); ProcessNca.Extract(srcFile.AsStream(), fullOutDirPath, verifyBeforeDecrypting, keyset, Out); } else { var destFileName = Path.Combine(sub.Name, subPfsFile.Name); if (!destFs.DirectoryExists(sub.Name)) { destFs.CreateDirectory(sub.Name); } destFs.CreateFile(destFileName, subPfsFile.Size, CreateFileOptions.None); using (IFile dstFile = destFs.OpenFile(destFileName, OpenMode.Write)) { if (taskType == XciTaskType.decrypt && subPfsFile.Name.EndsWith(".nca")) { ProcessNca.Process(srcFile, dstFile, verifyBeforeDecrypting, keyset, Out); } else { srcFile.CopyTo(dstFile); } } } } } } outputFile.WriteByte(0x0A); outputFile.Dispose(); } }