Line 1: |
Line 1: |
− | Known internally as "XCI" (NX Card Image), this is the format used for storing the contents of a Nintendo Switch Gamecard.
| + | This is the format used for storing the contents of a Nintendo Switch Gamecard. |
| | | |
− | = Gamecard Header = | + | = Structure = |
− | This header is 0x200 bytes and is located at offset 0 in the Gamecard. | + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Offset |
| + | ! Size |
| + | ! Description |
| + | |- |
| + | | 0x0 |
| + | | 0x1000 |
| + | | [[#CardKeyArea]] |
| + | |- |
| + | | 0x1000 |
| + | | 0x200 |
| + | | [[#CardHeader]] |
| + | |- |
| + | | 0x1200 |
| + | | 0x6E00 |
| + | | ReservedArea |
| + | |- |
| + | | 0x8000 |
| + | | 0x8000 |
| + | | [[#CertArea]] |
| + | |- |
| + | | 0x10000 |
| + | | Variable |
| + | | [[#NormalArea]] |
| + | |- |
| + | | Variable |
| + | | Variable |
| + | | [[#RomArea]] |
| + | |- |
| + | | Invalid |
| + | | Invalid |
| + | | BackupArea |
| + | |} |
| + | |
| + | == CardKeyArea == |
| + | This region cannot be read directly once written to the Gamecard. Therefore, it is hidden away during read/write operations on the raw Gamecard data. |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Offset |
| + | ! Size |
| + | ! Description |
| + | |- |
| + | | 0x0 |
| + | | 0x200 |
| + | | [[#InitialData]] |
| + | |- |
| + | | 0x200 |
| + | | 0xD00 |
| + | | [[#TitleKeyArea]] |
| + | |- |
| + | | 0xF00 |
| + | | 0x100 |
| + | | Reserved |
| + | |} |
| + | |
| + | === InitialData === |
| + | This region is used for challenge–response authentication when changing to the Gamecard's secure mode. |
| + | |
| + | [[Filesystem_services|FS]] calculates a SHA-256 hash over the whole 0x200 bytes and compares it with the hash stored at offset 0x160 in the [[#CardHeader]]. |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Offset |
| + | ! Size |
| + | ! Description |
| + | |- |
| + | | 0x0 |
| + | | 0x8 |
| + | | Package ID from [[#CardHeader]] at offset 0x110 |
| + | |- |
| + | | 0x8 |
| + | | 0x8 |
| + | | Empty |
| + | |- |
| + | | 0x10 |
| + | | 0x10 |
| + | | Challenge–response authentication data |
| + | |- |
| + | | 0x20 |
| + | | 0x10 |
| + | | Challenge–response authentication MAC |
| + | |- |
| + | | 0x30 |
| + | | 0xC |
| + | | Challenge–response authentication Nonce |
| + | |- |
| + | | 0x3C |
| + | | 0x1C4 |
| + | | Reserved (must be empty) |
| + | |} |
| + | |
| + | === TitleKeyArea === |
| + | This region is stored encrypted and contains the title keys used by the [[#InitialData]]. |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Offset |
| + | ! Size |
| + | ! Description |
| + | |- |
| + | | 0x0 |
| + | | 0x8 |
| + | | TitleKey1 |
| + | |- |
| + | | 0x8 |
| + | | 0x8 |
| + | | TitleKey2 |
| + | |- |
| + | | 0x10 |
| + | | 0xCF0 |
| + | | Reserved |
| + | |} |
| | | |
| + | == CardHeader == |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
| |- | | |- |
Line 16: |
Line 130: |
| | 0x100 | | | 0x100 |
| | 0x4 | | | 0x4 |
− | | Magicnum "HEAD" | + | | MagicCode ("HEAD") |
| |- | | |- |
| | 0x104 | | | 0x104 |
| | 0x4 | | | 0x4 |
− | | Secure Area Start Address (in Media Units which are 0x200 bytes) | + | | RomAreaStartPageAddress (in Gamecard page units, which are 0x200 bytes) |
| |- | | |- |
| | 0x108 | | | 0x108 |
| | 0x4 | | | 0x4 |
− | | Backup Area Start Address (always 0xFFFFFFFF) | + | | BackupAreaStartPageAddress (always 0xFFFFFFFF) |
| |- | | |- |
| | 0x10C | | | 0x10C |
| | 0x1 | | | 0x1 |
− | | TitleKeyDec Index (high nibble) and KEK Index (low nibble) | + | | TitleKeyDecIndex (high nibble) and KekIndex (low nibble) |
| |- | | |- |
| | 0x10D | | | 0x10D |
| | 0x1 | | | 0x1 |
− | | [[#Gamecard Size|Gamecard Size]] | + | | [[#RomSize]] |
| |- | | |- |
| | 0x10E | | | 0x10E |
| | 0x1 | | | 0x1 |
− | | Gamecard Header Version | + | | CardHeaderVersion |
| |- | | |- |
| | 0x10F | | | 0x10F |
| | 0x1 | | | 0x1 |
− | | [[#Gamecard Flags|Gamecard Flags]] | + | | [[#Flags]] |
| |- | | |- |
| | 0x110 | | | 0x110 |
| | 0x8 | | | 0x8 |
− | | Package ID (used for challenge–response authentication) | + | | PackageId (used for challenge–response authentication) |
| |- | | |- |
| | 0x118 | | | 0x118 |
| | 0x8 | | | 0x8 |
− | | Valid Data End Address (in Media Units which are 0x200 bytes) | + | | ValidDataEndAddress (in Gamecard page units, which are 0x200 bytes) |
| |- | | |- |
| | 0x120 | | | 0x120 |
| | 0x10 | | | 0x10 |
− | | Gamecard Info IV (reversed) | + | | Iv (reversed) |
| |- | | |- |
| | 0x130 | | | 0x130 |
| | 0x8 | | | 0x8 |
− | | HFS0 partition offset | + | | PartitionFsHeaderAddress |
| |- | | |- |
| | 0x138 | | | 0x138 |
| | 0x8 | | | 0x8 |
− | | HFS0 header size | + | | PartitionFsHeaderSize |
| |- | | |- |
| | 0x140 | | | 0x140 |
| | 0x20 | | | 0x20 |
− | | SHA-256 hash of the [[#HFS0 Header|HFS0 Header]] | + | | PartitionFsHeaderHash (SHA-256 hash of the [[#PartitionFsHeader]]) |
| |- | | |- |
| | 0x160 | | | 0x160 |
| | 0x20 | | | 0x20 |
− | | SHA-256 hash of the [[#Initial Data|Initial Data]] | + | | InitialDataHash (SHA-256 hash of the [[#InitialData]]) |
| |- | | |- |
| | 0x180 | | | 0x180 |
| | 0x4 | | | 0x4 |
− | | Security Mode (0x01 = T1, 0x02 = T2) | + | | SelSec (0x01 = T1, 0x02 = T2) |
| |- | | |- |
| | 0x184 | | | 0x184 |
| | 0x4 | | | 0x4 |
− | | T1 Key Index (always 2) | + | | SelT1Key (always 2) |
| |- | | |- |
| | 0x188 | | | 0x188 |
| | 0x4 | | | 0x4 |
− | | Key Index (always 0) | + | | SelKey (always 0) |
| |- | | |- |
| | 0x18C | | | 0x18C |
| | 0x4 | | | 0x4 |
− | | Normal Area End Address (in Media Units which are 0x200 bytes) | + | | LimArea (in Gamecard page units, which are 0x200 bytes) |
| |- | | |- |
| | 0x190 | | | 0x190 |
| | 0x70 | | | 0x70 |
− | | [[#Gamecard Info|Gamecard Info]] (AES-128-CBC encrypted) | + | | [[#CardInfo]] ( encrypted) |
| + | |} |
| + | |
| + | === CardInfo === |
| + | This region is stored encrypted (AES-128-CBC). |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Offset |
| + | ! Size |
| + | ! Description |
| + | |- |
| + | | 0x0 |
| + | | 0x8 |
| + | | FwVersion (0x00 = Development, 0x01 = Retail, [4.0.0+] 0x02 = Retail, [11.0.0+] 0x04 = Retail) |
| + | |- |
| + | | 0x8 |
| + | | 0x4 |
| + | | AccCtrl1 (0x00A10011 = 25MHz access, 0x00A10010 = 50MHz access) |
| + | |- |
| + | | 0xC |
| + | | 0x4 |
| + | | Wait1TimeRead (always 0x1388) |
| + | |- |
| + | | 0x10 |
| + | | 0x4 |
| + | | Wait2TimeRead (always 0) |
| + | |- |
| + | | 0x14 |
| + | | 0x4 |
| + | | Wait1TimeWrite (always 0) |
| + | |- |
| + | | 0x18 |
| + | | 0x4 |
| + | | Wait2TimeWrite (always 0) |
| + | |- |
| + | | 0x1C |
| + | | 0x4 |
| + | | FwMode (the current SdkAddonVersion) |
| + | |- |
| + | | 0x20 |
| + | | 0x4 |
| + | | UppVersion |
| + | |- |
| + | | 0x24 |
| + | | 0x1 |
| + | | [9.0.0+] CompatibilityType (0x00 = Normal, 0x01 = Terra) |
| + | |- |
| + | | 0x25 |
| + | | 0x3 |
| + | | Empty |
| + | |- |
| + | | 0x28 |
| + | | 0x8 |
| + | | UppHash (SHA-256 hash of the [[#UpdatePartition]]) |
| + | |- |
| + | | 0x30 |
| + | | 0x8 |
| + | | UppId (always 0x0100000000000816) |
| + | |- |
| + | | 0x38 |
| + | | 0x38 |
| + | | Empty |
| |} | | |} |
| | | |
− | == Gamecard Size == | + | === RomSize === |
| [[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardSize|GameCardSize]]. | | [[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardSize|GameCardSize]]. |
| | | |
Line 118: |
Line 294: |
| |} | | |} |
| | | |
− | == Gamecard Flags == | + | === Flags === |
| [[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardAttribute|GameCardAttribute]]. | | [[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardAttribute|GameCardAttribute]]. |
| | | |
Line 142: |
Line 318: |
| |} | | |} |
| | | |
− | == Gamecard Info == | + | == CertArea == |
− | When decrypted, this 0x70 byte region is as follows:
| + | This is the Gamecard's unique certificate. |
− | | |
− | {| class="wikitable" border="1"
| |
− | |-
| |
− | ! Offset
| |
− | ! Size
| |
− | ! Description
| |
− | |-
| |
− | | 0x0
| |
− | | 0x8
| |
− | | Firmware Version (0x00 = Development, 0x01 = Retail, [4.0.0+] 0x02 = Retail, [11.0.0+] 0x04 = Retail)
| |
− | |-
| |
− | | 0x8
| |
− | | 0x4
| |
− | | Access Control (0x00A10011 = 25MHz access, 0x00A10010 = 50MHz access)
| |
− | |-
| |
− | | 0xC
| |
− | | 0x4
| |
− | | Read Time Wait1 (always 0x1388)
| |
− | |-
| |
− | | 0x10
| |
− | | 0x4
| |
− | | Read Time Wait2 (always 0)
| |
− | |-
| |
− | | 0x14
| |
− | | 0x4
| |
− | | Write Time Wait1 (always 0)
| |
− | |-
| |
− | | 0x18
| |
− | | 0x4
| |
− | | Write Time Wait2 (always 0)
| |
− | |-
| |
− | | 0x1C
| |
− | | 0x4
| |
− | | Firmware Mode
| |
− | |-
| |
− | | 0x20
| |
− | | 0x4
| |
− | | CUP Version
| |
− | |-
| |
− | | 0x24
| |
− | | 0x1
| |
− | | [9.0.0+] Compatibility Type (0x00 = Normal, 0x01 = Terra)
| |
− | |-
| |
− | | 0x25
| |
− | | 0x3
| |
− | | Empty
| |
− | |-
| |
− | | 0x28
| |
− | | 0x8
| |
− | | Update Partition Hash
| |
− | |-
| |
− | | 0x30
| |
− | | 0x8
| |
− | | CUP ID (always 0x0100000000000816, which is the title-listing data archive's title ID)
| |
− | |-
| |
− | | 0x38
| |
− | | 0x38
| |
− | | Empty
| |
− | |}
| |
− | | |
− | = Gamecard Certificate =
| |
− | This is the Gamecard's unique certificate and is located at offset 0x7000. | |
| | | |
| [[Filesystem_services|FS]] retrieves this data with [[Filesystem_services#GetGameCardDeviceCertificate|GetGameCardDeviceCertificate]]. | | [[Filesystem_services|FS]] retrieves this data with [[Filesystem_services#GetGameCardDeviceCertificate|GetGameCardDeviceCertificate]]. |
Line 221: |
Line 335: |
| | 0x100 | | | 0x100 |
| | 0x4 | | | 0x4 |
− | | Magicnum "CERT" | + | | MagicCode ("CERT") |
| |- | | |- |
| | 0x104 | | | 0x104 |
| | 0x4 | | | 0x4 |
− | | Empty | + | | Version |
| |- | | |- |
| | 0x108 | | | 0x108 |
| | 0x1 | | | 0x1 |
− | | KEK Index | + | | KekIndex |
| |- | | |- |
| | 0x109 | | | 0x109 |
| | 0x7 | | | 0x7 |
− | | Empty | + | | Reserved |
| |- | | |- |
| | 0x110 | | | 0x110 |
| | 0x10 | | | 0x10 |
− | | Device ID | + | | DeviceId |
| |- | | |- |
| | 0x120 | | | 0x120 |
| | 0x10 | | | 0x10 |
− | | Unknown | + | | Iv |
| |- | | |- |
| | 0x130 | | | 0x130 |
| | 0xD0 | | | 0xD0 |
− | | Encrypted data | + | | Data (encrypted) |
| + | |- |
| + | | 0x200 |
| + | | 0x7E00 |
| + | | Reserved |
| |} | | |} |
| | | |
− | The data between the Gamecard Certificate and the start of the HFS0 region is all 0xFF, except for a few carts that have been found, inwhich it is 0x00.
| + | == NormalArea == |
− | | + | This region contains all non-secure partitions of the Gamecard file system. |
− | = Initial Data = | |
− | This data is used for challenge–response authentication when changing to the Gamecard's secure mode. | |
− | | |
− | [[Filesystem_services|FS]] calculates a SHA-256 hash over the whole 0x200 bytes and compares it with the hash stored at offset 0x160 in the [[#Gamecard Header|Gamecard Header]].
| |
| | | |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
Line 261: |
Line 375: |
| ! Description | | ! Description |
| |- | | |- |
− | | 0x0 | + | | Variable |
− | | 0x8 | + | | Variable |
− | | Package ID from [[#Gamecard Header|Gamecard Header]] at offset 0x110 | + | | [[#PartitionFsHeader|RootPartitionHeader]] |
| + | |- |
| + | | Variable |
| + | | Variable |
| + | | [[#PartitionFsHeader|UpdatePartitionHeader]] |
| + | |- |
| + | | Variable |
| + | | Variable |
| + | | [[#UpdatePartition]] |
| + | |- |
| + | | Variable |
| + | | Variable |
| + | | [4.0.0+] [[#PartitionFsHeader|LogoPartitionHeader]] |
| + | |- |
| + | | Variable |
| + | | Variable |
| + | | [4.0.0+] [[#LogoPartition]] |
| |- | | |- |
− | | 0x8 | + | | Variable |
− | | 0x8 | + | | Variable |
− | | Empty | + | | [[#PartitionFsHeader|NormalPartitionHeader]] |
| |- | | |- |
− | | 0x10 | + | | Variable |
− | | 0x10 | + | | Variable |
− | | Challenge–response authentication data | + | | [[#NormalPartition]] |
| + | |} |
| + | |
| + | === UpdatePartition === |
| + | This partition contains .cnmt.nca + .nca files for the entire system update required to play the game. Launch day carts contain a full copy of 1.0 ncas, newer carts contain newer sysupdate NCAs etc. |
| + | |
| + | === NormalPartition === |
| + | This partition contains the .cnmt.nca and the game icondata nca. This is presumably for future compatibility so that if a future update changes the cryptographic protocol for the secure partition. Game icon data can still be shown in the home menu on old firmwares. |
| + | |
| + | [4.0.0+] This partition is now empty. |
| + | |
| + | === LogoPartition === |
| + | [4.0.0+] This partition now contains the contents of the [[#NormalPartition]]. |
| + | |
| + | == RomArea == |
| + | This region contains all secure partitions of the Gamecard file system. |
| + | |
| + | {| class="wikitable" border="1" |
| |- | | |- |
− | | 0x20
| + | ! Offset |
− | | 0x10
| + | ! Size |
− | | Challenge–response authentication MAC
| + | ! Description |
| |- | | |- |
− | | 0x30 | + | | Variable |
− | | 0xC | + | | Variable |
− | | Challenge–response authentication Nonce | + | | [[#PartitionFsHeader|SecurePartitionHeader]] |
| |- | | |- |
− | | 0x3C | + | | Variable |
− | | 0x1C4 | + | | Variable |
− | | Reserved (must be empty) | + | | [[#SecurePartition]] |
| |} | | |} |
| | | |
− | = HFS0 = | + | === SecurePartition === |
| + | This partition contains an identical copy of the .cnmt.nca and game icondata nca, as well as all other ncas required for the game. |
| + | |
| + | == PartitionFs == |
| This is the Gamecard file system which starts with magicnum "HFS0". | | This is the Gamecard file system which starts with magicnum "HFS0". |
| | | |
− | == Header == | + | === PartitionFsHeader === |
− | The "SHA-256 File System" or "HFS0" starts at offset 0xF000 in the Gamecard. The first 0x200 bytes act as a global header and represent the root partition which points to the other partitions ("normal", "logo", "update" and "secure). | + | The "SHA-256 File System" or "HFS0" starts at offset 0x10000 in the Gamecard. The first 0x200 bytes act as a global header and represent the root partition which points to the other partitions ("normal", "logo", "update" and "secure). |
| | | |
− | A hash for this header is stored at offset 0x140 in the [[#Gamecard Header|Gamecard Header]]. | + | A hash for this header is stored at offset 0x140 in the [[#CardHeader]]. |
− | | |
− | == File System ==
| |
− | The actual file system is as follows (also valid for the root partition):
| |
| | | |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
Line 305: |
Line 452: |
| | 0x0 | | | 0x0 |
| | 0x4 | | | 0x4 |
− | | Magicnum "HFS0" | + | | MagicCode ("HFS0") |
| |- | | |- |
| | 0x4 | | | 0x4 |
| | 0x4 | | | 0x4 |
− | | Number of files | + | | FileCount |
| |- | | |- |
| | 0x8 | | | 0x8 |
| | 0x4 | | | 0x4 |
− | | Size of the string table | + | | StringTableSize |
| |- | | |- |
| | 0xC | | | 0xC |
| | 0x4 | | | 0x4 |
− | | Zero/Reserved | + | | Reserved |
| |- | | |- |
| | 0x10 | | | 0x10 |
| | X | | | X |
− | | File Entry Table | + | | [[#FileEntryTable]] |
| |- | | |- |
| | 0x10 + X | | | 0x10 + X |
| | Y | | | Y |
− | | String Table | + | | StringTable |
| |- | | |- |
| | 0x10 + X + Y | | | 0x10 + X + Y |
| | Z | | | Z |
− | | Raw File Data | + | | RawFileData |
| |} | | |} |
| | | |
− | Where File Entry Table consists of Number of Files FileEntries:
| + | ==== FileEntryTable ==== |
− | | |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
| |- | | |- |
Line 366: |
Line 512: |
| | | |
| The string table is 00-padded to align the start of raw filedata with a sector/media unit boundary (usually?). | | The string table is 00-padded to align the start of raw filedata with a sector/media unit boundary (usually?). |
− |
| |
− | = Cartridge Layout =
| |
− | Observed gamecards contain three partitions: "update", "normal", and "secure".
| |
− |
| |
− | The update partition (Gamecard partition 0 for fsp-srv cmd 31) contains .cnmt.nca + .nca files for the entire system update required to play the game. Launch day carts contain a full copy of 1.0 ncas, newer carts contain newer sysupdate NCAs etc.
| |
− |
| |
− | The normal partition contains the .cnmt.nca and the game icondata nca. This is presumably for future compatibility so that if a future update changes the cryptographic protocol for the secure partition, Game icon data can still be shown in the home menu on old firmwares.
| |
− |
| |
− | The secure partition contains an identical copy of the .cnmt.nca and game icondata nca, as well as all other ncas required for the game.
| |
− |
| |
− | The entire rest of the Gamecard after the secure partition ends is all FF padding.
| |
− |
| |
− | [4.0.0+] The "normal" partition is now empty and a new partition "logo" was added.
| |
− | A partition "boot" got added with exact same content than "logo".
| |