public static IStorage ProcessXci(IStorage xciStorage, bool compress) { Xci xci = new Xci(KeySet, xciStorage); IStorage xciHeaderStorage = new MemoryStorage(new byte[0x200]); xciStorage.CopyTo(xciHeaderStorage); XciPartition securePartition = xci.OpenPartition(XciPartitionType.Secure); IStorage processedSecurePartition = ProcessPartitionFileSystem(securePartition, PartitionFileSystemType.Hashed, compress); PartitionFileSystemBuilder rootPartitionBuilder = new PartitionFileSystemBuilder(); rootPartitionBuilder.AddFile("secure", processedSecurePartition.AsFile(OpenMode.Read)); IStorage rootPartitionStorage = rootPartitionBuilder.Build(PartitionFileSystemType.Hashed); using (BinaryReader reader = new BinaryReader(rootPartitionStorage.AsStream())) { PartitionFileSystemHeader pfsHeader = new PartitionFileSystemHeader(reader); Span <byte> pfsHeaderBytes = stackalloc byte[pfsHeader.HeaderSize]; Span <byte> pfsHeaderHash = stackalloc byte[0x20]; rootPartitionStorage.Read(0, pfsHeaderBytes); Sha256.GenerateSha256Hash(pfsHeaderBytes, pfsHeaderHash); xciHeaderStorage.Write(0x138, BitConverter.GetBytes(pfsHeader.HeaderSize)); xciHeaderStorage.Write(0x140, pfsHeaderHash); } return(new ConcatenationStorage(new[] { xciHeaderStorage, new MemoryStorage(new byte[xci.Header.RootPartitionOffset - 0x200]), // Cert and padding rootPartitionStorage }, false)); }
public static CnmtExtended GetCnmtExtended(string folderPath, Keyset keyset, Output Out) { var dirDecrypted = new DirectoryInfo(folderPath); foreach (var inFile in dirDecrypted.GetFiles("*.cnmt.nca")) { Out.Log($"{inFile}\r\n"); var ncaStorage = new StreamStorage(new FileStream(inFile.FullName, FileMode.Open, FileAccess.Read), false); var DecryptedHeader = new byte[0xC00]; ncaStorage.Read(DecryptedHeader, 0, 0xC00, 0); var Header = new NcaHeader(new BinaryReader(new MemoryStream(DecryptedHeader)), keyset); for (var i = 0; i < 4; ++i) { var section = NcaParseSection.ParseSection(Header, i); if (section == null || section.Header.Type != SectionType.Pfs0) { continue; } IStorage sectionStorage = ncaStorage.Slice(section.Offset, section.Size, false); IStorage pfs0Storage = sectionStorage.Slice(section.Header.Sha256Info.DataOffset, section.Header.Sha256Info.DataSize, false); var Pfs0Header = new PartitionFileSystemHeader(new BinaryReader(pfs0Storage.AsStream())); var FileDict = Pfs0Header.Files.ToDictionary(x => x.Name, x => x); foreach (var file in FileDict) { if (file.Key.EndsWith(".cnmt")) { IStorage fileStorage = pfs0Storage.Slice(Pfs0Header.HeaderSize + file.Value.Offset, file.Value.Size, false); var metadata = new Cnmt(fileStorage.AsStream()); if (metadata.ExtendedData != null) { ncaStorage.Dispose(); return(metadata.ExtendedData); } } } } ncaStorage.Dispose(); } return(null); }
public static void Process(string folderPath, Keyset keyset, Output Out) { var cnmtExtended = CnmtNca.GetCnmtExtended(folderPath, keyset, Out); if (cnmtExtended == null) { Out.Log( "[Info] Skiped fragemt trimming as no patch Cnmt was found\r\n"); return; } if (cnmtExtended.FragmentSets.Length == 0) { Out.Log( "[Info] Skiped fragemt trimming as no DeltaApplyInfos in the patch Cnmt were found\r\n"); return; } var DeltaContentID = 0; foreach (var deltaApplyInfo in cnmtExtended.FragmentSets) { long offset = 0; for (var deltaApplyInfoId = 0; deltaApplyInfoId < deltaApplyInfo.FragmentCount; ++deltaApplyInfoId) { var matching = $"{Utils.BytesToString(deltaApplyInfo.NcaIdNew).ToLower()}.nca"; var length = new FileInfo(Path.Combine(folderPath, matching)).Length; var hexLen = Math.Ceiling(Math.Log(length, 16.0)); if (deltaApplyInfo.FragmentCount > 1) { matching += $":{offset.ToString($"X{hexLen}")}:{0.ToString($"X{hexLen}")}"; } var lowerNcaID = Utils.BytesToString(cnmtExtended.DeltaContents[DeltaContentID].NcaId) .ToLower(); var ncaFileName = Path.Combine(folderPath, $"{lowerNcaID}.nca"); if (!File.Exists(ncaFileName)) { Out.Warn($"[WARN] File: {ncaFileName} not found!\r\n"); break; } Out.Log($"{ncaFileName}\r\n"); var ncaStorage = new StreamStorage(new FileStream(ncaFileName, FileMode.Open, FileAccess.Read), false); var DecryptedHeader = new byte[0xC00]; ncaStorage.Read(DecryptedHeader, 0, 0xC00, 0); var Header = new NcaHeader(new BinaryReader(new MemoryStream(DecryptedHeader)), keyset); var fragmentTrimmed = false; for (var sector = 0; sector < 4; ++sector) { var section = NcaParseSection.ParseSection(Header, sector); if (section == null || section.Header.Type != SectionType.Pfs0) { continue; } IStorage sectionStorage = ncaStorage.Slice(section.Offset, section.Size, false); IStorage pfs0Storage = sectionStorage.Slice(section.Header.Sha256Info.DataOffset, section.Header.Sha256Info.DataSize, false); var Pfs0Header = new PartitionFileSystemHeader(new BinaryReader(pfs0Storage.AsStream())); var FileDict = Pfs0Header.Files.ToDictionary(x => x.Name, x => x); var path = PathTools.Normalize("fragment").TrimStart('/'); if (FileDict.TryGetValue(path, out var fragmentFile)) { if (Pfs0Header.NumFiles != 1) { throw new InvalidDataException( "A fragment Pfs0 container should contain exactly 1 file"); } if (fragmentTrimmed) { throw new InvalidDataException( "Multiple fragments in NCA found!"); } IStorage fragmentStorage = pfs0Storage.Slice( Pfs0Header.HeaderSize + fragmentFile.Offset, fragmentFile.Size, false); var buffer = new byte[0xC00]; fragmentStorage.Read(buffer, 0, buffer.Length, 0); var writerPath = Path.Combine(folderPath, $"{lowerNcaID}.tca"); var writer = File.Open(writerPath, FileMode.Create); var offsetBefore = section.Offset + section.Header.Sha256Info.DataOffset + Pfs0Header.HeaderSize + fragmentFile.Offset; var offsetAfter = offsetBefore + fragmentFile.Size; IStorage ncaStorageBeforeFragment = ncaStorage.Slice(0, offsetBefore, false); IStorage ncaStorageAfterFragment = ncaStorage.Slice(offsetAfter, ncaStorage.GetSize() - offsetAfter, false); ncaStorageBeforeFragment.CopyToStream(writer); offset += SaveDeltaHeader.Save(fragmentStorage, writer, matching); ncaStorageAfterFragment.CopyToStream(writer); writer.Position = 0x200; writer.WriteByte(0x54); writer.Dispose(); fragmentTrimmed = true; } } ncaStorage.Dispose(); File.Delete(ncaFileName); ++DeltaContentID; } Out.Log("----------\r\n"); } }
public static void Process(string folderPath, IFileSystem newBaseFolderFs, Keyset keyset, Output Out) { var dirDecrypted = new DirectoryInfo(folderPath); foreach (var inFile in dirDecrypted.GetFiles("*.tca")) { Out.Log($"{inFile}\r\n"); var ncaStorage = new StreamStorage(new FileStream(inFile.FullName, FileMode.Open, FileAccess.Read), false); var DecryptedHeader = new byte[0xC00]; ncaStorage.Read(DecryptedHeader, 0, 0xC00, 0); var Header = new NcaHeader(new BinaryReader(new MemoryStream(DecryptedHeader)), keyset); var fragmentTrimmed = false; for (var i = 0; i < 4; ++i) { var section = NcaParseSection.ParseSection(Header, i); if (section == null || section.Header.Type != SectionType.Pfs0) { continue; } if (fragmentTrimmed) { Out.Warn( "Multiple fragments in NCA found! Skip trimming this fragment.\r\n"); continue; } IStorage sectionStorage = ncaStorage.Slice(section.Offset, section.Size, false); IStorage pfs0Storage = sectionStorage.Slice(section.Header.Sha256Info.DataOffset, section.Header.Sha256Info.DataSize, false); var Pfs0Header = new PartitionFileSystemHeader(new BinaryReader(pfs0Storage.AsStream())); var FileDict = Pfs0Header.Files.ToDictionary(x => x.Name, x => x); var path = PathTools.Normalize(FragmentFileName).TrimStart('/'); if (Pfs0Header.NumFiles == 1 && FileDict.TryGetValue(path, out var fragmentFile)) { var inFileNameNoExtension = Path.GetFileNameWithoutExtension(inFile.Name); var writer = File.Open($"{folderPath}/{inFileNameNoExtension}.nca", FileMode.Create); var offsetBefore = section.Offset + section.Header.Sha256Info.DataOffset + Pfs0Header.HeaderSize + fragmentFile.Offset; IStorage ncaStorageBeforeFragment = ncaStorage.Slice(0, offsetBefore, false); IStorage fragmentStorageOverflow = ncaStorage.Slice(offsetBefore, ncaStorage.GetSize() - offsetBefore, false); ncaStorageBeforeFragment.CopyToStream(writer); var TDV0len = RecreateDelta.Recreate(fragmentStorageOverflow, writer, newBaseFolderFs); var offsetAfter = offsetBefore + TDV0len; IStorage fragmentStorageAfter = ncaStorage.Slice(offsetAfter, ncaStorage.GetSize() - offsetAfter, false); fragmentStorageAfter.CopyToStream(writer); writer.Position = 0x200; writer.WriteByte(0x4E); writer.Dispose(); fragmentTrimmed = true; } } ncaStorage.Dispose(); File.Delete(inFile.FullName); } }