public void UntrimAndEncrypt(Keyset keyset) { FolderTools.ExtractTitlekeys(decryptedDir, keyset, Out); var decryptedFs = new LocalFileSystem(decryptedDir); var encryptedFs = new LocalFileSystem(encryptedDir); EncryptNCA.Encrypt(decryptedFs, encryptedFs, VerifyHashes, keyset, Out); var dirDecrypted = new DirectoryInfo(decryptedDir); foreach (var file in decryptedFs.EnumerateEntries() .Where(item => item.Type == DirectoryEntryType.File && !item.Name.EndsWith(".tca"))) { if (!file.Name.EndsWith(".nca")) { using (var srcFile = decryptedFs.OpenFile(file.FullPath, OpenMode.Read)) using (var destFile = FolderTools.CreateAndOpen(file, encryptedFs, file.Name, file.Size)) { srcFile.CopyTo(destFile); } } decryptedFs.DeleteFile(file.FullPath); } UntrimDeltaNCA.Process(decryptedDir, encryptedFs, keyset, Out); EncryptNCA.Encrypt(decryptedFs, encryptedFs, VerifyHashes, keyset, Out); }
public static void ProcessFs(IFileSystem sourceFs, IFileSystem destFs, Output Out) { foreach (var file in sourceFs.EnumerateEntries().Where(item => item.Type == DirectoryEntryType.File)) { using (IFile srcFile = sourceFs.OpenFile(file.FullPath, OpenMode.Read)) using (var decStorage = new DecompressionStorage(srcFile)) { var destName = $"{file.Name.Substring(0, file.Name.LastIndexOf('.'))}"; using (IFile outputFile = FolderTools.CreateAndOpen(file, destFs, destName, decStorage.GetSize())) { decStorage.CopyTo(outputFile.AsStorage()); } } } }
public static void Encrypt(IFileSystem sourceFs, IFileSystem destFs, bool verifyEncrypted, Keyset keyset, Output Out) { foreach (var decryptedNcaEntry in sourceFs.EnumerateEntries().Where(item => item.Name.EndsWith(".nca"))) { Out.Log($"Input: {decryptedNcaEntry.Name}\r\n"); using (var decryptedNca = sourceFs.OpenFile(decryptedNcaEntry.FullPath, OpenMode.Read)) { if (destFs != null) { Out.Log("Opened NCA for writing...\r\n"); using (IFile outputFile = FolderTools.CreateAndOpen(decryptedNcaEntry, destFs, decryptedNcaEntry.Name, decryptedNca.GetSize())) { EncryptFunct(decryptedNca.AsStream(), outputFile.AsStream(), decryptedNcaEntry.Name, verifyEncrypted, keyset, Out); } } else { EncryptFunct(decryptedNca.AsStream(), null, decryptedNcaEntry.Name, verifyEncrypted, keyset, Out); } } } }
private void CompressFunct() { var CompressionIO = new byte[104857600]; var blocksPerChunk = CompressionIO.Length / bs + (CompressionIO.Length % bs > 0 ? 1 : 0); var sourceFs = new LocalFileSystem(inFolderPath); var destFs = new LocalFileSystem(outFolderPath); foreach (var file in sourceFs.EnumerateEntries().Where(item => item.Type == DirectoryEntryType.File)) { Out.Log($"{file.FullPath}\r\n"); var outFileName = $"{file.Name}.nsz"; using (var outputFileBase = FolderTools.CreateAndOpen(file, destFs, outFileName)) using (var outputFile = new FilePositionStorage(outputFileBase)) using (var inputFileBase = sourceFs.OpenFile(file.FullPath, OpenMode.Read)) using (var inputFile = new FilePositionStorage(inputFileBase)) { amountOfBlocks = (int)Math.Ceiling((decimal)inputFile.GetSize() / bs); sizeOfSize = (int)Math.Ceiling(Math.Log(bs, 2) / 8); var perBlockHeaderSize = sizeOfSize + 1; var headerSize = 0x15 + perBlockHeaderSize * amountOfBlocks; outputFile.Seek(headerSize); var nsZipMagic = new byte[] { 0x6e, 0x73, 0x5a, 0x69, 0x70 }; var nsZipMagicRandomKey = new byte[5]; secureRNG.GetBytes(nsZipMagicRandomKey); Util.XorArrays(nsZipMagic, nsZipMagicRandomKey); var chunkIndex = 0; nsZipHeader = new byte[headerSize]; Array.Copy(nsZipMagic, 0x00, nsZipHeader, 0x00, 0x05); Array.Copy(nsZipMagicRandomKey, 0x00, nsZipHeader, 0x05, 0x05); nsZipHeader[0x0A] = 0x00; //Version nsZipHeader[0x0B] = 0x01; //Type nsZipHeader[0x0C] = (byte)(bs >> 32); nsZipHeader[0x0D] = (byte)(bs >> 24); nsZipHeader[0x0E] = (byte)(bs >> 16); nsZipHeader[0x0F] = (byte)(bs >> 8); nsZipHeader[0x10] = (byte)bs; nsZipHeader[0x11] = (byte)(amountOfBlocks >> 24); nsZipHeader[0x12] = (byte)(amountOfBlocks >> 16); nsZipHeader[0x13] = (byte)(amountOfBlocks >> 8); nsZipHeader[0x14] = (byte)amountOfBlocks; sha256Compressed = new SHA256Cng(); long maxPos = inputFile.GetSize(); int blocksLeft; int blocksInThisChunk; do { var outputLen = new int[blocksPerChunk]; //Filled with 0 inputFile.Read(CompressionIO); blocksLeft = amountOfBlocks - chunkIndex * blocksPerChunk; blocksInThisChunk = Math.Min(blocksPerChunk, blocksLeft); var opt = new ParallelOptions() { MaxDegreeOfParallelism = this.MaxDegreeOfParallelism }; //for(int index = 0; index < blocksInThisChunk; ++index) Parallel.For(0, blocksInThisChunk, opt, index => { var currentBlockID = chunkIndex * blocksPerChunk + index; var startPosRelative = index * bs; //Don't directly cast bytesLeft to int or sectors over 2 GB will overflow into negative size long startPos = (long)currentBlockID * (long)bs; long bytesLeft = maxPos - startPos; var blockSize = bs < bytesLeft ? bs : (int)bytesLeft; Out.Print($"Block: {currentBlockID + 1}/{amountOfBlocks} ({opt.MaxDegreeOfParallelism})\r\n"); CompressionAlgorithm compressionAlgorithm; outputLen[index] = CompressBlock(ref CompressionIO, startPosRelative, blockSize, out compressionAlgorithm); //Out.Log($"inputLen[{currentBlockID}]: {blockSize}\r\n"); //Out.Log($"outputLen[{currentBlockID}]: {outputLen[index]} bytesLeft={bytesLeft}\r\n"); var offset = currentBlockID * (sizeOfSize + 1); switch (compressionAlgorithm) { case CompressionAlgorithm.None: nsZipHeader[0x15 + offset] = 0x00; break; case CompressionAlgorithm.Zstandard: nsZipHeader[0x15 + offset] = 0x01; break; case CompressionAlgorithm.LZMA: nsZipHeader[0x15 + offset] = 0x02; break; default: throw new ArgumentOutOfRangeException(); } for (var j = 0; j < sizeOfSize; ++j) { nsZipHeader[0x16 + offset + j] = (byte)(outputLen[index] >> ((sizeOfSize - j - 1) * 8)); } }); for (int index = 0; index < blocksInThisChunk; ++index) { var startPos = index * bs; sha256Compressed.TransformBlock(CompressionIO, startPos, outputLen[index], null, 0); var dataToWrite = CompressionIO.AsSpan().Slice(startPos, outputLen[index]); outputFile.Write(dataToWrite); } ++chunkIndex; } while (blocksLeft - blocksInThisChunk > 0); outputFile.Write(nsZipHeader, 0); sha256Header = new SHA256Cng(); sha256Header.ComputeHash(nsZipHeader); var sha256Hash = new byte[0x20]; Array.Copy(sha256Header.Hash, sha256Hash, 0x20); sha256Compressed.TransformFinalBlock(new byte[0], 0, 0); Util.XorArrays(sha256Hash, sha256Compressed.Hash); //Console.WriteLine(sha256Header.Hash.ToHexString()); //Console.WriteLine(sha256Compressed.Hash.ToHexString()); outputFile.Seek(0, SeekOrigin.End); outputFile.Write(sha256Hash.AsSpan().Slice(0, 0x10)); } } }