예제 #1
0
        private static void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId)
        {
            Nca controlNca = null;

            // Add keys to key set if needed
            foreach (DirectoryEntryEx ticketEntry in pfs.EnumerateEntries("/", "*.tik"))
            {
                Result result = pfs.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);

                if (result.IsSuccess())
                {
                    Ticket ticket = new Ticket(ticketFile.AsStream());

                    _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet)));
                }
            }

            // Find the Control NCA and store it in variable called controlNca
            foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
            {
                pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();

                Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());

                if (nca.Header.ContentType == NcaContentType.Control)
                {
                    controlNca = nca;
                }
            }

            // Return the ControlFS
            controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
            titleId   = controlNca?.Header.TitleId.ToString("x16");
        }
예제 #2
0
        private static void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId)
        {
            Nca controlNca = null;

            // Add keys to key set if needed
            _virtualFileSystem.ImportTickets(pfs);

            // Find the Control NCA and store it in variable called controlNca
            foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
            {
                pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();

                Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());

                if (nca.Header.ContentType == NcaContentType.Control)
                {
                    controlNca = nca;
                }
            }

            // Return the ControlFS
            controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
            titleId   = controlNca?.Header.TitleId.ToString("x16");
        }
예제 #3
0
        public AvatarWindow(ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar")
        {
            // TODO: Handle the dynamic avatar background. For now we will just uses a white one.

            CanFocus  = false;
            Resizable = false;
            Modal     = true;
            TypeHint  = Gdk.WindowTypeHint.Dialog;

            SetDefaultSize(620, 400);
            SetPosition(WindowPosition.Center);

            VBox vbox = new VBox(false, 0);

            Add(vbox);

            ScrolledWindow scrolledWindow = new ScrolledWindow
            {
                ShadowType = ShadowType.EtchedIn
            };

            scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic);

            HBox hbox = new HBox(false, 0);

            Button chooseButton = new Button()
            {
                Label           = "Choose",
                CanFocus        = true,
                ReceivesDefault = true
            };

            chooseButton.Clicked += ChooseButton_Pressed;

            Button closeButton = new Button()
            {
                Label    = "Close",
                CanFocus = true
            };

            closeButton.Clicked += CloseButton_Pressed;

            vbox.PackStart(scrolledWindow, true, true, 0);
            hbox.PackStart(chooseButton, true, true, 0);
            hbox.PackStart(closeButton, true, true, 0);
            vbox.PackStart(hbox, false, false, 0);

            _listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf));
            _listStore.SetSortColumnId(0, SortType.Ascending);

            string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.NandSystem, NcaContentType.Data);
            string avatarPath  = virtualFileSystem.SwitchPathToSystemPath(contentPath);

            if (!string.IsNullOrWhiteSpace(avatarPath))
            {
                using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open))
                {
                    Nca         nca   = new Nca(virtualFileSystem.KeySet, ncaFileStream);
                    IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);

                    foreach (var item in romfs.EnumerateEntries())
                    {
                        // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.

                        if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs"))
                        {
                            romfs.OpenFile(out IFile file, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();

                            using (MemoryStream stream = new MemoryStream())
                                using (MemoryStream streamJpg = new MemoryStream())
                                {
                                    file.AsStream().CopyTo(stream);

                                    stream.Position = 0;

                                    Image avatarImage = Image.LoadPixelData <Rgba32>(DecompressYaz0(stream), 256, 256);

                                    avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(255, 255, 255, 255)));
                                    avatarImage.SaveAsJpeg(streamJpg);

                                    avatarDict.Add(item.FullPath, streamJpg.ToArray());

                                    _listStore.AppendValues(item.FullPath, new Gdk.Pixbuf(streamJpg.ToArray(), 96, 96));
                                }
                        }
                    }
                }
            }

            _iconView              = new IconView(_listStore);
            _iconView.ItemWidth    = 64;
            _iconView.ItemPadding  = 10;
            _iconView.PixbufColumn = 1;

            _iconView.SelectionChanged += IconView_SelectionChanged;

            _iconView.SelectPath(new TreePath(new int[] { 0 }));

            scrolledWindow.Add(_iconView);

            _iconView.GrabFocus();

            ShowAll();
        }
예제 #4
0
        public void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
        {
            if (mainNca.Header.ContentType != NcaContentType.Program)
            {
                Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");

                return;
            }

            IStorage    dataStorage = null;
            IFileSystem codeFs      = null;

            if (patchNca == null)
            {
                if (mainNca.CanOpenSection(NcaSectionType.Data))
                {
                    dataStorage = mainNca.OpenStorage(NcaSectionType.Data, FsIntegrityCheckLevel);
                }

                if (mainNca.CanOpenSection(NcaSectionType.Code))
                {
                    codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, FsIntegrityCheckLevel);
                }
            }
            else
            {
                if (patchNca.CanOpenSection(NcaSectionType.Data))
                {
                    dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, FsIntegrityCheckLevel);
                }

                if (patchNca.CanOpenSection(NcaSectionType.Code))
                {
                    codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, FsIntegrityCheckLevel);
                }
            }

            if (codeFs == null)
            {
                Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");

                return;
            }

            if (dataStorage == null)
            {
                Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
            }
            else
            {
                Device.FileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read));
            }

            LoadExeFs(codeFs, out Npdm metaData);

            TitleId = metaData.Aci0.TitleId;

            if (controlNca != null)
            {
                ReadControlData(controlNca);
            }
            else
            {
                ControlData.ByteSpan.Clear();
            }

            if (TitleId != 0)
            {
                EnsureSaveData(new TitleId(TitleId));
            }
        }
예제 #5
0
        public static void Process(Context ctx)
        {
            using (IStorage file = new LocalStorage(ctx.Options.InFile, FileAccess.Read))
            {
                var nca     = new Nca(ctx.Keyset, file);
                Nca baseNca = null;

                var ncaHolder = new NcaHolder {
                    Nca = nca
                };

                if (ctx.Options.HeaderOut != null)
                {
                    using (var outHeader = new FileStream(ctx.Options.HeaderOut, FileMode.Create, FileAccess.ReadWrite))
                    {
                        nca.OpenDecryptedHeaderStorage().Slice(0, 0xc00).CopyToStream(outHeader);
                    }
                }

                if (ctx.Options.BaseNca != null)
                {
                    IStorage baseFile = new LocalStorage(ctx.Options.BaseNca, FileAccess.Read);
                    baseNca = new Nca(ctx.Keyset, baseFile);
                }

                for (int i = 0; i < 3; i++)
                {
                    if (ctx.Options.SectionOut[i] != null)
                    {
                        OpenStorage(i).WriteAllBytes(ctx.Options.SectionOut[i], ctx.Logger);
                    }

                    if (ctx.Options.SectionOutDir[i] != null)
                    {
                        FileSystemManager fs = ctx.Horizon.Fs;

                        string mountName = $"section{i}";

                        fs.Register(mountName, OpenFileSystem(i));
                        fs.Register("output", new LocalFileSystem(ctx.Options.SectionOutDir[i]));

                        FsUtils.CopyDirectoryWithProgress(fs, mountName + ":/", "output:/", logger: ctx.Logger);

                        fs.Unmount(mountName);
                        fs.Unmount("output");
                    }

                    if (ctx.Options.Validate && nca.SectionExists(i))
                    {
                        if (nca.Header.GetFsHeader(i).IsPatchSection() && baseNca != null)
                        {
                            ncaHolder.Validities[i] = baseNca.VerifySection(nca, i, ctx.Logger);
                        }
                        else
                        {
                            ncaHolder.Validities[i] = nca.VerifySection(i, ctx.Logger);
                        }
                    }
                }

                if (ctx.Options.ListRomFs && nca.CanOpenSection(NcaSectionType.Data))
                {
                    IFileSystem romfs = OpenFileSystemByType(NcaSectionType.Data);

                    foreach (DirectoryEntry entry in romfs.EnumerateEntries())
                    {
                        ctx.Logger.LogMessage(entry.FullPath);
                    }
                }

                if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ReadBench)
                {
                    if (!nca.SectionExists(NcaSectionType.Data))
                    {
                        ctx.Logger.LogMessage("NCA has no RomFS section");
                        return;
                    }

                    if (ctx.Options.RomfsOut != null)
                    {
                        OpenStorageByType(NcaSectionType.Data).WriteAllBytes(ctx.Options.RomfsOut, ctx.Logger);
                    }

                    if (ctx.Options.RomfsOutDir != null)
                    {
                        FileSystemManager fs = ctx.Horizon.Fs;

                        fs.Register("rom", OpenFileSystemByType(NcaSectionType.Data));
                        fs.Register("output", new LocalFileSystem(ctx.Options.RomfsOutDir));

                        FsUtils.CopyDirectoryWithProgress(fs, "rom:/", "output:/", logger: ctx.Logger);

                        fs.Unmount("rom");
                        fs.Unmount("output");
                    }

                    if (ctx.Options.ReadBench)
                    {
                        long     bytesToRead = 1024L * 1024 * 1024 * 5;
                        IStorage storage     = OpenStorageByType(NcaSectionType.Data);
                        var      dest        = new NullStorage(storage.GetSize());

                        int iterations = (int)(bytesToRead / storage.GetSize()) + 1;
                        ctx.Logger.LogMessage(iterations.ToString());

                        ctx.Logger.StartNewStopWatch();

                        for (int i = 0; i < iterations; i++)
                        {
                            storage.CopyTo(dest, ctx.Logger);
                            ctx.Logger.LogMessage(ctx.Logger.GetRateString());
                        }

                        ctx.Logger.PauseStopWatch();
                        ctx.Logger.LogMessage(ctx.Logger.GetRateString());
                    }
                }

                if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null)
                {
                    if (nca.Header.ContentType != ContentType.Program)
                    {
                        ctx.Logger.LogMessage("NCA's content type is not \"Program\"");
                        return;
                    }

                    if (!nca.SectionExists(NcaSectionType.Code))
                    {
                        ctx.Logger.LogMessage("Could not find an ExeFS section");
                        return;
                    }

                    if (ctx.Options.ExefsOut != null)
                    {
                        OpenStorageByType(NcaSectionType.Code).WriteAllBytes(ctx.Options.ExefsOut, ctx.Logger);
                    }

                    if (ctx.Options.ExefsOutDir != null)
                    {
                        FileSystemManager fs = ctx.Horizon.Fs;

                        fs.Register("code", OpenFileSystemByType(NcaSectionType.Code));
                        fs.Register("output", new LocalFileSystem(ctx.Options.ExefsOutDir));

                        FsUtils.CopyDirectoryWithProgress(fs, "code:/", "output:/", logger: ctx.Logger);

                        fs.Unmount("code");
                        fs.Unmount("output");
                    }
                }

                if (ctx.Options.PlaintextOut != null)
                {
                    nca.OpenDecryptedNca().WriteAllBytes(ctx.Options.PlaintextOut, ctx.Logger);
                }

                if (!ctx.Options.ReadBench)
                {
                    ctx.Logger.LogMessage(ncaHolder.Print());
                }

                IStorage OpenStorage(int index)
                {
                    if (ctx.Options.Raw)
                    {
                        if (baseNca != null)
                        {
                            return(baseNca.OpenRawStorageWithPatch(nca, index));
                        }

                        return(nca.OpenRawStorage(index));
                    }

                    if (baseNca != null)
                    {
                        return(baseNca.OpenStorageWithPatch(nca, index, ctx.Options.IntegrityLevel));
                    }

                    return(nca.OpenStorage(index, ctx.Options.IntegrityLevel));
                }

                IFileSystem OpenFileSystem(int index)
                {
                    if (baseNca != null)
                    {
                        return(baseNca.OpenFileSystemWithPatch(nca, index, ctx.Options.IntegrityLevel));
                    }

                    return(nca.OpenFileSystem(index, ctx.Options.IntegrityLevel));
                }

                IStorage OpenStorageByType(NcaSectionType type)
                {
                    return(OpenStorage(Nca.GetSectionIndexFromType(type, nca.Header.ContentType)));
                }

                IFileSystem OpenFileSystemByType(NcaSectionType type)
                {
                    return(OpenFileSystem(Nca.GetSectionIndexFromType(type, nca.Header.ContentType)));
                }
            }
        }
예제 #6
0
        public void EnsureInitialized(ContentManager contentManager)
        {
            if (_fontData == null)
            {
                _storage.ZeroFill();

                uint fontOffset = 0;

                FontInfo CreateFont(string name)
                {
                    if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename))
                    {
                        string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, NcaContentType.Data);
                        string fontPath    = _device.FileSystem.SwitchPathToSystemPath(contentPath);

                        if (!string.IsNullOrWhiteSpace(fontPath))
                        {
                            byte[] data;

                            using (IStorage ncaFileStream = new LocalStorage(fontPath, FileAccess.Read, FileMode.Open))
                            {
                                Nca         nca   = new Nca(_device.System.KeySet, ncaFileStream);
                                IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);

                                using var fontFile = new UniqueRef <IFile>();

                                romfs.OpenFile(ref fontFile.Ref(), ("/" + fontFilename).ToU8Span(), OpenMode.Read).ThrowIfFailure();

                                data = DecryptFont(fontFile.Get.AsStream());
                            }

                            FontInfo info = new FontInfo((int)fontOffset, data.Length);

                            WriteMagicAndSize(fontOffset, data.Length);

                            fontOffset += 8;

                            uint start = fontOffset;

                            for (; fontOffset - start < data.Length; fontOffset++)
                            {
                                _storage.GetRef <byte>(fontOffset) = data[fontOffset - start];
                            }

                            return(info);
                        }
                        else
                        {
                            if (!contentManager.TryGetSystemTitlesName(fontTitle, out string titleName))
                            {
                                titleName = "Unknown";
                            }

                            throw new InvalidSystemResourceException($"{titleName} ({fontTitle:x8}) system title not found! This font will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)");
                        }
                    }
                    else
                    {
                        throw new ArgumentException($"Unknown font \"{name}\"!");
                    }
                }

                _fontData = new Dictionary <SharedFontType, FontInfo>
                {
                    { SharedFontType.JapanUsEurope, CreateFont("FontStandard") },
                    { SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") },
                    { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") },
                    { SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") },
                    { SharedFontType.Korean, CreateFont("FontKorean") },
                    { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
                };

                if (fontOffset > Horizon.FontSize)
                {
                    throw new InvalidSystemResourceException("The sum of all fonts size exceed the shared memory size. " +
                                                             $"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. (actual size: {fontOffset} bytes).");
                }
            }
        }
예제 #7
0
        public void EnsureInitialized(ContentManager contentManager, bool ignoreMissingFonts)
        {
            if (_fontData == null)
            {
                _device.Memory.FillWithZeros(_physicalAddress, Horizon.FontSize);

                uint fontOffset = 0;

                FontInfo CreateFont(string name)
                {
                    if (contentManager.TryGetFontTitle(name, out long fontTitle) &&
                        contentManager.TryGetFontFilename(name, out string fontFilename))
                    {
                        string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, NcaContentType.Data);
                        string fontPath    = _device.FileSystem.SwitchPathToSystemPath(contentPath);

                        if (!string.IsNullOrWhiteSpace(fontPath))
                        {
                            byte[] data;

                            using (IStorage ncaFileStream = new LocalStorage(fontPath, FileAccess.Read, FileMode.Open))
                            {
                                Nca         nca   = new Nca(_device.System.KeySet, ncaFileStream);
                                IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);

                                romfs.OpenFile(out IFile fontFile, "/" + fontFilename, OpenMode.Read).ThrowIfFailure();

                                data = DecryptFont(fontFile.AsStream());
                            }

                            FontInfo info = new FontInfo((int)fontOffset, data.Length);

                            WriteMagicAndSize(_physicalAddress + fontOffset, data.Length);

                            fontOffset += 8;

                            uint start = fontOffset;

                            for (; fontOffset - start < data.Length; fontOffset++)
                            {
                                _device.Memory.WriteByte(_physicalAddress + fontOffset, data[fontOffset - start]);
                            }

                            return(info);
                        }
                    }

                    string fontFilePath = Path.Combine(_fontsPath, name + ".ttf");

                    if (File.Exists(fontFilePath))
                    {
                        byte[] data = File.ReadAllBytes(fontFilePath);

                        FontInfo info = new FontInfo((int)fontOffset, data.Length);

                        WriteMagicAndSize(_physicalAddress + fontOffset, data.Length);

                        fontOffset += 8;

                        uint start = fontOffset;

                        for (; fontOffset - start < data.Length; fontOffset++)
                        {
                            _device.Memory.WriteByte(_physicalAddress + fontOffset, data[fontOffset - start]);
                        }

                        return(info);
                    }
                    else if (!ignoreMissingFonts)
                    {
                        throw new InvalidSystemResourceException($"Font \"{name}.ttf\" not found. Please provide it in \"{_fontsPath}\".");
                    }

                    return(new FontInfo());
                }

                _fontData = new Dictionary <SharedFontType, FontInfo>
                {
                    { SharedFontType.JapanUsEurope, CreateFont("FontStandard") },
                    { SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") },
                    { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") },
                    { SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") },
                    { SharedFontType.Korean, CreateFont("FontKorean") },
                    { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
                };

                if (fontOffset > Horizon.FontSize && !ignoreMissingFonts)
                {
                    throw new InvalidSystemResourceException(
                              $"The sum of all fonts size exceed the shared memory size. " +
                              $"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. " +
                              $"(actual size: {fontOffset} bytes).");
                }
            }
        }
예제 #8
0
        public void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
        {
            if (mainNca.Header.ContentType != ContentType.Program)
            {
                Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");

                return;
            }

            IStorage    dataStorage = null;
            IFileSystem codeFs      = null;

            if (patchNca == null)
            {
                if (mainNca.CanOpenSection(NcaSectionType.Data))
                {
                    dataStorage = mainNca.OpenStorage(NcaSectionType.Data, FsIntegrityCheckLevel);
                }

                if (mainNca.CanOpenSection(NcaSectionType.Code))
                {
                    codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, FsIntegrityCheckLevel);
                }
            }
            else
            {
                if (patchNca.CanOpenSection(NcaSectionType.Data))
                {
                    dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, FsIntegrityCheckLevel);
                }

                if (patchNca.CanOpenSection(NcaSectionType.Code))
                {
                    codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, FsIntegrityCheckLevel);
                }
            }

            if (codeFs == null)
            {
                Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");

                return;
            }

            if (dataStorage == null)
            {
                Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
            }
            else
            {
                Device.FileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read));
            }

            LoadExeFs(codeFs, out Npdm metaData);

            Nacp ReadControlData()
            {
                IFileSystem controlRomfs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel);

                IFile controlFile = controlRomfs.OpenFile("/control.nacp", OpenMode.Read);

                Nacp controlData = new Nacp(controlFile.AsStream());

                TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
                TitleID   = metaData.Aci0.TitleId.ToString("x16");

                if (string.IsNullOrWhiteSpace(CurrentTitle))
                {
                    TitleName = CurrentTitle = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
                }

                return(controlData);
            }

            if (controlNca != null)
            {
                ReadControlData();
            }
            else
            {
                TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
            }
        }
예제 #9
0
        private static string Print(this NcaHolder ncaHolder)
        {
            Nca nca       = ncaHolder.Nca;
            int masterKey = GetMasterKeyRevisionFromKeyGeneration(nca.Header.KeyGeneration);

            int colLen = 36;
            var sb     = new StringBuilder();

            sb.AppendLine();

            sb.AppendLine("NCA:");
            PrintItem(sb, colLen, "Magic:", MagicToString(nca.Header.Magic));
            PrintItem(sb, colLen, $"Fixed-Key Signature{nca.VerifyHeaderSignature().GetValidityString()}:", nca.Header.Signature1.ToArray());
            PrintItem(sb, colLen, $"NPDM Signature{nca.VerifySignature2().GetValidityString()}:", nca.Header.Signature2.ToArray());
            PrintItem(sb, colLen, "Content Size:", $"0x{nca.Header.NcaSize:x12}");
            PrintItem(sb, colLen, "TitleID:", $"{nca.Header.TitleId:X16}");
            if (nca.CanOpenSection(NcaSectionType.Code))
            {
                IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.None);
                Result      r  = fs.OpenFile(out IFile file, "/main.npdm".ToU8String(), OpenMode.Read);
                if (r.IsSuccess())
                {
                    var npdm = new NpdmBinary(file.AsStream(), null);
                    PrintItem(sb, colLen, "Title Name:", npdm.TitleName);
                }
            }

            PrintItem(sb, colLen, "SDK Version:", nca.Header.SdkVersion);
            PrintItem(sb, colLen, "Distribution type:", nca.Header.DistributionType.Print());
            PrintItem(sb, colLen, "Content Type:", nca.Header.ContentType.Print());
            PrintItem(sb, colLen, "Master Key Revision:", $"{masterKey} ({Utilities.GetKeyRevisionSummary(masterKey)})");
            PrintItem(sb, colLen, "Encryption Type:", $"{(nca.Header.HasRightsId ? "Titlekey crypto" : "Standard crypto")}");

            if (nca.Header.HasRightsId)
            {
                PrintItem(sb, colLen, "Rights ID:", nca.Header.RightsId.ToArray());
            }
            else
            {
                PrintKeyArea();
            }

            PrintSections();

            return(sb.ToString());

            void PrintKeyArea()
            {
                NcaVersion version = nca.Header.FormatVersion;

                if (version == NcaVersion.Nca0RsaOaep)
                {
                    sb.AppendLine("Key Area (Encrypted):");
                    PrintItem(sb, colLen, "Key (RSA-OAEP Encrypted):", nca.Header.GetKeyArea().ToArray());

                    sb.AppendLine("Key Area (Decrypted):");
                    for (int i = 0; i < 2; i++)
                    {
                        PrintItem(sb, colLen, $"    Key {i} (Decrypted):", nca.GetDecryptedKey(i));
                    }
                }
                else if (version == NcaVersion.Nca0FixedKey)
                {
                    sb.AppendLine("Key Area:");
                    for (int i = 0; i < 2; i++)
                    {
                        PrintItem(sb, colLen, $"    Key {i}:", nca.Header.GetEncryptedKey(i).ToArray());
                    }
                }
                else
                {
                    int keyCount = version == NcaVersion.Nca0 ? 2 : 4;

                    PrintItem(sb, colLen, "Key Area Encryption Key:", nca.Header.KeyAreaKeyIndex);
                    sb.AppendLine("Key Area (Encrypted):");
                    for (int i = 0; i < keyCount; i++)
                    {
                        PrintItem(sb, colLen, $"    Key {i} (Encrypted):", nca.Header.GetEncryptedKey(i).ToArray());
                    }

                    sb.AppendLine("Key Area (Decrypted):");
                    for (int i = 0; i < keyCount; i++)
                    {
                        PrintItem(sb, colLen, $"    Key {i} (Decrypted):", nca.GetDecryptedKey(i));
                    }
                }
            }

            void PrintSections()
            {
                sb.AppendLine("Sections:");

                for (int i = 0; i < 4; i++)
                {
                    if (!nca.Header.IsSectionEnabled(i))
                    {
                        continue;
                    }

                    NcaFsHeader sectHeader = nca.GetFsHeader(i);
                    bool        isExefs    = nca.Header.ContentType == NcaContentType.Program && i == 0;

                    sb.AppendLine($"    Section {i}:");
                    PrintItem(sb, colLen, "        Offset:", $"0x{nca.Header.GetSectionStartOffset(i):x12}");
                    PrintItem(sb, colLen, "        Size:", $"0x{nca.Header.GetSectionSize(i):x12}");
                    PrintItem(sb, colLen, "        Partition Type:", GetPartitionType(sectHeader, isExefs, nca.Header.IsNca0()));
                    PrintItem(sb, colLen, "        Section CTR:", $"{sectHeader.Counter:x16}");
                    PrintItem(sb, colLen, "        Section Validity:", $"{ncaHolder.Validities[i].Print()}");

                    switch (sectHeader.HashType)
                    {
                    case NcaHashType.Sha256:
                        PrintSha256Hash(sectHeader, i);
                        break;

                    case NcaHashType.Ivfc:
                        Validity masterHashValidity = nca.ValidateSectionMasterHash(i);

                        PrintIvfcHashNew(sb, colLen, 8, sectHeader.GetIntegrityInfoIvfc(), IntegrityStorageType.RomFs, masterHashValidity);
                        break;

                    default:
                        sb.AppendLine("        Unknown/invalid superblock!");
                        break;
                    }
                }
            }