public IStorage OpenDecryptedHeaderStorage() { long firstSectionOffset = long.MaxValue; bool hasEnabledSection = false; // Encrypted portion continues until the first section for (int i = 0; i < NcaHeader.SectionCount; i++) { if (Header.IsSectionEnabled(i)) { hasEnabledSection = true; firstSectionOffset = Math.Min(firstSectionOffset, Header.GetSectionStartOffset(i)); } } long headerSize = hasEnabledSection ? NcaHeader.HeaderSize : firstSectionOffset; IStorage header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, headerSize), Keyset.HeaderKey, NcaHeader.HeaderSectorSize, true), 1, true); int version = ReadHeaderVersion(header); if (version == 2) { header = OpenNca2Header(headerSize); } return(header); }
public void Open() { DiskInfo disk = SelectedDisk; var storage = new CachedStorage(new DeviceStream(disk.PhysicalName, disk.Length).AsStorage(), disk.SectorSize * 100, 4, true); storage.SetReadOnly(); Stream stream = storage.AsStream(); Keyset keyset = OpenKeyset(); var nand = new Nand(stream, keyset); Stream prodinfo = nand.OpenProdInfo(); var calibration = new Calibration(prodinfo); keyset.EticketExtKeyRsa = Crypto.DecryptRsaKey(calibration.EticketExtKeyRsa, keyset.EticketRsaKek); Ticket[] tickets = GetTickets(keyset, nand); using (var outStream = new StreamWriter("titlekeys.txt")) { foreach (Ticket ticket in tickets) { byte[] key = ticket.GetTitleKey(keyset); outStream.WriteLine($"{ticket.RightsId.ToHexString()},{key.ToHexString()}"); } } }
private static void Refresh() { ManagementObjectCollection disks = GetDisks(); foreach (ManagementObject disk in disks) { if (disk["Model"].ToString() == "Linux UMS disk 0 USB Device") // probably a bad way of filtering? { try { DiskInfo info = CreateDiskInfo(disk); IEnumerable <PartitionInfo> partitions = CreatePartitionInfos(GetPartitions()).Where((p) => info.Index == p.DiskIndex).OrderBy((p) => p.Index); PartitionInfo lastPartition = partitions.Last(); long length = (long)(lastPartition.Size + lastPartition.StartingOffset); long missingLength = (0x747BFFE00 - 0x727800000) + 0x200; // (start of GPT backup - end of USER) + length of GPT backup length += missingLength; IStorage diskStorage = new CachedStorage(new DeviceStream(info.PhysicalName, length).AsStorage().AsReadOnly(), info.SectorSize * 100, 4, true); if (InsertNAND(diskStorage, true)) { CurrentDisk = info; } } catch (UnauthorizedAccessException) { Console.WriteLine("Cannot direct access drive due to lack of permissions."); } } } }
private IStorage OpenNca0BodyStorage(bool openEncrypted) { // NCA0 encrypts the entire NCA body using AES-XTS instead of // using different encryption types and IVs for each section. Assert.SdkEqual(0, Header.Version); if (openEncrypted == IsEncrypted) { return(GetRawStorage()); } if (Nca0TransformedBody != null) { return(Nca0TransformedBody); } byte[] key0 = GetContentKey(NcaKeyType.AesXts0); byte[] key1 = GetContentKey(NcaKeyType.AesXts1); Nca0TransformedBody = new CachedStorage(new Aes128XtsStorage(GetRawStorage(), key0, key1, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); return(Nca0TransformedBody); IStorage GetRawStorage() { BaseStorage.GetSize(out long ncaSize).ThrowIfFailure(); return(BaseStorage.Slice(0x400, ncaSize - 0x400)); } }
private IStorage OpenAesCtrExStorage(IStorage baseStorage, int index) { NcaFsHeader fsHeader = Header.GetFsHeader(index); NcaFsPatchInfo info = fsHeader.GetPatchInfo(); long sectionOffset = Header.GetSectionStartOffset(index); long sectionSize = Header.GetSectionSize(index); long bktrOffset = info.RelocationTreeOffset; long bktrSize = sectionSize - bktrOffset; long dataSize = info.RelocationTreeOffset; byte[] key = GetContentKey(NcaKeyType.AesCtr); byte[] counter = Aes128CtrStorage.CreateCounter(fsHeader.Counter, bktrOffset + sectionOffset); byte[] counterEx = Aes128CtrStorage.CreateCounter(fsHeader.Counter, sectionOffset); IStorage bucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true); IStorage encryptionBucketTreeData = bucketTreeData.Slice(info.EncryptionTreeOffset - bktrOffset); IStorage decStorage = new Aes128CtrExStorage(baseStorage.Slice(0, dataSize), encryptionBucketTreeData, key, counterEx, true); decStorage = new CachedStorage(decStorage, 0x4000, 4, true); return(new ConcatenationStorage(new[] { decStorage, bucketTreeData }, true)); }
public Stream OpenProdInfo() { IStorage encStorage = ProdInfo.Open().AsStorage(); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[0], 0x4000, true), 0x4000, 4, true); return(decStorage.AsStream(FileAccess.Read)); }
public IStorage OpenDecryptedWarmBootStorage() { if (!IsDecrypted) { return(null); } IStorage warmBootStorage = OpenWarmBootStorage(); // Only Mariko warmboot storage is encrypted if (!IsMariko) { return(warmBootStorage); } int size = GetSectionSize(Package1Section.WarmBoot); int encryptedSectionSize = size - MarikoWarmBootPlainTextSectionSize; var plainTextSection = new SubStorage(warmBootStorage, 0, MarikoWarmBootPlainTextSectionSize); var encryptedSubStorage = new SubStorage(warmBootStorage, MarikoWarmBootPlainTextSectionSize, encryptedSectionSize); var zeroIv = new Buffer16(); IStorage decryptedSection = new AesCbcStorage(encryptedSubStorage, KeySet.MarikoBek, zeroIv.Bytes, true); decryptedSection = new CachedStorage(decryptedSection, 0x200, 1, true); return(new ConcatenationStorage(new List <IStorage> { plainTextSection, decryptedSection }, true)); }
public FatFileSystemProvider OpenUserPartition() { IStorage encStorage = User.Open().AsStorage(); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[3], 0x4000, true), 0x4000, 4, true); var fat = new FatFileSystem(decStorage.AsStream(FileAccess.Read), Ownership.None); return(new FatFileSystemProvider(fat)); }
public Nax0(Keyset keyset, IStorage storage, string sdPath, bool leaveOpen) { LeaveOpen = leaveOpen; ReadHeader(storage.AsStream()); DeriveKeys(keyset, sdPath, storage); BaseStorage = new CachedStorage(new Aes128XtsStorage(storage.Slice(SectorSize), Key, SectorSize, leaveOpen), 4, leaveOpen); }
public NandPartition OpenUserPartition() { IStorage encStorage = User.Open().AsStorage(); var decStorage = new CachedStorage(new Aes128XtsStorage(encStorage, Keyset.BisKeys[3], 0x4000, true), 0x4000, 4, true); decStorage.SetReadOnly(); var fat = new FatFileSystem(decStorage.AsStream(), Ownership.None); return(new NandPartition(fat)); }
public void PostNullIfnformationToCacheStorage() { var cache = new CachedStorage(); cache.SetValueById("1", null); object result; cache.TryGetValueById("1", out result); result.Should().BeNull(); }
public void GetfnformationFromEmptyCacheStorage() { var cache = new CachedStorage(); object result; cache.TryGetValueById("1", out result); result.Should().BeNull(); }
public void PostInformationWithNullKeyToCacheStorage() { var cache = new CachedStorage(); cache.SetValueById(null, null); object result; cache.TryGetValueById(null, out result); result.Should().BeNull(); }
public void PostInformationAndGetInformationWithDifferentKeys() { var cache = new CachedStorage(); cache.SetValueById("1", null); object result; cache.TryGetValueById("2", out result); result.Should().BeNull(); }
public void PostIfnformationToCacheStorage() { var cache = new CachedStorage(); object inf = new Byte[1, 3, 4, 5, 6]; cache.SetValueById("1", inf); object result; cache.TryGetValueById("1", out result); result.Should().Be(inf); }
public static void Process(Context ctx) { switch (ctx.Options.BenchType?.ToLower()) { case "aesctr": { IStorage decStorage = new MemoryStorage(new byte[Size]); IStorage encStorage = new Aes128CtrStorage(new MemoryStorage(new byte[Size]), new byte[0x10], new byte[0x10], true); CopyBenchmark(decStorage, encStorage, Iterations, "MemoryStorage Encrypt: ", ctx.Logger); CopyBenchmark(encStorage, decStorage, Iterations, "MemoryStorage Decrypt: ", ctx.Logger); decStorage = new NullStorage(Size); encStorage = new Aes128CtrStorage(new NullStorage(Size), new byte[0x10], new byte[0x10], true); CopyBenchmark(decStorage, encStorage, Iterations, "NullStorage Encrypt: ", ctx.Logger); CopyBenchmark(encStorage, decStorage, Iterations, "NullStorage Decrypt: ", ctx.Logger); decStorage = new MemoryStorage(new byte[Size]); encStorage = new CachedStorage(new Aes128CtrStorage(new MemoryStorage(new byte[Size]), new byte[0x10], new byte[0x10], true), 0x4000, 4, true); CopyBenchmark(decStorage, encStorage, Iterations, "CachedStorage Encrypt: ", ctx.Logger); CopyBenchmark(encStorage, decStorage, Iterations, "CachedStorage Decrypt: ", ctx.Logger); break; } case "aesxts": { IStorage decStorage = new MemoryStorage(new byte[Size]); IStorage encStorage = new Aes128XtsStorage(new MemoryStorage(new byte[Size]), new byte[0x20], 81920, true); CopyBenchmark(decStorage, encStorage, Iterations, "MemoryStorage Encrypt: ", ctx.Logger); CopyBenchmark(encStorage, decStorage, Iterations, "MemoryStorage Decrypt: ", ctx.Logger); decStorage = new NullStorage(Size); encStorage = new Aes128XtsStorage(new NullStorage(Size), new byte[0x20], 81920, true); CopyBenchmark(decStorage, encStorage, Iterations, "NullStorage Encrypt: ", ctx.Logger); CopyBenchmark(encStorage, decStorage, Iterations, "NullStorage Decrypt: ", ctx.Logger); decStorage = new MemoryStorage(new byte[Size]); encStorage = new CachedStorage(new Aes128XtsStorage(new MemoryStorage(new byte[Size]), new byte[0x20], 0x4000, true), 4, true); CopyBenchmark(decStorage, encStorage, Iterations, "CachedStorage Encrypt: ", ctx.Logger); CopyBenchmark(encStorage, decStorage, Iterations, "CachedStorage Decrypt: ", ctx.Logger); break; } default: ctx.Logger.LogMessage("Unknown benchmark type."); return; } }
private Result SetPk11Storage() { // Read the PK11 header from the body storage int pk11Offset = IsModern ? ModernStage1Size : LegacyStage1Size; Result rc = BodyStorage.Read(pk11Offset, SpanHelpers.AsByteSpan(ref _pk11Header)); if (rc.IsFailure()) { return(rc); } // Check if PK11 is already decrypted, creating the PK11 storage if it is IsDecrypted = _pk11Header.Magic == Package1Pk11Header.ExpectedMagic; if (IsDecrypted) { Pk11Storage = new SubStorage(BodyStorage, pk11Offset, Pk11Size); return(Result.Success); } var encPk11Storage = new SubStorage(BodyStorage, pk11Offset, Pk11Size); // See if we have an Erista package1 key that can decrypt this PK11 if (!IsMariko && TryFindEristaKeyRevision()) { IsDecrypted = true; IStorage decPk11Storage; if (IsModern) { decPk11Storage = new AesCbcStorage(encPk11Storage, KeySet.Package1Keys[KeyRevision], _stage1Footer.Iv, true); } else { decPk11Storage = new Aes128CtrStorage(encPk11Storage, KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ToArray(), true); } Pk11Storage = new CachedStorage(decPk11Storage, 0x4000, 1, true); return(Result.Success); } // We can't decrypt the PK11. Set Pk11Storage to the encrypted PK11 storage Pk11Storage = encPk11Storage; return(Result.Success); }
private IStorage OpenAesCtrExStorage(IStorage baseStorage, int index, bool decrypting) { NcaFsHeader fsHeader = GetFsHeader(index); NcaFsPatchInfo info = fsHeader.GetPatchInfo(); long sectionOffset = Header.GetSectionStartOffset(index); long sectionSize = Header.GetSectionSize(index); long bktrOffset = info.RelocationTreeOffset; long bktrSize = sectionSize - bktrOffset; long dataSize = info.RelocationTreeOffset; byte[] key = GetContentKey(NcaKeyType.AesCtr); byte[] counter = Aes128CtrStorage.CreateCounter(fsHeader.Counter, bktrOffset + sectionOffset); byte[] counterEx = Aes128CtrStorage.CreateCounter(fsHeader.Counter, sectionOffset); IStorage bucketTreeData; IStorage outputBucketTreeData; if (decrypting) { bucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true); outputBucketTreeData = bucketTreeData; } else { bucketTreeData = baseStorage.Slice(bktrOffset, bktrSize); outputBucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true); } var encryptionBucketTreeData = new SubStorage(bucketTreeData, info.EncryptionTreeOffset - bktrOffset, sectionSize - info.EncryptionTreeOffset); var cachedBucketTreeData = new CachedStorage(encryptionBucketTreeData, IndirectStorage.NodeSize, 6, true); var treeHeader = new BucketTree.Header(); info.EncryptionTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader)); long nodeStorageSize = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount); long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount); var tableNodeStorage = new SubStorage(cachedBucketTreeData, 0, nodeStorageSize); var tableEntryStorage = new SubStorage(cachedBucketTreeData, nodeStorageSize, entryStorageSize); IStorage decStorage = new Aes128CtrExStorage(baseStorage.Slice(0, dataSize), tableNodeStorage, tableEntryStorage, treeHeader.EntryCount, key, counterEx, true); return(new ConcatenationStorage(new[] { decStorage, outputBucketTreeData }, true)); }
public Package2Header(IStorage storage, Keyset keyset, int keyGeneration) { var reader = new BinaryReader(storage.AsStream()); byte[] key = keyset.Package2Keys[keyGeneration]; Signature = reader.ReadBytes(0x100); byte[] sigData = reader.ReadBytes(0x100); SignatureValidity = CryptoOld.Rsa2048PssVerify(sigData, Signature, keyset.Package2FixedKeyModulus); reader.BaseStream.Position -= 0x100; Counter = reader.ReadBytes(0x10); Stream headerStream = new CachedStorage(new Aes128CtrStorage(storage.Slice(0x100), key, Counter, true), 0x4000, 4, true).AsStream(); headerStream.Position = 0x10; reader = new BinaryReader(headerStream); for (int i = 0; i < 4; i++) { SectionCounters[i] = reader.ReadBytes(0x10); } Magic = reader.ReadAscii(4); BaseOffset = reader.ReadInt32(); reader.BaseStream.Position += 4; VersionMax = reader.ReadByte(); VersionMin = reader.ReadByte(); reader.BaseStream.Position += 2; for (int i = 0; i < 4; i++) { SectionSizes[i] = reader.ReadInt32(); } for (int i = 0; i < 4; i++) { SectionOffsets[i] = reader.ReadInt32(); } for (int i = 0; i < 4; i++) { SectionHashes[i] = reader.ReadBytes(0x20); } }
public IStorage OpenHeaderStorage(bool openEncrypted) { long firstSectionOffset = long.MaxValue; bool hasEnabledSection = false; // Encrypted portion continues until the first section for (int i = 0; i < NcaHeader.SectionCount; i++) { if (Header.IsSectionEnabled(i)) { hasEnabledSection = true; firstSectionOffset = Math.Min(firstSectionOffset, Header.GetSectionStartOffset(i)); } } long headerSize = hasEnabledSection ? firstSectionOffset : NcaHeader.HeaderSize; IStorage rawHeaderStorage = BaseStorage.Slice(0, headerSize); if (openEncrypted == IsEncrypted) { return(rawHeaderStorage); } IStorage header; switch (Header.Version) { case 3: header = new CachedStorage(new Aes128XtsStorage(rawHeaderStorage, KeySet.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); break; case 2: header = OpenNca2Header(headerSize, !openEncrypted); break; case 0: header = new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, 0x400), KeySet.HeaderKey, NcaHeader.HeaderSectorSize, true, !openEncrypted), 1, true); break; default: throw new NotSupportedException("Unsupported NCA version"); } return(header); }
public static void ProcessPk21(Context ctx) { using (var file = new CachedStorage(new LocalStorage(ctx.Options.InFile, FileAccess.Read), 0x4000, 4, false)) { var package2 = new Package2(ctx.Keyset, file); ctx.Logger.LogMessage(package2.Print()); string outDir = ctx.Options.OutDir; if (outDir != null) { Directory.CreateDirectory(outDir); package2.OpenKernel().WriteAllBytes(Path.Combine(outDir, "Kernel.bin"), ctx.Logger); package2.OpenIni1().WriteAllBytes(Path.Combine(outDir, "INI1.bin"), ctx.Logger); package2.OpenDecryptedPackage().WriteAllBytes(Path.Combine(outDir, "Decrypted.bin"), ctx.Logger); } } }
public static void ProcessPk21(Context ctx) { using (var file = new CachedStorage(new LocalStorage(ctx.Options.InFile, FileAccess.Read), 0x4000, 4, false)) { var package2 = new Package2StorageReader(); package2.Initialize(ctx.Keyset, file).ThrowIfFailure(); ctx.Logger.LogMessage(package2.Print()); string outDir = ctx.Options.OutDir; string iniDir = ctx.Options.Ini1OutDir; if (iniDir == null && ctx.Options.ExtractIni1) { iniDir = Path.Combine(outDir, "INI1"); } if (outDir != null) { Directory.CreateDirectory(outDir); package2.OpenPayload(out IStorage kernelStorage, 0).ThrowIfFailure(); kernelStorage.WriteAllBytes(Path.Combine(outDir, "Kernel.bin"), ctx.Logger); package2.OpenIni(out IStorage ini1Storage).ThrowIfFailure(); ini1Storage.WriteAllBytes(Path.Combine(outDir, "INI1.bin"), ctx.Logger); package2.OpenDecryptedPackage(out IStorage decPackageStorage).ThrowIfFailure(); decPackageStorage.WriteAllBytes(Path.Combine(outDir, "Decrypted.bin"), ctx.Logger); } if (iniDir != null) { Directory.CreateDirectory(iniDir); package2.OpenIni(out IStorage ini1Storage).ThrowIfFailure(); ProcessKip.ExtractIni1(ini1Storage, iniDir); } } }
public IStorage OpenRawStorageWithPatch(Nca patchNca, int index) { IStorage patchStorage = patchNca.OpenRawStorage(index); IStorage baseStorage = SectionExists(index) ? OpenRawStorage(index) : new NullStorage(); patchStorage.GetSize(out long patchSize).ThrowIfFailure(); baseStorage.GetSize(out long baseSize).ThrowIfFailure(); NcaFsHeader header = patchNca.Header.GetFsHeader(index); NcaFsPatchInfo patchInfo = header.GetPatchInfo(); if (patchInfo.RelocationTreeSize == 0) { return(patchStorage); } var treeHeader = new BucketTree.Header(); patchInfo.RelocationTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader)); long nodeStorageSize = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount); long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount); var relocationTableStorage = new SubStorage(patchStorage, patchInfo.RelocationTreeOffset, patchInfo.RelocationTreeSize); var cachedTableStorage = new CachedStorage(relocationTableStorage, IndirectStorage.NodeSize, 4, true); var tableNodeStorage = new SubStorage(cachedTableStorage, 0, nodeStorageSize); var tableEntryStorage = new SubStorage(cachedTableStorage, nodeStorageSize, entryStorageSize); var storage = new IndirectStorage(); storage.Initialize(tableNodeStorage, tableEntryStorage, treeHeader.EntryCount).ThrowIfFailure(); storage.SetStorage(0, baseStorage, 0, baseSize); storage.SetStorage(1, patchStorage, 0, patchSize); return(storage); }
private static void Refresh() { ManagementObjectCollection disks = GetDisks(); foreach (ManagementObject disk in disks) { if (disk["Model"].ToString() == "Linux UMS disk 0 USB Device") // probably a bad way of filtering? { try { DiskInfo info = CreateDiskInfo(disk); IStorage diskStorage = new CachedStorage(new DeviceStream(info.PhysicalName, info.Length).AsStorage().AsReadOnly(), info.SectorSize * 100, 4, true); if (InsertNAND(diskStorage, true)) { CurrentDisk = info; } } catch (UnauthorizedAccessException) { Console.WriteLine("Cannot direct access drive due to lack of permissions."); } } } }
private static void Refresh() { foreach (DiskInfo info in CreateDiskInfos(GetDisks())) { if (info.Model == "Linux UMS disk 0 USB Device" && info.Partitions > 1) // probably a bad way of filtering? { try { IEnumerable <PartitionInfo> partitions = CreatePartitionInfos(GetPartitions()) .Where((p) => info.Index == p.DiskIndex) .OrderBy((p) => p.Index); if (!partitions.Any()) { continue; // obv the NAND should have *some* partitions } PartitionInfo lastPartition = partitions.Last(); long length = (long)(lastPartition.Size + lastPartition.StartingOffset); // thx windows for ignoring the GPT backup AND reporting the size of the disk incorrectly... long missingLength = (0x747BFFE00 - 0x727800000) + 0x200; // (start of GPT backup - end of USER) + length of GPT backup length += missingLength; DeviceStream stream = new DeviceStream(info.PhysicalName, length); IStorage diskStorage = new CachedStorage(stream.AsStorage().AsReadOnly(), info.SectorSize * 100, 4, true); if (InsertNAND(diskStorage, true)) { CurrentDisk = info; break; } } catch (UnauthorizedAccessException) { MessageBox.Show("Cannot get direct access drive due to lack of permissions.\nReopen HACGUI as administrator to use the plugged in NAND."); } } } }
/// <summary> /// Opens a decrypted <see cref="IStorage"/> of one of the payloads in the package. /// </summary> /// <param name="payloadStorage">If the method returns successfully, contains an <see cref="IStorage"/> /// of the specified payload.</param> /// <param name="index">The index of the payload to get. Must me less than <see cref="Package2Header.PayloadCount"/></param> /// <returns>The <see cref="Result"/> of the operation.</returns> public Result OpenPayload(out IStorage payloadStorage, int index) { UnsafeHelpers.SkipParamInit(out payloadStorage); if ((uint)index >= Package2Header.PayloadCount) { return(ResultLibHac.ArgumentOutOfRange.Log()); } int offset = _header.Meta.GetPayloadFileOffset(index); int size = (int)_header.Meta.PayloadSizes[index]; var payloadSubStorage = new SubStorage(_storage, offset, size); if (size == 0) { payloadStorage = payloadSubStorage; return(Result.Success); } byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray(); payloadStorage = new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true); return(Result.Success); }
private static bool FindEmuMMC(DriveInfo drive) { DirectoryInfo root = drive.RootDirectory; FileInfo emummcIni = root.GetFile("emuMMC/emummc.ini"); if (emummcIni.Exists) { // find the DiskDrive associated with this drive letter VolumeInfo volume = AllVolumes .Where(x => x.Caption == drive.Name).FirstOrDefault(); LogicalDiskInfo logicalDisk = AllLogicalDisks .Where(x => x.DeviceID == volume.DriveLetter).FirstOrDefault(); IEnumerable <PartitionInfo> partitionsFromLogicalDisk = ToDiskPartitions(logicalDisk); if (!partitionsFromLogicalDisk.Any()) { return(false); } DiskInfo disk = AllDisks.Where(x => x.Index == partitionsFromLogicalDisk.First().DiskIndex).FirstOrDefault(); IEnumerable <PartitionInfo> partitions = AllPartitions.Where(x => x.DiskIndex == disk.Index); // parse ini FileIniDataParser parser = new FileIniDataParser(); IniData ini = parser.ReadFile(emummcIni.FullName); ini.SectionKeySeparator = '/'; if (!ini.TryGetKey("emummc/sector", out string sectorStr)) { return(false); } ulong sector = ulong.Parse(sectorStr.Replace("0x", ""), System.Globalization.NumberStyles.HexNumber); PartitionInfo partition = partitions.Where(x => (sector * x.BlockSize) - 0x1000000 /* hekate's 16MB padding to protect the emuMMC */ == x.StartingOffset).FirstOrDefault(); bool usingEmummc = partition != null; if (usingEmummc) { MessageBoxResult r = MessageBox.Show("emuMMC was detected on this SD card. Do you want to open that instead of sysMMC content?", "emuMMC", MessageBoxButton.YesNo); if (r == MessageBoxResult.No) { usingEmummc = false; } } if (usingEmummc) { DeviceStream stream = new DeviceStream(disk.PhysicalName, disk.Length); IStorage diskStorage = new CachedStorage(stream.AsStorage().AsReadOnly(), disk.SectorSize * 100, 4, true); long offset = (long)partition.StartingOffset; offset += 0x1000000; // account for hekate's padding offset += 0x400000; // BOOT0 offset += 0x400000; // BOOT1 NANDService.InsertNAND(diskStorage.Slice(offset, (long)partition.Size), false); } return(usingEmummc); } return(false); }