// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include #include #include "filesystem.h" namespace skyline { namespace constant { constexpr size_t MediaUnitSize{0x200}; //!< The unit size of entries in an NCA } namespace vfs { enum class NcaContentType : u8 { Program = 0x0, //!< Program NCA Meta = 0x1, //!< Metadata NCA Control = 0x2, //!< Control NCA Manual = 0x3, //!< Manual NCA Data = 0x4, //!< Data NCA PublicData = 0x5, //!< Public data NCA }; /** * @brief The NCA class provides an easy way to access the contents of an Nintendo Content Archive * @url https://switchbrew.org/wiki/NCA_Format */ class NCA { private: enum class NcaDistributionType : u8 { System = 0x0, //!< This NCA was distributed on the EShop or is part of the system GameCard = 0x1, //!< This NCA was distributed on a GameCard }; /** * @brief The key generation version in NCAs before HOS 3.0.1 */ enum class NcaLegacyKeyGenerationType : u8 { Fw100 = 0x0, //!< 1.0.0 Fw300 = 0x2, //!< 3.0.0 }; /** * @brief The key generation version in NCAs after HOS 3.0.0, this is changed by Nintendo frequently */ enum class NcaKeyGenerationType : u8 { Fw301 = 0x3, //!< 3.0.1 Fw400 = 0x4, //!< 4.0.0 Fw500 = 0x5, //!< 5.0.0 Fw600 = 0x6, //!< 6.0.0 Fw620 = 0x7, //!< 6.2.0 Fw700 = 0x8, //!< 7.0.0 Fw810 = 0x9, //!< 8.1.0 Fw900 = 0xA, //!< 9.0.0 Fw910 = 0xB, //!< 9.1.0 Invalid = 0xFF, //!< An invalid key generation type }; enum class NcaKeyAreaEncryptionKeyType : u8 { Application = 0x0, //!< This NCA uses the application key encryption area Ocean = 0x1, //!< This NCA uses the ocean key encryption area System = 0x2, //!< This NCA uses the system key encryption area }; struct NcaFsEntry { u32 startOffset; //!< The start offset of the filesystem in units of 0x200 bytes u32 endOffset; //!< The start offset of the filesystem in units of 0x200 bytes u64 _pad_; }; enum class NcaSectionFsType : u8 { RomFs = 0x0, //!< This section contains a RomFs filesystem PFS0 = 0x1, //!< This section contains a PFS0 filesystem }; enum class NcaSectionHashType : u8 { HierarchicalSha256 = 0x2, //!< The hash header for this section is that of a PFS0 HierarchicalIntegrity = 0x3, //!< The hash header for this section is that of a RomFS }; enum class NcaSectionEncryptionType : u8 { None = 0x1, //!< This NCA doesn't use any encryption XTS = 0x2, //!< This NCA uses AES-XTS encryption CTR = 0x3, //!< This NCA uses AES-CTR encryption BKTR = 0x4, //!< This NCA uses BKTR together AES-CTR encryption }; /** * @brief The data for a single level of the hierarchical integrity scheme */ struct HierarchicalIntegrityLevel { u64 offset; //!< The offset of the level data u64 size; //!< The size of the level data u32 blockSize; //!< The block size of the level data u32 _pad_; }; static_assert(sizeof(HierarchicalIntegrityLevel) == 0x18); /** * @brief The hash info header of the hierarchical integrity scheme */ struct HierarchicalIntegrityHashInfo { u32 magic; //!< The hierarchical integrity magic, 'IVFC' u32 magicNumber; //!< The magic number 0x2000 u32 masterHashSize; //!< The size of the master hash u32 numLevels; //!< The number of levels std::array levels; //!< An array of the hierarchical integrity levels u8 _pad0_[0x20]; std::array masterHash; //!< The master hash of the hierarchical integrity system u8 _pad1_[0x18]; }; static_assert(sizeof(HierarchicalIntegrityHashInfo) == 0xF8); /** * @brief The hash info header of the SHA256 hashing scheme for PFS0 */ struct HierarchicalSha256HashInfo { std::array hashTableHash; //!< A SHA256 hash over the hash table u32 blockSize; //!< The block size of the filesystem u32 _pad_; u64 hashTableOffset; //!< The offset from the end of the section header of the hash table u64 hashTableSize; //!< The size of the hash table u64 pfs0Offset; //!< The offset from the end of the section header of the PFS0 u64 pfs0Size; //!< The size of the PFS0 u8 _pad1_[0xB0]; }; static_assert(sizeof(HierarchicalSha256HashInfo) == 0xF8); struct NcaSectionHeader { u16 version; //!< The version, always 2 NcaSectionFsType fsType; //!< The type of the filesystem in the section NcaSectionHashType hashType; //!< The type of hash header that is used for this section NcaSectionEncryptionType encryptionType; //!< The type of encryption that is used for this section u8 _pad0_[0x3]; union { HierarchicalIntegrityHashInfo integrityHashInfo; //!< The HashInfo used for RomFS HierarchicalSha256HashInfo sha256HashInfo; //!< The HashInfo used for PFS0 }; u8 _pad1_[0x40]; // PatchInfo u32 generation; //!< The generation of the NCA section u32 secureValue; //!< The secure value of the section u8 _pad2_[0x30]; //!< SparseInfo u8 _pad3_[0x88]; }; static_assert(sizeof(NcaSectionHeader) == 0x200); struct NcaHeader { std::array fixed_key_sig; //!< An RSA-PSS signature over the header with fixed key std::array npdm_key_sig; //!< An RSA-PSS signature over header with key in NPDM u32 magic; //!< The magic of the NCA: 'NCA3' NcaDistributionType distributionType; //!< Whether this NCA is from a gamecard or the E-Shop NcaContentType contentType; NcaLegacyKeyGenerationType legacyKeyGenerationType; //!< The keyblob to use for decryption NcaKeyAreaEncryptionKeyType keyAreaEncryptionKeyType; //!< The index of the key area encryption key that is needed u64 size; //!< The total size of the NCA u64 programId; u32 contentIndex; u32 sdkVersion; //!< The version of the SDK the NCA was built with NcaKeyGenerationType keyGenerationType; //!< The keyblob to use for decryption u8 fixedKeyGeneration; //!< The fixed key index u8 _pad0_[0xE]; std::array rightsId; std::array fsEntries; //!< The filesystem entries for this NCA std::array, 4> sectionHashes; //!< SHA-256 hashes for each filesystem header std::array, 4> encryptedKeyArea; //!< The encrypted key area for each filesystem u8 _pad1_[0xC0]; std::array sectionHeaders; }; static_assert(sizeof(NcaHeader) == 0xC00); std::shared_ptr backing; std::shared_ptr keyStore; bool encrypted{false}; bool rightsIdEmpty; bool useKeyArea; void ReadPfs0(const NcaSectionHeader §ionHeader, const NcaFsEntry &entry); void ReadRomFs(const NcaSectionHeader §ionHeader, const NcaFsEntry &entry); std::shared_ptr CreateBacking(const NcaSectionHeader §ionHeader, std::shared_ptr rawBacking, size_t offset); u8 GetKeyGeneration(); crypto::KeyStore::Key128 GetTitleKey(); crypto::KeyStore::Key128 GetKeyAreaKey(NcaSectionEncryptionType type); public: std::shared_ptr exeFs; //!< The PFS0 filesystem for this NCA's ExeFS section std::shared_ptr logo; //!< The PFS0 filesystem for this NCA's logo section std::shared_ptr cnmt; //!< The PFS0 filesystem for this NCA's CNMT section std::shared_ptr romFs; //!< The backing for this NCA's RomFS section NcaHeader header; //!< The header of the NCA NcaContentType contentType; //!< The content type of the NCA NCA(std::shared_ptr backing, std::shared_ptr keyStore, bool useKeyArea = false); }; } }