mirror of
				https://github.com/Atmosphere-NX/Atmosphere-libs.git
				synced 2025-11-03 21:01:17 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			331 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) Atmosphère-NX
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms and conditions of the GNU General Public License,
 | 
						|
 * version 2, as published by the Free Software Foundation.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope it will be useful, but WITHOUT
 | 
						|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | 
						|
 * more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
#pragma once
 | 
						|
#include <vapours.hpp>
 | 
						|
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
 | 
						|
 | 
						|
namespace ams::fssystem {
 | 
						|
 | 
						|
    /* ACCURATE_TO_VERSION: 14.3.0.0 */
 | 
						|
 | 
						|
    struct Hash {
 | 
						|
        static constexpr size_t Size = IHash256Generator::HashSize;
 | 
						|
        u8 value[Size];
 | 
						|
    };
 | 
						|
    static_assert(sizeof(Hash) == Hash::Size);
 | 
						|
    static_assert(util::is_pod<Hash>::value);
 | 
						|
 | 
						|
    using NcaDigest = Hash;
 | 
						|
 | 
						|
    struct NcaHeader {
 | 
						|
        enum class ContentType : u8 {
 | 
						|
            Program    = 0,
 | 
						|
            Meta       = 1,
 | 
						|
            Control    = 2,
 | 
						|
            Manual     = 3,
 | 
						|
            Data       = 4,
 | 
						|
            PublicData = 5,
 | 
						|
 | 
						|
            Start = Program,
 | 
						|
            End   = PublicData,
 | 
						|
        };
 | 
						|
 | 
						|
        enum class DistributionType : u8 {
 | 
						|
            Download = 0,
 | 
						|
            GameCard = 1,
 | 
						|
 | 
						|
            Start = Download,
 | 
						|
            End   = GameCard,
 | 
						|
        };
 | 
						|
 | 
						|
        enum class EncryptionType : u8 {
 | 
						|
            Auto = 0,
 | 
						|
            None = 1,
 | 
						|
        };
 | 
						|
 | 
						|
        enum DecryptionKey {
 | 
						|
            DecryptionKey_AesXts   = 0,
 | 
						|
            DecryptionKey_AesXts1  = DecryptionKey_AesXts,
 | 
						|
            DecryptionKey_AesXts2  = 1,
 | 
						|
            DecryptionKey_AesCtr   = 2,
 | 
						|
            DecryptionKey_AesCtrEx = 3,
 | 
						|
            DecryptionKey_AesCtrHw = 4,
 | 
						|
            DecryptionKey_Count,
 | 
						|
        };
 | 
						|
 | 
						|
        struct FsInfo {
 | 
						|
            u32 start_sector;
 | 
						|
            u32 end_sector;
 | 
						|
            u32 hash_sectors;
 | 
						|
            u32 reserved;
 | 
						|
        };
 | 
						|
        static_assert(sizeof(FsInfo) == 0x10);
 | 
						|
        static_assert(util::is_pod<FsInfo>::value);
 | 
						|
 | 
						|
        static constexpr u32 Magic0 = util::FourCC<'N','C','A','0'>::Code;
 | 
						|
        static constexpr u32 Magic1 = util::FourCC<'N','C','A','1'>::Code;
 | 
						|
        static constexpr u32 Magic2 = util::FourCC<'N','C','A','2'>::Code;
 | 
						|
        static constexpr u32 Magic3 = util::FourCC<'N','C','A','3'>::Code;
 | 
						|
 | 
						|
        static constexpr u32 Magic              = Magic3;
 | 
						|
 | 
						|
        static constexpr size_t Size                             = 1_KB;
 | 
						|
        static constexpr s32    FsCountMax                       = 4;
 | 
						|
        static constexpr size_t HeaderSignCount                  = 2;
 | 
						|
        static constexpr size_t HeaderSignSize                   = 0x100;
 | 
						|
        static constexpr size_t EncryptedKeyAreaSize             = 0x100;
 | 
						|
        static constexpr size_t SectorSize                       = 0x200;
 | 
						|
        static constexpr size_t SectorShift                      = 9;
 | 
						|
        static constexpr size_t RightsIdSize                     = 0x10;
 | 
						|
        static constexpr size_t XtsBlockSize                     = 0x200;
 | 
						|
        static constexpr size_t CtrBlockSize                     = 0x10;
 | 
						|
 | 
						|
        static_assert(SectorSize == (1 << SectorShift));
 | 
						|
 | 
						|
        /* Data members. */
 | 
						|
        u8 header_sign_1[HeaderSignSize];
 | 
						|
        u8 header_sign_2[HeaderSignSize];
 | 
						|
        u32 magic;
 | 
						|
        DistributionType distribution_type;
 | 
						|
        ContentType content_type;
 | 
						|
        u8 key_generation;
 | 
						|
        u8 key_index;
 | 
						|
        u64 content_size;
 | 
						|
        u64 program_id;
 | 
						|
        u32 content_index;
 | 
						|
        u32 sdk_addon_version;
 | 
						|
        u8 key_generation_2;
 | 
						|
        u8 header1_signature_key_generation;
 | 
						|
        u8 reserved_222[2];
 | 
						|
        u32 reserved_224[3];
 | 
						|
        u8 rights_id[RightsIdSize];
 | 
						|
        FsInfo fs_info[FsCountMax];
 | 
						|
        Hash fs_header_hash[FsCountMax];
 | 
						|
        u8 encrypted_key_area[EncryptedKeyAreaSize];
 | 
						|
 | 
						|
        static constexpr u64 SectorToByte(u32 sector) {
 | 
						|
            return static_cast<u64>(sector) << SectorShift;
 | 
						|
        }
 | 
						|
 | 
						|
        static constexpr u32 ByteToSector(u64 byte) {
 | 
						|
            return static_cast<u32>(byte >> SectorShift);
 | 
						|
        }
 | 
						|
 | 
						|
        u8 GetProperKeyGeneration() const;
 | 
						|
    };
 | 
						|
    static_assert(sizeof(NcaHeader) == NcaHeader::Size);
 | 
						|
    static_assert(util::is_pod<NcaHeader>::value);
 | 
						|
 | 
						|
    struct NcaBucketInfo {
 | 
						|
        static constexpr size_t HeaderSize = 0x10;
 | 
						|
        fs::Int64 offset;
 | 
						|
        fs::Int64 size;
 | 
						|
        u8 header[HeaderSize];
 | 
						|
    };
 | 
						|
    static_assert(util::is_pod<NcaBucketInfo>::value);
 | 
						|
 | 
						|
    struct NcaPatchInfo {
 | 
						|
        static constexpr size_t Size   = 0x40;
 | 
						|
        static constexpr size_t Offset = 0x100;
 | 
						|
 | 
						|
        fs::Int64 indirect_offset;
 | 
						|
        fs::Int64 indirect_size;
 | 
						|
        u8  indirect_header[NcaBucketInfo::HeaderSize];
 | 
						|
        fs::Int64 aes_ctr_ex_offset;
 | 
						|
        fs::Int64 aes_ctr_ex_size;
 | 
						|
        u8  aes_ctr_ex_header[NcaBucketInfo::HeaderSize];
 | 
						|
 | 
						|
        bool HasIndirectTable() const;
 | 
						|
        bool HasAesCtrExTable() const;
 | 
						|
    };
 | 
						|
    static_assert(util::is_pod<NcaPatchInfo>::value);
 | 
						|
 | 
						|
    union NcaAesCtrUpperIv {
 | 
						|
        u64 value;
 | 
						|
        struct {
 | 
						|
            u32 generation;
 | 
						|
            u32 secure_value;
 | 
						|
        } part;
 | 
						|
    };
 | 
						|
    static_assert(util::is_pod<NcaAesCtrUpperIv>::value);
 | 
						|
 | 
						|
    struct NcaSparseInfo {
 | 
						|
        NcaBucketInfo bucket;
 | 
						|
        fs::Int64 physical_offset;
 | 
						|
        u16 generation;
 | 
						|
        u8  reserved[6];
 | 
						|
 | 
						|
        s64 GetPhysicalSize() const {
 | 
						|
            return this->bucket.offset + this->bucket.size;
 | 
						|
        }
 | 
						|
 | 
						|
        u32 GetGeneration() const {
 | 
						|
            return static_cast<u32>(this->generation) << 16;
 | 
						|
        }
 | 
						|
 | 
						|
        const NcaAesCtrUpperIv MakeAesCtrUpperIv(NcaAesCtrUpperIv upper_iv) const {
 | 
						|
            NcaAesCtrUpperIv sparse_upper_iv = upper_iv;
 | 
						|
            sparse_upper_iv.part.generation = this->GetGeneration();
 | 
						|
            return sparse_upper_iv;
 | 
						|
        }
 | 
						|
    };
 | 
						|
    static_assert(util::is_pod<NcaSparseInfo>::value);
 | 
						|
 | 
						|
    struct NcaCompressionInfo {
 | 
						|
        NcaBucketInfo bucket;
 | 
						|
        u8 reserved[8];
 | 
						|
    };
 | 
						|
    static_assert(util::is_pod<NcaCompressionInfo>::value);
 | 
						|
 | 
						|
    struct NcaMetaDataHashDataInfo {
 | 
						|
        fs::Int64 offset;
 | 
						|
        fs::Int64 size;
 | 
						|
        Hash hash;
 | 
						|
    };
 | 
						|
    static_assert(util::is_pod<NcaMetaDataHashDataInfo>::value);
 | 
						|
 | 
						|
    struct NcaFsHeader {
 | 
						|
        static constexpr size_t Size           = 0x200;
 | 
						|
        static constexpr size_t HashDataOffset = 0x8;
 | 
						|
 | 
						|
        struct Region {
 | 
						|
            fs::Int64 offset;
 | 
						|
            fs::Int64 size;
 | 
						|
        };
 | 
						|
        static_assert(util::is_pod<Region>::value);
 | 
						|
 | 
						|
        enum class FsType : u8 {
 | 
						|
            RomFs       = 0,
 | 
						|
            PartitionFs = 1,
 | 
						|
        };
 | 
						|
 | 
						|
        enum class EncryptionType : u8 {
 | 
						|
            Auto                  = 0,
 | 
						|
            None                  = 1,
 | 
						|
            AesXts                = 2,
 | 
						|
            AesCtr                = 3,
 | 
						|
            AesCtrEx              = 4,
 | 
						|
            AesCtrSkipLayerHash   = 5,
 | 
						|
            AesCtrExSkipLayerHash = 6,
 | 
						|
        };
 | 
						|
 | 
						|
        enum class HashType : u8 {
 | 
						|
            Auto                          = 0,
 | 
						|
            None                          = 1,
 | 
						|
            HierarchicalSha256Hash        = 2,
 | 
						|
            HierarchicalIntegrityHash     = 3,
 | 
						|
            AutoSha3                      = 4,
 | 
						|
            HierarchicalSha3256Hash       = 5,
 | 
						|
            HierarchicalIntegritySha3Hash = 6,
 | 
						|
        };
 | 
						|
 | 
						|
        enum class MetaDataHashType : u8 {
 | 
						|
            None                  = 0,
 | 
						|
            HierarchicalIntegrity = 1,
 | 
						|
        };
 | 
						|
 | 
						|
        union HashData {
 | 
						|
            struct HierarchicalSha256Data {
 | 
						|
                static constexpr size_t HashLayerCountMax = 5;
 | 
						|
                static const size_t MasterHashOffset;
 | 
						|
 | 
						|
                Hash fs_data_master_hash;
 | 
						|
                s32 hash_block_size;
 | 
						|
                s32 hash_layer_count;
 | 
						|
                Region hash_layer_region[HashLayerCountMax];
 | 
						|
            } hierarchical_sha256_data;
 | 
						|
            static_assert(util::is_pod<HierarchicalSha256Data>::value);
 | 
						|
 | 
						|
            struct IntegrityMetaInfo {
 | 
						|
                static const size_t MasterHashOffset;
 | 
						|
 | 
						|
                u32 magic;
 | 
						|
                u32 version;
 | 
						|
                u32 master_hash_size;
 | 
						|
 | 
						|
                struct LevelHashInfo {
 | 
						|
                    u32 max_layers;
 | 
						|
 | 
						|
                    struct HierarchicalIntegrityVerificationLevelInformation {
 | 
						|
                        static constexpr size_t IntegrityMaxLayerCount = 7;
 | 
						|
                        fs::Int64 offset;
 | 
						|
                        fs::Int64 size;
 | 
						|
                        s32 block_order;
 | 
						|
                        u8  reserved[4];
 | 
						|
                    } info[HierarchicalIntegrityVerificationLevelInformation::IntegrityMaxLayerCount - 1];
 | 
						|
 | 
						|
                    struct SignatureSalt {
 | 
						|
                        static constexpr size_t Size = 0x20;
 | 
						|
                        u8 value[Size];
 | 
						|
                    } seed;
 | 
						|
                } level_hash_info;
 | 
						|
 | 
						|
                Hash master_hash;
 | 
						|
            } integrity_meta_info;
 | 
						|
            static_assert(util::is_pod<IntegrityMetaInfo>::value);
 | 
						|
 | 
						|
            u8 padding[NcaPatchInfo::Offset - HashDataOffset];
 | 
						|
        };
 | 
						|
 | 
						|
        u16 version;
 | 
						|
        FsType fs_type;
 | 
						|
        HashType hash_type;
 | 
						|
        EncryptionType encryption_type;
 | 
						|
        MetaDataHashType meta_data_hash_type;
 | 
						|
        u8 reserved[2];
 | 
						|
        HashData hash_data;
 | 
						|
        NcaPatchInfo patch_info;
 | 
						|
        NcaAesCtrUpperIv aes_ctr_upper_iv;
 | 
						|
        NcaSparseInfo sparse_info;
 | 
						|
        NcaCompressionInfo compression_info;
 | 
						|
        NcaMetaDataHashDataInfo meta_data_hash_data_info;
 | 
						|
        u8 pad[0x30];
 | 
						|
 | 
						|
        bool IsSkipLayerHashEncryption() const {
 | 
						|
            return this->encryption_type == EncryptionType::AesCtrSkipLayerHash || this->encryption_type == EncryptionType::AesCtrExSkipLayerHash;
 | 
						|
        }
 | 
						|
 | 
						|
        Result GetHashTargetOffset(s64 *out) const {
 | 
						|
            switch (this->hash_type) {
 | 
						|
                case HashType::HierarchicalIntegrityHash:
 | 
						|
                case HashType::HierarchicalIntegritySha3Hash:
 | 
						|
                    *out = this->hash_data.integrity_meta_info.level_hash_info.info[this->hash_data.integrity_meta_info.level_hash_info.max_layers - 2].offset;
 | 
						|
                    R_SUCCEED();
 | 
						|
                case HashType::HierarchicalSha256Hash:
 | 
						|
                case HashType::HierarchicalSha3256Hash:
 | 
						|
                    *out = this->hash_data.hierarchical_sha256_data.hash_layer_region[this->hash_data.hierarchical_sha256_data.hash_layer_count - 1].offset;
 | 
						|
                    R_SUCCEED();
 | 
						|
                default:
 | 
						|
                    R_THROW(fs::ResultInvalidNcaFsHeader());
 | 
						|
            }
 | 
						|
        }
 | 
						|
    };
 | 
						|
    static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size);
 | 
						|
    static_assert(util::is_pod<NcaFsHeader>::value);
 | 
						|
    static_assert(AMS_OFFSETOF(NcaFsHeader, patch_info) == NcaPatchInfo::Offset);
 | 
						|
 | 
						|
    inline constexpr const size_t NcaFsHeader::HashData::HierarchicalSha256Data::MasterHashOffset = AMS_OFFSETOF(NcaFsHeader, hash_data.hierarchical_sha256_data.fs_data_master_hash);
 | 
						|
    inline constexpr const size_t NcaFsHeader::HashData::IntegrityMetaInfo::MasterHashOffset      = AMS_OFFSETOF(NcaFsHeader, hash_data.integrity_meta_info.master_hash);
 | 
						|
 | 
						|
    struct NcaMetaDataHashData {
 | 
						|
        s64 layer_info_offset;
 | 
						|
        NcaFsHeader::HashData::IntegrityMetaInfo integrity_meta_info;
 | 
						|
    };
 | 
						|
    static_assert(sizeof(NcaMetaDataHashData) == sizeof(NcaFsHeader::HashData::IntegrityMetaInfo) + sizeof(s64));
 | 
						|
    static_assert(util::is_pod<NcaMetaDataHashData>::value);
 | 
						|
 | 
						|
}
 |