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"); }
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"); }
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(); }
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)); } }
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))); } } }
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)."); } } }
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)."); } } }
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"); } }
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; } } }