// SPDX-License-Identifier: MPL-2.0 // Copyright Ā© 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include #include #include #include #include "loader.h" namespace skyline::loader { /** * @brief The XciLoader class abstracts access to an XCI file through the Loader interface * @url https://switchbrew.org/wiki/XCI */ class XciLoader : public Loader { private: enum class GamecardSize : u8 { Size1GB = 0xFA, Size2GB = 0xF8, Size4GB = 0xF0, Size8GB = 0xE0, Size16GB = 0xE1, Size32GB = 0xE2 }; enum class GamecardFlags : u8 { AutoBoot = 0, HistoryErase = 1, RepairTool = 2, DifferentRegionCupToTerraDevice = 3, DifferentRegionCupToGlobalDevice = 4 }; /** * @brief The encryption type used for gamecard verification */ enum class SecurityMode : u8 { T1 = 0x01, T2 = 0x02 }; enum class FirmwareVersion : u64 { Development = 0x00, Retail = 0x01, Retail400 = 0x02, //!< [4.0.0+] Retail Retail1100 = 0x04 //!< [11.0.0+] Retail }; /** * @brief The speed at which the gamecard is accessed */ enum class AccessControl : u32 { ClockRate25Mhz = 0x00A10011, ClockRate50Mhz = 0x00A10010 }; /** * @brief [9.0.0+] The region of Switch HW the gamecard is compatible with */ enum class CompatType : u8 { Global = 0x00, //!< Normal China = 0x01 //!< Terra }; struct GamecardInfo { FirmwareVersion firmwareVersion; AccessControl accessControl; //!< The speed at which the gamecard is accessed u32 readTimeWait1; //!< Read Time Wait1, always 0x1388 u32 readTimeWait2; //!< Read Time Wait2, always 0 u32 writeTimeWait1; //!< Write Time Wait1, always 0 u32 writeTimeWait2; //!< Write Time Wait2, always 0 u32 firmwareMode; u32 cupVersion; CompatType compatType; u8 _pad0_[0x3]; u64 updatePartitionHash; u64 cupId; //!< CUP ID, always 0x0100000000000816, which is the title-listing data archive's title ID u8 _pad1_[0x38]; }; static_assert(sizeof(GamecardInfo) == 0x70); struct GamecardHeader { u8 signature[0x100]; //!< RSA-2048 PKCS #1 signature over the header u32 magic; //!< The magic of the gamecard format: 'HEAD' u32 secureAreaStartAddress; //!< Secure Area Start Address in media units u32 backupAreaStartAddress; //!< Backup Area Start Address, always 0xFFFFFFFF u8 titleKeyDecKekIndex; //!< TitleKeyDec Index (high nibble) and KEK Index (low nibble) GamecardSize size; u8 version; //!< Gamecard header version GamecardFlags flags; //!< GameCardAttribute u64 packageId; //!< The package ID, used for challenge–response authentication u64 validDataEndAddress; //!< Valid Data End Address in media units std::array infoIv; //!< Gamecard Info IV (reversed) u64 hfs0PartitionOffset; //!< The HFS0 header partition offset u64 hfs0HeaderSize; std::array hfs0HeaderSha256; //!< SHA-256 hash of the HFS0 Header std::array initialDataSha256; //!< SHA-256 hash of the Initial Data SecurityMode securityMode; u32 t1KeyIndex; //!< T1 Key Index, always 2 u32 keyIndex; //!< Key Index, always 0 u32 normalAreaEndAddress; //!< Normal Area End Address in media units GamecardInfo gamecardInfo; //!< Gamecard Info (AES-128-CBC encrypted) } header{}; static_assert(sizeof(GamecardHeader) == 0x200); std::shared_ptr xci; //!< A shared pointer to XCI HFS0 header partition std::shared_ptr secure; //!< A shared pointer to the secure HFS0 partition std::shared_ptr update; //!< A shared pointer to the update HFS0 partition std::shared_ptr normal; //!< A shared pointer to the normal HFS0 partition std::shared_ptr logo; //!< A shared pointer to the logo HFS0 partition std::shared_ptr controlRomFs; //!< A shared pointer to the control NCA's RomFS std::optional programNca; //!< The main program NCA within the secure partition std::optional controlNca; //!< The main control NCA within the secure partition public: XciLoader(const std::shared_ptr &backing, const std::shared_ptr &keyStore); std::vector GetIcon(language::ApplicationLanguage language) override; void *LoadProcessData(const std::shared_ptr &process, const DeviceState &state) override; }; }