Example #1
0
        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));
        }
Example #2
0
        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);
        }
Example #3
0
        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");
            }
        }
Example #4
0
        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);
            }
        }