public static void AddIfExists(this List <NCCHRegion> list, NCCHRegion region) { if (region.Size != 0) { list.Add(region); } }
private void ExtractExeFS(Stream output, NCCHRegion region, Keyslot secondaryKeyslot, bool close = true) { if (secondaryKeyslot == Keyslot.NCCH) { //if the secondary keyslot is also the original 0x2C NCCH Keyslot, don't bother decrypting anything in parts, and just shove the entire ExeFS through a CryptoStream Tools.CryptFileStreamPart(this.NCCHMemoryMappedFile, output, new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)Keyslot.NCCH], region.CTR), region.Offset, region.Size, close); return; } //here we go, i don't like this part byte[] header = new byte[0x200]; using (MemoryMappedViewStream headerViewStream = this.NCCHMemoryMappedFile.CreateViewStream(region.Offset, 0x200)) { header = Tools.CryptBytes(headerViewStream.ReadBytes(0x200), new AesCtrCryptoTransform(this.Cryptor.NormalKey[0x2C], region.CTR)); } output.Write(header); //create dummy ExeFS class instance to figure out the locations of each file in the ExeFS ExeFS exefs = new ExeFS(header); //write decrypted header to output file foreach (ExeFSEntry entry in exefs.Entries) { byte[] CTR = (region.CTRInt + (entry.Offset / 16)).ToCTRBytes(); AesCtrCryptoTransform transform = (NormalCryptoExeFSFiles.Contains(entry.Name)) ? new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)Keyslot.NCCH], CTR) : new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)secondaryKeyslot], CTR); using (MemoryMappedViewStream fileViewStream = this.NCCHMemoryMappedFile.CreateViewStream(region.Offset + entry.Offset, entry.Size)) { CryptoStream cs = new CryptoStream(output, transform, CryptoStreamMode.Write); output.Seek(entry.Offset, SeekOrigin.Begin); fileViewStream.CopyTo(cs); cs.FlushFinalBlock(); } } //sneaky way to make it gm9-like if (!(output.GetType() == typeof(MemoryMappedViewStream))) { output.SetLength(Tools.RoundUp(output.Length, 0x200)); } if (close) { output.Dispose(); } }
public void ExtractSection(NCCHSection section, Stream outputStream, bool decrypt = true, bool close = true) { if (this.Info.Flags.UsesEncryption && this.Cryptor == null && decrypt == true) { throw new ArgumentException("This NCCH is encrypted, and no Crypto Engine was provided to perform decryption."); } if (!this.Regions.Any(region => region.Type == section)) { throw new ArgumentException($"The requested section ({Enum.GetName(typeof(NCCHSection), section)}) was not found inside this NCCH."); } NCCHRegion region = this.Regions.Find(region => region.Type == section); if (this.Info.Flags.UsesEncryption && decrypt && !NoCryptoSections.Contains(region.Type)) //if encrypted, user wants to decrypt, and the selected region is known to use encryption { Keyslot primaryKeyslot; Keyslot secondaryKeyslot; byte[] secondaryKeyY = new byte[16]; if (this.Info.Flags.UsesFixedKey) { primaryKeyslot = ((this.Info.TitleID & (0x10 << 32)) > 0) ? Keyslot.FixedSystemKey : Keyslot.ZeroKey; secondaryKeyslot = primaryKeyslot; } else { primaryKeyslot = Keyslot.NCCH; secondaryKeyslot = secondaryKeyslot = this.Info.Flags.CryptoMethod switch { 0x00 => Keyslot.NCCH, 0x01 => Keyslot.NCCH70, 0x0A => Keyslot.NCCH93, 0x0B => Keyslot.NCCH96, _ => throw new Exception("Could not determine crypto type. This should NOT happen.") }; if (this.Info.Flags.UsesSeed) { this.Info.LoadSeed(this.SeedDB); secondaryKeyY = this.Info.SeededKeyY; } else { secondaryKeyY = this.Info.KeyY; } } this.Cryptor.SetKeyslot("y", (int)primaryKeyslot, this.Info.KeyY.ToUnsignedBigInt()); //load primary key into keyslot this.Cryptor.SetKeyslot("y", (int)secondaryKeyslot, secondaryKeyY.ToUnsignedBigInt()); //load secondary key into keyslot //decrypts based on section switch (region.Type) { case NCCHSection.ExHeader: Tools.CryptFileStreamPart(this.NCCHMemoryMappedFile, outputStream, new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)primaryKeyslot], region.CTR), region.Offset, region.Size, close); break; case NCCHSection.RomFS: //uses secondary keyslot for it's entirety Tools.CryptFileStreamPart(this.NCCHMemoryMappedFile, outputStream, new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)secondaryKeyslot], region.CTR), region.Offset, region.Size, close); break; case NCCHSection.ExeFS: //can use two keyslots. because of this, I separated the ExeFS decryption into another method ExtractExeFS(outputStream, region, secondaryKeyslot, close); break; } } else //just extract the raw section, because no crypto is used or the user did not request decryption { Tools.ExtractFileStreamPart(this.NCCHMemoryMappedFile, outputStream, region.Offset, region.Size, close); } }