private long OpenNcaFs(ServiceCtx Context, string NcaPath, Stream NcaStream) { Nca Nca = new Nca(Context.Device.System.KeySet, NcaStream, false); NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); NcaSection PfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); if (RomfsSection != null) { Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new RomFsProvider(RomfsStream)); MakeObject(Context, NcaFileSystem); } else if (PfsSection != null) { Stream PfsStream = Nca.OpenSection(PfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); Pfs Pfs = new Pfs(PfsStream); IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new PFsProvider(Pfs)); MakeObject(Context, NcaFileSystem); } else { return(MakeError(ErrorModule.Fs, FsErr.PartitionNotFound)); } return(0); }
public Task <ImageSource> FindTitleIcon(Title title) { if (title.ControlNca != null) { NcaSection meta = title.ControlNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); RomFsFileSystem controlFS = new RomFsFileSystem(title.ControlNca.OpenSection(meta.SectionNum, false, IntegrityCheckLevel.ErrorOnInvalid, false)); DirectoryEntry file = controlFS.EnumerateEntries("icon_*.dat").FirstOrDefault(); if (file != null) { return(new Task <ImageSource>(() => { try { JpegBitmapDecoder decoder = new JpegBitmapDecoder(controlFS.OpenFile(file.FullPath, OpenMode.Read).AsStream(), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); decoder.Frames[0].Freeze(); return decoder.Frames[0]; } catch (Exception) { return null; } })); } } return(null); }
public static void ExtractSection(this Nca nca, int index, string outputDir, bool verify = false, IProgressReport logger = null) { if (index < 0 || index > 3) { throw new IndexOutOfRangeException(); } if (nca.Sections[index] == null) { return; } NcaSection section = nca.Sections[index]; Stream stream = nca.OpenSection(index, false, verify); switch (section.Type) { case SectionType.Invalid: break; case SectionType.Pfs0: var pfs0 = new Pfs(stream); pfs0.Extract(outputDir, logger); break; case SectionType.Romfs: var romfs = new Romfs(stream); romfs.Extract(outputDir, logger); break; case SectionType.Bktr: break; } }
private long OpenNcaFs(ServiceCtx context, string ncaPath, LibHac.IO.IStorage ncaStorage) { Nca nca = new Nca(context.Device.System.KeySet, ncaStorage, false); NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); NcaSection pfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); if (romfsSection != null) { LibHac.IO.IStorage romfsStorage = nca.OpenSection(romfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false); IFileSystem ncaFileSystem = new IFileSystem(ncaPath, new RomFsProvider(romfsStorage)); MakeObject(context, ncaFileSystem); } else if (pfsSection != null) { LibHac.IO.IStorage pfsStorage = nca.OpenSection(pfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false); Pfs pfs = new Pfs(pfsStorage); IFileSystem ncaFileSystem = new IFileSystem(ncaPath, new PFsProvider(pfs)); MakeObject(context, ncaFileSystem); } else { return(MakeError(ErrorModule.Fs, FsErr.PartitionNotFound)); } return(0); }
// OpenDataStorageByDataId(u8 storageId, nn::ApplicationId tid) -> object<nn::fssrv::sf::IStorage> dataStorage public long OpenDataStorageByDataId(ServiceCtx Context) { StorageId StorageId = (StorageId)Context.RequestData.ReadByte(); byte[] Padding = Context.RequestData.ReadBytes(7); long TitleId = Context.RequestData.ReadInt64(); StorageId InstalledStorage = Context.Device.System.ContentManager.GetInstalledStorage(TitleId, ContentType.Data, StorageId); if (InstalledStorage == StorageId.None) { InstalledStorage = Context.Device.System.ContentManager.GetInstalledStorage(TitleId, ContentType.AocData, StorageId); } if (InstalledStorage != StorageId.None) { string ContentPath = Context.Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.AocData); if (string.IsNullOrWhiteSpace(ContentPath)) { ContentPath = Context.Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.AocData); } string InstallPath = Context.Device.FileSystem.SwitchPathToSystemPath(ContentPath); if (!string.IsNullOrWhiteSpace(InstallPath)) { string NcaPath = InstallPath; if (File.Exists(NcaPath)) { FileStream NcaStream = new FileStream(NcaPath, FileMode.Open, FileAccess.Read); Nca Nca = new Nca(Context.Device.System.KeySet, NcaStream, false); NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); MakeObject(Context, new IStorage(RomfsStream)); return(0); } else { throw new FileNotFoundException($"No Nca found in Path `{NcaPath}`."); } } else { throw new DirectoryNotFoundException($"Path for title id {TitleId:x16} on Storage {StorageId} was not found in Path {InstallPath}."); } } throw new FileNotFoundException($"System archive with titleid {TitleId:x16} was not found on Storage {StorageId}. Found in {InstalledStorage}."); }
// OpenDataStorageByDataId(u8 storageId, nn::ApplicationId tid) -> object<nn::fssrv::sf::IStorage> dataStorage public long OpenDataStorageByDataId(ServiceCtx context) { StorageId storageId = (StorageId)context.RequestData.ReadByte(); byte[] padding = context.RequestData.ReadBytes(7); long titleId = context.RequestData.ReadInt64(); StorageId installedStorage = context.Device.System.ContentManager.GetInstalledStorage(titleId, ContentType.Data, storageId); if (installedStorage == StorageId.None) { installedStorage = context.Device.System.ContentManager.GetInstalledStorage(titleId, ContentType.AocData, storageId); } if (installedStorage != StorageId.None) { string contentPath = context.Device.System.ContentManager.GetInstalledContentPath(titleId, storageId, ContentType.AocData); if (string.IsNullOrWhiteSpace(contentPath)) { contentPath = context.Device.System.ContentManager.GetInstalledContentPath(titleId, storageId, ContentType.AocData); } string installPath = context.Device.FileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(installPath)) { string ncaPath = installPath; if (File.Exists(ncaPath)) { FileStream ncaStream = new FileStream(ncaPath, FileMode.Open, FileAccess.Read); Nca nca = new Nca(context.Device.System.KeySet, ncaStream, false); NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); Stream romfsStream = nca.OpenSection(romfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel); MakeObject(context, new IStorage(romfsStream)); return(0); } else { throw new FileNotFoundException($"No Nca found in Path `{ncaPath}`."); } } else { throw new DirectoryNotFoundException($"Path for title id {titleId:x16} on Storage {storageId} was not found in Path {installPath}."); } } throw new FileNotFoundException($"System archive with titleid {titleId:x16} was not found on Storage {storageId}. Found in {installedStorage}."); }
public static NcaSection ParseSection(NcaHeader Header, int index) { var entry = Header.SectionEntries[index]; var header = Header.FsHeaders[index]; if (entry.MediaStartOffset == 0) { return(null); } var sect = new NcaSection(); sect.SectionNum = index; sect.Offset = Utils.MediaToReal(entry.MediaStartOffset); sect.Size = Utils.MediaToReal(entry.MediaEndOffset) - sect.Offset; sect.Header = header; sect.Type = header.Type; return(sect); }
public static Validity VerifySection(this Nca nca, int index, IProgressReport logger = null, bool quiet = false) { if (nca.Sections[index] == null) { throw new ArgumentOutOfRangeException(nameof(index)); } NcaSection sect = nca.Sections[index]; NcaHashType hashType = sect.Header.HashType; if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) { return(Validity.Unchecked); } var stream = nca.OpenStorage(index, IntegrityCheckLevel.IgnoreOnInvalid, false) as HierarchicalIntegrityVerificationStorage; if (stream == null) { return(Validity.Unchecked); } if (!quiet) { logger?.LogMessage($"Verifying section {index}..."); } Validity validity = stream.Validate(true, logger); if (hashType == NcaHashType.Ivfc) { stream.SetLevelValidities(sect.Header.IvfcInfo); } else if (hashType == NcaHashType.Sha256) { sect.Header.Sha256Info.HashValidity = validity; } return(validity); }
public Bktr(Stream patchRomfs, Stream baseRomfs, NcaSection section) { if (section.Header.EncryptionType != NcaEncryptionType.AesCtrEx) { throw new ArgumentException("Section is not of type BKTR"); } Patch = patchRomfs ?? throw new NullReferenceException($"{nameof(patchRomfs)} cannot be null"); Base = baseRomfs ?? throw new NullReferenceException($"{nameof(baseRomfs)} cannot be null"); IvfcLevelHeader level5 = section.Header.IvfcInfo.LevelHeaders[5]; Length = level5.LogicalOffset + level5.HashDataSize; using (var reader = new BinaryReader(patchRomfs, Encoding.Default, true)) { patchRomfs.Position = section.Header.BktrInfo.RelocationHeader.Offset; RelocationBlock = new RelocationBlock(reader); } foreach (RelocationBucket bucket in RelocationBlock.Buckets) { RelocationEntries.AddRange(bucket.Entries); } for (int i = 0; i < RelocationEntries.Count - 1; i++) { RelocationEntries[i].Next = RelocationEntries[i + 1]; RelocationEntries[i].VirtOffsetEnd = RelocationEntries[i + 1].VirtOffset; } RelocationEntries[RelocationEntries.Count - 1].VirtOffsetEnd = level5.LogicalOffset + level5.HashDataSize; RelocationOffsets = RelocationEntries.Select(x => x.VirtOffset).ToList(); CurrentEntry = GetRelocationEntry(0); UpdateSourceStreamPositions(); }
private static string Print(this Nca nca) { int colLen = 36; var sb = new StringBuilder(); sb.AppendLine(); sb.AppendLine("NCA:"); PrintItem(sb, colLen, "Magic:", nca.Header.Magic); PrintItem(sb, colLen, $"Fixed-Key Signature{nca.Header.FixedSigValidity.GetValidityString()}:", nca.Header.Signature1); PrintItem(sb, colLen, $"NPDM Signature{nca.Header.NpdmSigValidity.GetValidityString()}:", nca.Header.Signature2); PrintItem(sb, colLen, "Content Size:", $"0x{nca.Header.NcaSize:x12}"); PrintItem(sb, colLen, "TitleID:", $"{nca.Header.TitleId:X16}"); PrintItem(sb, colLen, "SDK Version:", nca.Header.SdkVersion); PrintItem(sb, colLen, "Distribution type:", nca.Header.Distribution); PrintItem(sb, colLen, "Content Type:", nca.Header.ContentType); PrintItem(sb, colLen, "Master Key Revision:", $"{nca.CryptoType} ({Util.GetKeyRevisionSummary(nca.CryptoType)})"); PrintItem(sb, colLen, "Encryption Type:", $"{(nca.HasRightsId ? "Titlekey crypto" : "Standard crypto")}"); if (nca.HasRightsId) { PrintItem(sb, colLen, "Rights ID:", nca.Header.RightsId); } else { PrintItem(sb, colLen, "Key Area Encryption Key:", nca.Header.KaekInd); sb.AppendLine("Key Area (Encrypted):"); for (int i = 0; i < 4; i++) { PrintItem(sb, colLen, $" Key {i} (Encrypted):", nca.Header.EncryptedKeys[i]); } sb.AppendLine("Key Area (Decrypted):"); for (int i = 0; i < 4; i++) { PrintItem(sb, colLen, $" Key {i} (Decrypted):", nca.DecryptedKeys[i]); } } PrintSections(); return(sb.ToString()); void PrintSections() { sb.AppendLine("Sections:"); for (int i = 0; i < 4; i++) { NcaSection sect = nca.Sections[i]; if (sect == null) { continue; } bool isExefs = nca.Header.ContentType == ContentType.Program && i == (int)ProgramPartitionType.Code; sb.AppendLine($" Section {i}:"); PrintItem(sb, colLen, " Offset:", $"0x{sect.Offset:x12}"); PrintItem(sb, colLen, " Size:", $"0x{sect.Size:x12}"); PrintItem(sb, colLen, " Partition Type:", isExefs ? "ExeFS" : sect.Type.ToString()); PrintItem(sb, colLen, " Section CTR:", sect.Header.Ctr); switch (sect.Header.HashType) { case NcaHashType.Sha256: PrintSha256Hash(sect); break; case NcaHashType.Ivfc: PrintIvfcHash(sb, colLen, 8, sect.Header.IvfcInfo, IntegrityStorageType.RomFs); break; default: sb.AppendLine(" Unknown/invalid superblock!"); break; } } } void PrintSha256Hash(NcaSection sect) { Sha256Info hashInfo = sect.Header.Sha256Info; PrintItem(sb, colLen, $" Master Hash{sect.MasterHashValidity.GetValidityString()}:", hashInfo.MasterHash); sb.AppendLine($" Hash Table{sect.Header.Sha256Info.HashValidity.GetValidityString()}:"); PrintItem(sb, colLen, " Offset:", $"0x{hashInfo.HashTableOffset:x12}"); PrintItem(sb, colLen, " Size:", $"0x{hashInfo.HashTableSize:x12}"); PrintItem(sb, colLen, " Block Size:", $"0x{hashInfo.BlockSize:x}"); PrintItem(sb, colLen, " PFS0 Offset:", $"0x{hashInfo.DataOffset:x12}"); PrintItem(sb, colLen, " PFS0 Size:", $"0x{hashInfo.DataSize:x12}"); } }
public static void Process(Context ctx) { using (IStorage file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) { var nca = new Nca(ctx.Keyset, file, false); nca.ValidateMasterHashes(); nca.ParseNpdm(); if (ctx.Options.BaseNca != null) { IStorage baseFile = new LocalStorage(ctx.Options.BaseNca, FileAccess.Read); var baseNca = new Nca(ctx.Keyset, baseFile, false); nca.SetBaseNca(baseNca); } for (int i = 0; i < 3; i++) { if (ctx.Options.SectionOut[i] != null) { nca.ExportSection(i, ctx.Options.SectionOut[i], ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.SectionOutDir[i] != null) { nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.Validate && nca.Sections[i] != null) { nca.VerifySection(i, ctx.Logger); } } if (ctx.Options.ListRomFs && nca.Sections[1] != null) { var romfs = new RomFsFileSystem(nca.OpenSection(1, false, ctx.Options.IntegrityLevel, true)); foreach (DirectoryEntry entry in romfs.EnumerateEntries()) { ctx.Logger.LogMessage(entry.FullPath); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ReadBench) { NcaSection section = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (section == null) { ctx.Logger.LogMessage("NCA has no RomFS section"); return; } if (section.Type == SectionType.Bktr && ctx.Options.BaseNca == null) { ctx.Logger.LogMessage("Cannot save BKTR section without base RomFS"); return; } if (ctx.Options.RomfsOut != null) { nca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.RomfsOutDir != null) { IFileSystem romfs = nca.OpenSectionFileSystem(section.SectionNum, ctx.Options.IntegrityLevel); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.ReadBench) { long bytesToRead = 1024L * 1024 * 1024 * 5; IStorage storage = nca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel, true); var dest = new NullStorage(storage.Length); int iterations = (int)(bytesToRead / storage.Length) + 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; } NcaSection section = nca.Sections[(int)ProgramPartitionType.Code]; if (section == null) { ctx.Logger.LogMessage("Could not find an ExeFS section"); return; } if (ctx.Options.ExefsOut != null) { nca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOutDir != null) { IFileSystem pfs = nca.OpenSectionFileSystem(section.SectionNum, ctx.Options.IntegrityLevel); pfs.Extract(ctx.Options.ExefsOutDir, ctx.Logger); } } if (ctx.Options.PlaintextOut != null) { nca.OpenDecryptedNca().WriteAllBytes(ctx.Options.PlaintextOut, ctx.Logger); } if (!ctx.Options.ReadBench) { ctx.Logger.LogMessage(nca.Print()); } } }
public static void EncryptFunct( Stream Input, Stream Output, string ncaFilename, bool verifyEncrypted, Keyset keyset, Output Out) { var DecryptedKeys = Utils.CreateJaggedArray <byte[][]>(4, 0x10); var HeaderKey1 = new byte[16]; var HeaderKey2 = new byte[16]; Buffer.BlockCopy(keyset.HeaderKey, 0, HeaderKey1, 0, 16); Buffer.BlockCopy(keyset.HeaderKey, 16, HeaderKey2, 0, 16); var DecryptedHeader = new byte[0xC00]; Input.Read(DecryptedHeader, 0, 0xC00); var Header = new NcaHeader(new BinaryReader(new MemoryStream(DecryptedHeader)), keyset); var CryptoType = Math.Max(Header.CryptoType, Header.CryptoType2); if (CryptoType > 0) { CryptoType--; } var HasRightsId = !Header.RightsId.IsEmpty(); if (!HasRightsId) { Out.Log("Key Area (Encrypted):\r\n"); if (keyset.KeyAreaKeys[CryptoType][Header.KaekInd].IsEmpty()) { throw new ArgumentException($"key_area_key_{KakNames[Header.KaekInd]}_{CryptoType:x2}", "Missing area key!"); } Out.Log( $"key_area_key_{KakNames[Header.KaekInd]}_{CryptoType:x2}: {Utils.BytesToString(keyset.KeyAreaKeys[CryptoType][Header.KaekInd])}\r\n"); for (var i = 0; i < 4; ++i) { LibHac.Crypto.DecryptEcb(keyset.KeyAreaKeys[CryptoType][Header.KaekInd], Header.EncryptedKeys[i], DecryptedKeys[i], 0x10); Out.Log($"Key {i} (Encrypted): {Utils.BytesToString(Header.EncryptedKeys[i])}\r\n"); Out.Log($"Key {i} (Decrypted): {Utils.BytesToString(DecryptedKeys[i])}\r\n"); } } else { var titleKey = keyset.TitleKeys[Header.RightsId]; var TitleKeyDec = new byte[0x10]; LibHac.Crypto.DecryptEcb(keyset.TitleKeks[CryptoType], titleKey, TitleKeyDec, 0x10); Out.Log($"titleKey: {Utils.BytesToString(titleKey)}\r\n"); Out.Log($"TitleKeyDec: {Utils.BytesToString(TitleKeyDec)}\r\n"); DecryptedKeys[2] = TitleKeyDec; } var Sections = new NcaSection[4]; var SectionsByOffset = new Dictionary <long, int>(); var lowestOffset = long.MaxValue; for (var i = 0; i < 4; ++i) { var section = NcaParseSection.ParseSection(Header, i); if (section == null) { continue; } SectionsByOffset.Add(section.Offset, i); if (section.Offset < lowestOffset) { lowestOffset = section.Offset; } Sections[i] = section; } Out.Log($"HeaderKey: {Utils.BytesToString(keyset.HeaderKey)}\r\n"); Out.Log("Encrypting and writing header to NCA...\r\n"); SHA256Cng sha256NCA = null; if (verifyEncrypted) { sha256NCA = new SHA256Cng(); sha256NCA.Initialize(); } var encryptedHeader = CryptoInitialisers.AES_XTS(HeaderKey1, HeaderKey2, 0x200, DecryptedHeader, 0); if (Output != null) { Output.Write(encryptedHeader, 0, DecryptedHeader.Length); } if (verifyEncrypted) { sha256NCA.TransformBlock(encryptedHeader, 0, DecryptedHeader.Length, null, 0); } var dummyHeader = new byte[0xC00]; ulong dummyHeaderSector = 6; long dummyHeaderPos; for (dummyHeaderPos = 0xC00; dummyHeaderPos < lowestOffset; dummyHeaderPos += 0xC00) { var dummyHeaderWriteCount = (int)Math.Min(lowestOffset - dummyHeaderPos, DecryptedHeader.Length); Input.Read(dummyHeader, 0, dummyHeaderWriteCount); var dummyHeaderEncrypted = CryptoInitialisers.AES_XTS(HeaderKey1, HeaderKey2, 0x200, dummyHeader, dummyHeaderSector); if (Output != null) { Output.Write(dummyHeaderEncrypted, 0, dummyHeaderWriteCount); } if (verifyEncrypted) { sha256NCA.TransformBlock(dummyHeaderEncrypted, 0, dummyHeaderWriteCount, null, 0); } dummyHeaderSector += 6; } Out.Log("Encrypting and writing sectors to NCA...\r\n"); Out.Log("Sections:\r\n"); foreach (var i in SectionsByOffset.OrderBy(i => i.Key).Select(item => item.Value)) { var sect = Sections[i]; if (sect == null) { continue; } var isExefs = Header.ContentType == ContentType.Program && i == (int)ProgramPartitionType.Code; var PartitionType = isExefs ? "ExeFS" : sect.Type.ToString(); Out.Log($" Section {i}:\r\n"); Out.Log($" Offset: 0x{sect.Offset:x12}\r\n"); Out.Log($" Size: 0x{sect.Size:x12}\r\n"); Out.Log($" Partition Type: {PartitionType}\r\n"); Out.Log($" Section CTR: {Utils.BytesToString(sect.Header.Ctr)}\r\n"); var initialCounter = new byte[0x10]; if (sect.Header.Ctr != null) { Array.Copy(sect.Header.Ctr, initialCounter, 8); } Out.Log($"initialCounter: {Utils.BytesToString(initialCounter)}\r\n"); if (Input.Position != sect.Offset) { //Input.Seek(sect.Offset, SeekOrigin.Begin); //Output.Seek(sect.Offset, SeekOrigin.Begin); //Todo: sha256NCA Gap support throw new NotImplementedException("Gaps between NCA sections aren't implemented yet!"); } const int maxBS = 10485760; //10 MB int bs; var DecryptedSectionBlock = new byte[maxBS]; var sectOffsetEnd = sect.Offset + sect.Size; var AesCtrEncrypter = new Aes128CtrTransform(DecryptedKeys[2], initialCounter); switch (sect.Header.EncryptionType) { case NcaEncryptionType.None: while (Input.Position < sectOffsetEnd) { bs = (int)Math.Min(sectOffsetEnd - Input.Position, maxBS); Out.Print($"Encrypted: {Input.Position / 0x100000} MB\r\n"); Input.Read(DecryptedSectionBlock, 0, bs); if (Output != null) { Output.Write(DecryptedSectionBlock, 0, bs); } if (verifyEncrypted) { sha256NCA.TransformBlock(DecryptedSectionBlock, 0, bs, null, 0); } } break; case NcaEncryptionType.AesCtr: while (Input.Position < sectOffsetEnd) { SetCtrOffset(initialCounter, Input.Position); bs = (int)Math.Min(sectOffsetEnd - Input.Position, maxBS); Out.Print($"Encrypted: {Input.Position / 0x100000} MB\r\n"); Input.Read(DecryptedSectionBlock, 0, bs); AesCtrEncrypter.Counter = initialCounter; AesCtrEncrypter.TransformBlock(DecryptedSectionBlock); if (Output != null) { Output.Write(DecryptedSectionBlock, 0, bs); } if (verifyEncrypted) { sha256NCA.TransformBlock(DecryptedSectionBlock, 0, bs, null, 0); } } break; case NcaEncryptionType.AesCtrEx: var info = sect.Header.BktrInfo; var MyBucketTree = new MyBucketTree <AesSubsectionEntry>( new MemoryStream(sect.Header.BktrInfo.EncryptionHeader.Header), Input, sect.Offset + info.EncryptionHeader.Offset); var SubsectionEntries = MyBucketTree.GetEntryList(); var SubsectionOffsets = SubsectionEntries.Select(x => x.Offset).ToList(); var subsectionEntryCounter = new byte[0x10]; Array.Copy(initialCounter, subsectionEntryCounter, 0x10); foreach (var entry in SubsectionEntries) { do { bs = (int)Math.Min((sect.Offset + entry.OffsetEnd) - Input.Position, maxBS); SetCtrOffset(subsectionEntryCounter, Input.Position); subsectionEntryCounter[7] = (byte)entry.Counter; subsectionEntryCounter[6] = (byte)(entry.Counter >> 8); subsectionEntryCounter[5] = (byte)(entry.Counter >> 16); subsectionEntryCounter[4] = (byte)(entry.Counter >> 24); var DecryptedSectionBlockLUL = new byte[bs]; Out.Print($"Encrypted: {Input.Position / 0x100000} MB\r\n"); Out.Log($"{Input.Position}: {Utils.BytesToString(subsectionEntryCounter)}\r\n"); Input.Read(DecryptedSectionBlockLUL, 0, bs); AesCtrEncrypter.Counter = subsectionEntryCounter; AesCtrEncrypter.TransformBlock(DecryptedSectionBlockLUL); if (Output != null) { Output.Write(DecryptedSectionBlockLUL, 0, bs); } if (verifyEncrypted) { sha256NCA.TransformBlock(DecryptedSectionBlockLUL, 0, bs, null, 0); } } while (Input.Position < entry.OffsetEnd); } while (Input.Position < sectOffsetEnd) { SetCtrOffset(subsectionEntryCounter, Input.Position); bs = (int)Math.Min(sectOffsetEnd - Input.Position, maxBS); Out.Print($"EncryptedAfter: {Input.Position / 0x100000} MB\r\n"); Input.Read(DecryptedSectionBlock, 0, bs); Out.Log($"{Input.Position}: {Utils.BytesToString(subsectionEntryCounter)}\r\n"); AesCtrEncrypter.Counter = subsectionEntryCounter; AesCtrEncrypter.TransformBlock(DecryptedSectionBlock); if (Output != null) { Output.Write(DecryptedSectionBlock, 0, bs); } if (verifyEncrypted) { sha256NCA.TransformBlock(DecryptedSectionBlock, 0, bs, null, 0); } } break; default: throw new NotImplementedException(); } } if (verifyEncrypted) { sha256NCA.TransformFinalBlock(new byte[0], 0, 0); var sha256NCAHashString = Utils.BytesToString(sha256NCA.Hash).ToLower(); if (sha256NCAHashString.StartsWith(ncaFilename.Split('.')[0].ToLower())) { Out.Log($"[VERIFIED] {sha256NCAHashString}\r\n"); } else { throw new Exception($"[INVALID HASH] sha256({ncaFilename}) = {sha256NCAHashString}\r\n"); } } }
private void OnNandFound() { Nand nand = NANDService.NAND; Stream NANDSource = NANDService.NANDSource; NANDSource.Seek(0x804000, SeekOrigin.Begin); // BCPKG2-1-Normal-Main offset + length of BootConfig FileStream pkg2stream = HACGUIKeyset.TempPkg2FileInfo.Create(); NANDSource.CopyToNew(pkg2stream, 0x7FC000); // rest of BCPPKG2-Normal-Main partition pkg2stream.Seek(0, SeekOrigin.Begin); byte[] pkg2raw = new byte[pkg2stream.Length]; pkg2stream.Read(pkg2raw, 0, pkg2raw.Length); Package2 pkg2 = new Package2(HACGUIKeyset.Keyset, new MemoryStream(pkg2raw)); HACGUIKeyset.RootTempPkg2FolderInfo.Create(); FileStream kernelstream = HACGUIKeyset.TempKernelFileInfo.Create(); FileStream INI1stream = HACGUIKeyset.TempINI1FileInfo.Create(); pkg2.OpenKernel().CopyTo(kernelstream); pkg2.OpenIni1().CopyTo(INI1stream); kernelstream.Close(); INI1stream.Close(); Ini1 INI1 = new Ini1(pkg2.OpenIni1()); List <HashSearchEntry> hashes = new List <HashSearchEntry>(); Dictionary <byte[], byte[]> keys = new Dictionary <byte[], byte[]>(); HACGUIKeyset.RootTempINI1Folder.Create(); foreach (Kip kip in INI1.Kips) { Stream rodatastream, datastream; switch (kip.Header.Name) { case "FS": hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyApplicationSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyOceanSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeySystemSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKeySourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyApplicationSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyApplicationSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyOceanSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyOceanSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeySystemSourceHash], HACGUIKeyset.Keyset.KeyAreaKeySystemSource, 0x10); Array.Copy(keys[NintendoKeys.HeaderKekSourceHash], HACGUIKeyset.Keyset.HeaderKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKekSourceHash], HACGUIKeyset.Keyset.SaveMacKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKeySourceHash], HACGUIKeyset.Keyset.SaveMacKeySource, 0x10); hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); bool sdWarn = false; hashes.Add(new HashSearchEntry(NintendoKeys.SDCardKekSourceHash, 0x10)); try { keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.SDCardKekSourceHash], HACGUIKeyset.Keyset.SdCardKekSource, 0x10); } catch (EndOfStreamException) { MessageBox.Show("Failed to find SD card kek source! The NAND is probably from 1.0.0."); sdWarn = true; } if (!sdWarn) // don't try to find the rest of the keys if the other one couldn't be found { hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardSaveKeySourceHash, 0x20)); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardNcaKeySourceHash, 0x20)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.SDCardSaveKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[0], 0x20); Array.Copy(keys[NintendoKeys.SDCardNcaKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[1], 0x20); } hashes.Clear(); rodatastream.Close(); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKeySourceHash, 0x20)); datastream = new MemoryStream(kip.DecompressSection(2)); keys = datastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.HeaderKeySourceHash], HACGUIKeyset.Keyset.HeaderKeySource, 0x20); datastream.Close(); hashes.Clear(); break; case "spl": hashes.Add(new HashSearchEntry(NintendoKeys.AesKeyGenerationSourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.AesKeyGenerationSourceHash], HACGUIKeyset.Keyset.AesKeyGenerationSource, 0x10); rodatastream.Close(); hashes.Clear(); break; } FileStream kipstream = HACGUIKeyset.RootTempINI1Folder.GetFile(kip.Header.Name + ".kip").Create(); kip.OpenRawFile().CopyTo(kipstream); kipstream.Close(); } pkg2stream.Close(); INI1stream.Close(); HACGUIKeyset.Keyset.DeriveKeys(); SwitchFs fs = new SwitchFs(HACGUIKeyset.Keyset, NANDService.NAND.OpenSystemPartition()); foreach (KeyValuePair <string, Nca> kv in fs.Ncas) { Nca nca = kv.Value; switch (nca.Header.TitleId) { case 0x0100000000000033: // es switch (nca.Header.ContentType) { case ContentType.Program: NcaSection exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); Stream pfsStream = nca.OpenSection(exefsSection.SectionNum, false, false); Pfs pfs = new Pfs(pfsStream); Nso nso = new Nso(pfs.OpenFile("main")); NsoSection section = nso.Sections[1]; Stream data = new MemoryStream(section.DecompressSection()); hashes.Clear(); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekekSourceHash, 0x10)); keys = data.FindKeyViaHash(hashes, new SHA256Managed(), 0x10, data.Length); byte[] EticketRsaKekSource = new byte[0x10]; byte[] EticketRsaKekekSource = new byte[0x10]; Array.Copy(keys[NintendoKeys.EticketRsaKekSourceHash], EticketRsaKekSource, 0x10); Array.Copy(keys[NintendoKeys.EticketRsaKekekSourceHash], EticketRsaKekekSource, 0x10); byte[] RsaOaepKekGenerationSource; XOR(NintendoKeys.KekMasks[0], NintendoKeys.KekSeeds[3], out RsaOaepKekGenerationSource); byte[] key1 = new byte[0x10]; Crypto.DecryptEcb(HACGUIKeyset.Keyset.MasterKeys[0], RsaOaepKekGenerationSource, key1, 0x10); byte[] key2 = new byte[0x10]; Crypto.DecryptEcb(key1, EticketRsaKekekSource, key2, 0x10); byte[] key3 = new byte[0x10]; Crypto.DecryptEcb(key2, EticketRsaKekSource, HACGUIKeyset.Keyset.EticketRsaKek, 0x10); break; } break; } } Stream prodinfo = nand.OpenProdInfo(); Stream prodinfoFile = HACGUIKeyset.TempPRODINFOFileInfo.Create(); prodinfo.CopyTo(prodinfoFile); prodinfo.Close(); prodinfoFile.Seek(0, SeekOrigin.Begin); Calibration cal0 = new Calibration(prodinfoFile); HACGUIKeyset.Keyset.EticketExtKeyRsa = Crypto.DecryptRsaKey(cal0.EticketExtKeyRsa, HACGUIKeyset.Keyset.EticketRsaKek); prodinfoFile.Close(); List <Ticket> tickets = new List <Ticket>(); NandPartition system = nand.OpenSystemPartition(); Stream e1Stream = system.OpenFile("save\\80000000000000E1", FileMode.Open, FileAccess.Read); tickets.AddRange(ReadTickets(HACGUIKeyset.Keyset, e1Stream)); Stream e2Stream = system.OpenFile("save\\80000000000000E2", FileMode.Open, FileAccess.Read); tickets.AddRange(ReadTickets(HACGUIKeyset.Keyset, e2Stream)); Stream nsAppmanStream = system.OpenFile("save\\8000000000000043", FileMode.Open, FileAccess.Read); Savefile save = new Savefile(HACGUIKeyset.Keyset, nsAppmanStream, false); Stream privateStream = save.OpenFile("/private"); byte[] sdSeed = new byte[0x10]; privateStream.Read(sdSeed, 0, 0x10); // Seek doesn't work so i just read twice privateStream.Read(sdSeed, 0, 0x10); HACGUIKeyset.Keyset.SetSdSeed(sdSeed); foreach (Ticket ticket in tickets) { HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId] = new byte[0x10]; Array.Copy(ticket.TitleKeyBlock, HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId], 0x10); } NANDService.Stop(); HACGUIKeyset.ProductionKeysFileInfo.Create().WriteString(HACGUIKeyset.PrintCommonKeys(HACGUIKeyset.Keyset, true)); HACGUIKeyset.ExtraKeysFileInfo.Create().WriteString(HACGUIKeyset.PrintCommonWithoutFriendlyKeys(HACGUIKeyset.Keyset)); HACGUIKeyset.ConsoleKeysFileInfo.Create().WriteString(ExternalKeys.PrintUniqueKeys(HACGUIKeyset.Keyset)); HACGUIKeyset.GetConsoleKeysFileInfoByName(PickConsole.ConsoleName).Create().WriteString(ExternalKeys.PrintUniqueKeys(HACGUIKeyset.Keyset)); HACGUIKeyset.TitleKeysFileInfo.Create().WriteString(ExternalKeys.PrintTitleKeys(HACGUIKeyset.Keyset)); }
public static void Process(Context ctx) { using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read)) { var xci = new Xci(ctx.Keyset, file.AsStorage()); ctx.Logger.LogMessage(xci.Print()); if (ctx.Options.RootDir != null) { xci.RootPartition?.Extract(ctx.Options.RootDir, ctx.Logger); } if (ctx.Options.UpdateDir != null) { xci.UpdatePartition?.Extract(ctx.Options.UpdateDir, ctx.Logger); } if (ctx.Options.NormalDir != null) { xci.NormalPartition?.Extract(ctx.Options.NormalDir, ctx.Logger); } if (ctx.Options.SecureDir != null) { xci.SecurePartition?.Extract(ctx.Options.SecureDir, ctx.Logger); } if (ctx.Options.LogoDir != null) { xci.LogoPartition?.Extract(ctx.Options.LogoDir, ctx.Logger); } if (ctx.Options.OutDir != null && xci.RootPartition != null) { XciPartition root = xci.RootPartition; if (root == null) { ctx.Logger.LogMessage("Could not find root partition"); return; } foreach (PfsFileEntry sub in root.Files) { var subPfs = new Pfs(root.OpenFile(sub)); string subDir = Path.Combine(ctx.Options.OutDir, sub.Name); subPfs.Extract(subDir, ctx.Logger); } } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { Nca mainNca = GetXciMainNca(xci, ctx); if (mainNca == null) { ctx.Logger.LogMessage("Could not find Program NCA"); return; } NcaSection exefsSection = mainNca.Sections[(int)ProgramPartitionType.Code]; if (exefsSection == null) { ctx.Logger.LogMessage("NCA has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { mainNca.ExtractSection(exefsSection.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { mainNca.ExportSection(exefsSection.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { Nca mainNca = GetXciMainNca(xci, ctx); if (mainNca == null) { ctx.Logger.LogMessage("Could not find Program NCA"); return; } NcaSection romfsSection = mainNca.Sections.FirstOrDefault(x => x.Type == SectionType.Romfs); if (romfsSection == null) { ctx.Logger.LogMessage("NCA has no RomFS section"); return; } if (ctx.Options.RomfsOutDir != null) { var romfs = new Romfs(mainNca.OpenSection(romfsSection.SectionNum, false, ctx.Options.IntegrityLevel, true)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { mainNca.ExportSection(romfsSection.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } } }
public void LoadNca(Nca MainNca, Nca ControlNca) { NcaSection RomfsSection = MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); NcaSection ExefsSection = MainNca.Sections.FirstOrDefault(x => x?.IsExefs == true); if (ExefsSection == null) { Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA"); return; } if (RomfsSection == null) { Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA"); } else { Stream RomfsStream = MainNca.OpenSection(RomfsSection.SectionNum, false, EnableFsIntegrityChecks); Device.FileSystem.SetRomFs(RomfsStream); } Stream ExefsStream = MainNca.OpenSection(ExefsSection.SectionNum, false, EnableFsIntegrityChecks); Pfs Exefs = new Pfs(ExefsStream); Npdm MetaData = null; if (Exefs.FileExists("main.npdm")) { Logger.PrintInfo(LogClass.Loader, "Loading main.npdm..."); MetaData = new Npdm(Exefs.OpenFile("main.npdm")); } else { Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); } Process MainProcess = MakeProcess(MetaData); void LoadNso(string Filename) { foreach (PfsFileEntry File in Exefs.Files.Where(x => x.Name.StartsWith(Filename))) { if (Path.GetExtension(File.Name) != string.Empty) { continue; } Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}..."); string Name = Path.GetFileNameWithoutExtension(File.Name); Nso Program = new Nso(Exefs.OpenFile(File), Name); MainProcess.LoadProgram(Program); } } Nacp ReadControlData() { Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, EnableFsIntegrityChecks)); byte[] ControlFile = ControlRomfs.GetFile("/control.nacp"); BinaryReader Reader = new BinaryReader(new MemoryStream(ControlFile)); Nacp ControlData = new Nacp(Reader); CurrentTitle = ControlData.Languages[(int)State.DesiredTitleLanguage].Title; if (string.IsNullOrWhiteSpace(CurrentTitle)) { CurrentTitle = ControlData.Languages.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title; } return(ControlData); } if (ControlNca != null) { MainProcess.ControlData = ReadControlData(); } else { CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16"); } if (!MainProcess.MetaData.Is64Bits) { throw new NotImplementedException("32-bit titles are unsupported!"); } LoadNso("rtld"); MainProcess.SetEmptyArgs(); LoadNso("main"); LoadNso("subsdk"); LoadNso("sdk"); MainProcess.Run(); }
public void EnsureInitialized(ContentManager ContentManager) { if (FontData == null) { Device.Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize); uint FontOffset = 0; FontInfo CreateFont(string Name) { if (ContentManager.TryGetFontTitle(Name, out long FontTitle)) { string ContentPath = ContentManager.GetInstalledContentPath(FontTitle, StorageId.NandSystem, ContentType.Data); string FontPath = Device.FileSystem.SwitchPathToSystemPath(ContentPath); if (!string.IsNullOrWhiteSpace(FontPath)) { int FileIndex = 0; //Use second file in Chinese Font title for standard if (Name == "FontChineseSimplified") { FileIndex = 1; } FileStream NcaFileStream = new FileStream(FontPath, FileMode.Open, FileAccess.Read); Nca Nca = new Nca(Device.System.KeySet, NcaFileStream, false); NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); Romfs Romfs = new Romfs(Nca.OpenSection(RomfsSection.SectionNum, false, Device.System.FsIntegrityCheckLevel)); Stream FontFile = Romfs.OpenFile(Romfs.Files[FileIndex]); byte[] Data = DecryptFont(FontFile); 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]); } NcaFileStream.Dispose(); Nca.Dispose(); 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 { throw new InvalidSystemResourceException($"Font \"{Name}.ttf\" not found. Please provide it in \"{FontsPath}\"."); } } 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 static void Process(Context ctx) { SwitchFs switchFs; var baseFs = new LocalFileSystem(ctx.Options.InFile); if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Nintendo", "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as SD card storage"); switchFs = SwitchFs.OpenSdCard(ctx.Keyset, baseFs); } else if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as NAND storage"); switchFs = SwitchFs.OpenNandPartition(ctx.Keyset, baseFs); } else { ctx.Logger.LogMessage("Treating path as a directory of loose NCAs"); switchFs = SwitchFs.OpenNcaDirectory(ctx.Keyset, baseFs); } if (ctx.Options.ListNcas) { ctx.Logger.LogMessage(ListNcas(switchFs)); } if (ctx.Options.ListTitles) { ctx.Logger.LogMessage(ListTitles(switchFs)); } if (ctx.Options.ListApps) { ctx.Logger.LogMessage(ListApplications(switchFs)); } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump ExeFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections[(int)ProgramPartitionType.Code]; if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump RomFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section"); return; } if (ctx.Options.RomfsOutDir != null) { var romfs = new RomFsFileSystem(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel, true)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.OutDir != null) { SaveTitle(ctx, switchFs); } if (ctx.Options.NspOut != null) { ProcessNsp.CreateNsp(ctx, switchFs); } if (ctx.Options.SaveOutDir != null) { ExportSdSaves(ctx, switchFs); } if (ctx.Options.Validate) { ValidateSwitchFs(ctx, switchFs); } }
public static void Process(Context ctx) { var switchFs = new SwitchFs(ctx.Keyset, new FileSystem(ctx.Options.InFile)); if (ctx.Options.ListTitles) { ListTitles(switchFs); } if (ctx.Options.ListApps) { ctx.Logger.LogMessage(ListApplications(switchFs)); } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump ExeFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections[(int)ProgramPartitionType.Code]; if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump RomFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section"); return; } if (ctx.Options.RomfsOutDir != null) { var romfs = new Romfs(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.OutDir != null) { SaveTitle(ctx, switchFs); } if (ctx.Options.NspOut != null) { ProcessNsp.CreateNsp(ctx, switchFs); } if (ctx.Options.SaveOutDir != null) { ExportSdSaves(ctx, switchFs); } if (ctx.Options.Validate) { ValidateSwitchFs(ctx, switchFs); } }
public static void Process(Context ctx) { using (var file = new StreamStorage(new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read), false)) { var nca = new Nca(ctx.Keyset, file, false); nca.ValidateMasterHashes(); nca.ParseNpdm(); if (ctx.Options.BaseNca != null) { var baseFile = new StreamStorage(new FileStream(ctx.Options.BaseNca, FileMode.Open, FileAccess.Read), false); var baseNca = new Nca(ctx.Keyset, baseFile, false); nca.SetBaseNca(baseNca); } for (int i = 0; i < 3; i++) { if (ctx.Options.SectionOut[i] != null) { nca.ExportSection(i, ctx.Options.SectionOut[i], ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.SectionOutDir[i] != null) { nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.Validate && nca.Sections[i] != null) { nca.VerifySection(i, ctx.Logger); } } if (ctx.Options.ListRomFs && nca.Sections[1] != null) { var romfs = new Romfs(nca.OpenSection(1, false, ctx.Options.IntegrityLevel, true)); foreach (RomfsFile romfsFile in romfs.Files) { ctx.Logger.LogMessage(romfsFile.FullPath); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { NcaSection section = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (section == null) { ctx.Logger.LogMessage("NCA has no RomFS section"); return; } if (section.Type == SectionType.Bktr && ctx.Options.BaseNca == null) { ctx.Logger.LogMessage("Cannot save BKTR section without base RomFS"); return; } if (ctx.Options.RomfsOut != null) { nca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.RomfsOutDir != null) { var romfs = new Romfs(nca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel, true)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } } 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; } NcaSection section = nca.Sections[(int)ProgramPartitionType.Code]; if (section == null) { ctx.Logger.LogMessage("Could not find an ExeFS section"); return; } if (ctx.Options.ExefsOut != null) { nca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOutDir != null) { nca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.PlaintextOut != null) { nca.OpenDecryptedNca().WriteAllBytes(ctx.Options.PlaintextOut, ctx.Logger); } ctx.Logger.LogMessage(nca.Print()); } }
private void OnNandFound() { Nand nand = NANDService.NAND; FileStream pkg2file = HACGUIKeyset.TempPkg2FileInfo.Create(); IStorage pkg2nand = nand.OpenPackage2(0); byte[] pkg2raw = new byte[0x7FC000]; pkg2nand.Read(pkg2raw, 0x4000); MemoryStorage pkg2memory = new MemoryStorage(pkg2raw); pkg2memory.CopyToStream(pkg2file); Package2 pkg2 = new Package2(HACGUIKeyset.Keyset, pkg2memory); HACGUIKeyset.RootTempPkg2FolderInfo.Create(); FileStream kernelstream = HACGUIKeyset.TempKernelFileInfo.Create(); FileStream INI1stream = HACGUIKeyset.TempINI1FileInfo.Create(); pkg2.OpenKernel().CopyToStream(kernelstream); pkg2.OpenIni1().CopyToStream(INI1stream); kernelstream.Close(); INI1stream.Close(); Ini1 INI1 = new Ini1(pkg2.OpenIni1()); List <HashSearchEntry> hashes = new List <HashSearchEntry>(); Dictionary <byte[], byte[]> keys = new Dictionary <byte[], byte[]>(); HACGUIKeyset.RootTempINI1Folder.Create(); foreach (Kip kip in INI1.Kips) { Stream rodatastream, datastream; switch (kip.Header.Name) { case "FS": hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyApplicationSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyOceanSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeySystemSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKeySourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyApplicationSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyApplicationSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyOceanSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyOceanSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeySystemSourceHash], HACGUIKeyset.Keyset.KeyAreaKeySystemSource, 0x10); Array.Copy(keys[NintendoKeys.HeaderKekSourceHash], HACGUIKeyset.Keyset.HeaderKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKekSourceHash], HACGUIKeyset.Keyset.SaveMacKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKeySourceHash], HACGUIKeyset.Keyset.SaveMacKeySource, 0x10); hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); bool sdWarn = false; hashes.Add(new HashSearchEntry(NintendoKeys.SDCardKekSourceHash, 0x10)); try { keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.SDCardKekSourceHash], HACGUIKeyset.Keyset.SdCardKekSource, 0x10); } catch (EndOfStreamException) { MessageBox.Show("Failed to find SD card kek source! The NAND is probably from 1.0.0."); sdWarn = true; } if (!sdWarn) // don't try to find the rest of the keys if the other one couldn't be found { hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardSaveKeySourceHash, 0x20)); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardNcaKeySourceHash, 0x20)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.SDCardSaveKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[0], 0x20); Array.Copy(keys[NintendoKeys.SDCardNcaKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[1], 0x20); } hashes.Clear(); rodatastream.Close(); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKeySourceHash, 0x20)); datastream = new MemoryStream(kip.DecompressSection(2)); keys = datastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.HeaderKeySourceHash], HACGUIKeyset.Keyset.HeaderKeySource, 0x20); datastream.Close(); hashes.Clear(); break; case "spl": hashes.Add(new HashSearchEntry(NintendoKeys.AesKeyGenerationSourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.AesKeyGenerationSourceHash], HACGUIKeyset.Keyset.AesKeyGenerationSource, 0x10); rodatastream.Close(); hashes.Clear(); break; } FileStream kipstream = HACGUIKeyset.RootTempINI1Folder.GetFile(kip.Header.Name + ".kip").Create(); kip.OpenRawFile().CopyToStream(kipstream); kipstream.Close(); } pkg2file.Close(); INI1stream.Close(); HACGUIKeyset.Keyset.DeriveKeys(); SwitchFs fs = SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, NANDService.NAND.OpenSystemPartition()); foreach (KeyValuePair <string, Nca> kv in fs.Ncas) { Nca nca = kv.Value; if (nca.CanOpenSection(0)) // mainly a check if the NCA can be decrypted { switch (nca.Header.TitleId) { case 0x0100000000000033: // es switch (nca.Header.ContentType) { case ContentType.Program: NcaSection exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); IStorage pfsStorage = nca.OpenSection(exefsSection.SectionNum, false, IntegrityCheckLevel.ErrorOnInvalid, false); PartitionFileSystem pfs = new PartitionFileSystem(pfsStorage); Nso nso = new Nso(new FileStorage(pfs.OpenFile("main", OpenMode.Read))); NsoSection section = nso.Sections[1]; Stream data = new MemoryStream(section.DecompressSection()); hashes.Clear(); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekekSourceHash, 0x10)); keys = data.FindKeyViaHash(hashes, new SHA256Managed(), 0x10, data.Length); byte[] EticketRsaKekSource = new byte[0x10]; byte[] EticketRsaKekekSource = new byte[0x10]; Array.Copy(keys[NintendoKeys.EticketRsaKekSourceHash], EticketRsaKekSource, 0x10); Array.Copy(keys[NintendoKeys.EticketRsaKekekSourceHash], EticketRsaKekekSource, 0x10); byte[] RsaOaepKekGenerationSource; XOR(NintendoKeys.KekMasks[0], NintendoKeys.KekSeeds[3], out RsaOaepKekGenerationSource); byte[] key1 = new byte[0x10]; Crypto.DecryptEcb(HACGUIKeyset.Keyset.MasterKeys[0], RsaOaepKekGenerationSource, key1, 0x10); byte[] key2 = new byte[0x10]; Crypto.DecryptEcb(key1, EticketRsaKekekSource, key2, 0x10); Crypto.DecryptEcb(key2, EticketRsaKekSource, HACGUIKeyset.Keyset.EticketRsaKek, 0x10); break; } break; case 0x0100000000000024: // ssl switch (nca.Header.ContentType) { case ContentType.Program: NcaSection exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); IStorage pfsStorage = nca.OpenSection(exefsSection.SectionNum, false, IntegrityCheckLevel.ErrorOnInvalid, false); PartitionFileSystem pfs = new PartitionFileSystem(pfsStorage); Nso nso = new Nso(new FileStorage(pfs.OpenFile("main", OpenMode.Read))); NsoSection section = nso.Sections[1]; Stream data = new MemoryStream(section.DecompressSection()); hashes.Clear(); hashes.Add(new HashSearchEntry(NintendoKeys.SslAesKeyXHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SslRsaKeyYHash, 0x10)); keys = data.FindKeyViaHash(hashes, new SHA256Managed(), 0x10, data.Length); byte[] SslAesKeyX = new byte[0x10]; byte[] SslRsaKeyY = new byte[0x10]; Array.Copy(keys[NintendoKeys.SslAesKeyXHash], SslAesKeyX, 0x10); Array.Copy(keys[NintendoKeys.SslRsaKeyYHash], SslRsaKeyY, 0x10); byte[] RsaPrivateKekGenerationSource; XOR(NintendoKeys.KekMasks[0], NintendoKeys.KekSeeds[1], out RsaPrivateKekGenerationSource); byte[] key1 = new byte[0x10]; Crypto.DecryptEcb(HACGUIKeyset.Keyset.MasterKeys[0], RsaPrivateKekGenerationSource, key1, 0x10); byte[] key2 = new byte[0x10]; Crypto.DecryptEcb(key1, SslAesKeyX, key2, 0x10); Crypto.DecryptEcb(key2, SslRsaKeyY, HACGUIKeyset.Keyset.SslRsaKek, 0x10); break; } break; } } } // save PRODINFO to file, then derive eticket_ext_key_rsa Stream prodinfo = nand.OpenProdInfo(); Stream prodinfoFile = HACGUIKeyset.TempPRODINFOFileInfo.Create(); prodinfo.CopyTo(prodinfoFile); prodinfo.Close(); prodinfoFile.Seek(0, SeekOrigin.Begin); Calibration cal0 = new Calibration(prodinfoFile); HACGUIKeyset.Keyset.EticketExtKeyRsa = Crypto.DecryptRsaKey(cal0.EticketExtKeyRsa, HACGUIKeyset.Keyset.EticketRsaKek); // get client certificate prodinfo.Seek(0x0AD0, SeekOrigin.Begin); byte[] buffer; buffer = new byte[0x4]; prodinfo.Read(buffer, 0, buffer.Length); // read cert length uint certLength = BitConverter.ToUInt32(buffer, 0); buffer = new byte[certLength]; prodinfo.Seek(0x0AE0, SeekOrigin.Begin); // should be redundant? prodinfo.Read(buffer, 0, buffer.Length); // read actual cert byte[] counter = cal0.SslExtKey.Take(0x10).ToArray(); byte[] key = cal0.SslExtKey.Skip(0x10).ToArray(); // bit strange structure but it works new Aes128CtrTransform(HACGUIKeyset.Keyset.SslRsaKek, counter).TransformBlock(key); // decrypt private key X509Certificate2 certificate = new X509Certificate2(); certificate.Import(buffer); certificate.ImportPrivateKey(key); byte[] pfx = certificate.Export(X509ContentType.Pkcs12, "switch"); Stream pfxStream = HACGUIKeyset.GetClientCertificateByName(PickConsolePage.ConsoleName).Create(); pfxStream.Write(pfx, 0, pfx.Length); pfxStream.Close(); prodinfoFile.Close(); // get tickets List <Ticket> tickets = new List <Ticket>(); FatFileSystemProvider system = nand.OpenSystemPartition(); const string e1FileName = "save\\80000000000000E1"; const string e2FileName = "save\\80000000000000E2"; if (system.FileExists(e1FileName)) { IFile e1File = system.OpenFile(e1FileName, OpenMode.Read); IStorage e1Storage = new FileStorage(e1File); tickets.AddRange(DumpTickets(HACGUIKeyset.Keyset, e1Storage, PickConsolePage.ConsoleName)); } if (system.FileExists(e2FileName)) { IFile e2File = system.OpenFile(e2FileName, OpenMode.Read); IStorage e2Storage = new FileStorage(e2File); tickets.AddRange(DumpTickets(HACGUIKeyset.Keyset, e2Storage, PickConsolePage.ConsoleName)); } IStorage nsAppmanStorage = new FileStorage(system.OpenFile("save\\8000000000000043", OpenMode.Read)); SaveDataFileSystem save = new SaveDataFileSystem(HACGUIKeyset.Keyset, nsAppmanStorage, IntegrityCheckLevel.ErrorOnInvalid, false); IStorage privateStorage = new FileStorage(save.OpenFile("/private", OpenMode.Read)); byte[] sdSeed = new byte[0x10]; privateStorage.Read(sdSeed, 0x10); HACGUIKeyset.Keyset.SetSdSeed(sdSeed); foreach (Ticket ticket in tickets) { HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId] = new byte[0x10]; Array.Copy(ticket.GetTitleKey(HACGUIKeyset.Keyset), HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId], 0x10); } NANDService.Stop(); DirectoryInfo oldKeysDirectory = HACGUIKeyset.RootFolderInfo.GetDirectory("keys"); if (oldKeysDirectory.Exists) { oldKeysDirectory.Delete(true); // fix old versions after restructure of directory } // write all keys to file new SaveKeysetTask(PickConsolePage.ConsoleName).CreateTask().RunSynchronously(); Preferences.Current.DefaultConsoleName = PickConsolePage.ConsoleName; Preferences.Current.Write(); }
public void EnsureInitialized(ContentManager contentManager) { if (_fontData == null) { _device.Memory.FillWithZeros(_physicalAddress, Horizon.FontSize); uint fontOffset = 0; FontInfo CreateFont(string name) { if (contentManager.TryGetFontTitle(name, out long fontTitle)) { string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, ContentType.Data); string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(fontPath)) { int fileIndex = 0; //Use second file in Chinese Font title for standard if (name == "FontChineseSimplified") { fileIndex = 1; } byte[] data; using (FileStream ncaFileStream = new FileStream(fontPath, FileMode.Open, FileAccess.Read)) { Nca nca = new Nca(_device.System.KeySet, ncaFileStream.AsStorage(), false); NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); Romfs romfs = new Romfs(nca.OpenSection(romfsSection.SectionNum, false, _device.System.FsIntegrityCheckLevel, false)); Stream fontFile = romfs.OpenFile(romfs.Files[fileIndex]).AsStream(); data = DecryptFont(fontFile); } 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 { throw new InvalidSystemResourceException($"Font \"{name}.ttf\" not found. Please provide it in \"{_fontsPath}\"."); } } _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)."); } } }