Difference between revisions of "NCSD"

From 3dbrew
Jump to navigation Jump to search
(Undo revision 3631 by Neimod (talk))
(→‎InitialData: Fix struct being too large caused by having redundant data)
 
(51 intermediate revisions by 13 users not shown)
Line 3: Line 3:
  
 
== Overview ==
 
== Overview ==
There are two known specialisations of the NCSD container format. The CTR Cart Image (CCI) format and the CTR System Update (CSU). CCI is the format of retail game ROM dumps. CSU is used with developer system updates and tools
+
There are two known specialisations of the NCSD container format. The CTR Cart Image (CCI) format and the 3DS' raw [[Flash Filesystem#NAND structure|NAND format]]. CCI is the format of game ROM images.
  
NCSD images start with a NCSD header, followed by up to a maximum of 8 [[NCCH]] partitions. The first partition([[NCCH]] 0) usually starts at 0x4000, this is generally the main [[NCCH#CXI|NCCH]] (Executable). The following info on partitions 1, 2, and 7 are only valid for .CCI. The second partition([[NCCH]] 1) contains the game "Manual"(title ID for the .[[NCCH#CFA|CFA]] containing the Manual always starts with '0005'). The third partition([[NCCH]] 2) contains the download play "Child"(The title ID for the .[[NCCH#CFA|CFA]] containing the DLP Child always starts with '0006'). The eighth block([[NCCH]] 7) contains "Update Data"(The title ID for the .[[NCCH#CFA|CFA]] containing the Update Data always starts with '8000') The format of partitions can be determined from the partition FS flags, while the contents can be determined from the partitions flags.
+
'''CTR System Update (CSU)''' is a variant of CCI, where the only difference is in the file extension. This is used with developer System Updates and associated [[3DS Development Unit Software|Tools]].
 +
 
 +
 
 +
NCSD images start with a NCSD header, followed by up to a maximum of 8 [[NCCH]] partitions.
 +
 
 +
For CCI images, the partitions are reserved as follows:
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
[[NCCH]] Index
 +
!  Reserved Use
 +
|-
 +
| 0
 +
| Executable Content ([[NCCH#CXI|CXI]])
 +
|-
 +
| 1
 +
| E-Manual ([[NCCH#CFA|CFA]])
 +
|-
 +
| 2
 +
| [[Download Play]] Child container ([[NCCH#CFA|CFA]])
 +
|-
 +
| 6
 +
| New3DS [[System_Update_CFA|Update Data]] ([[NCCH#CFA|CFA]])
 +
|-
 +
| 7
 +
| [[System_Update_CFA|Update Data]] ([[NCCH#CFA|CFA]])
 +
|}
 +
 
 +
The format of partitions can be determined from the partition FS flags (normally these are zero for CCI/CSU NCSD Images).
  
 
== NCSD header ==
 
== NCSD header ==
Line 28: Line 56:
 
|  0x108
 
|  0x108
 
|  8
 
|  8
Title/Program ID
+
Media ID
 
|-
 
|-
 
|  0x110
 
|  0x110
 
|  8
 
|  8
|  Partitions FS type
+
|  Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save)
 
|-
 
|-
 
|  0x118
 
|  0x118
 
|  8
 
|  8
|  Partitions crypt type  
+
|  Partitions crypt type (each byte corresponds to a partition in the partition table)
 
|-
 
|-
 
|  0x120
 
|  0x120
 
|  0x40=(4+4)*8
 
|  0x40=(4+4)*8
|  Offset & Length partition table
+
|  Offset & Length partition table, in media units
 +
|-
 +
|  0x160
 +
|  0xA0
 +
|  ...
 +
|}
 +
 
 +
For carts,
 +
{| class="wikitable" border="1"
 +
|-
 +
!  Offset
 +
!  Size
 +
!  Description
 
|-
 
|-
 
|  0x160
 
|  0x160
Line 56: Line 96:
 
|  0x188
 
|  0x188
 
|  8
 
|  8
|  Partition Flags: byte[5]-byte[7] indicate content type ( system update, application, manual, ... ) size of media units ( 512*2^byte[6] ) and encryption.
+
|  Partition Flags (See Below)
 
|-
 
|-
 
|  0x190
 
|  0x190
 
|  0x40=8*8
 
|  0x40=8*8
Partitions' Title ID table  
+
Partition ID table  
 
|-
 
|-
 
|  0x1D0
 
|  0x1D0
0x28
+
0x20
 
|  Reserved
 
|  Reserved
 
|-
 
|-
0x1F8
+
| 0x1F0
8
+
| 0xE
 +
| Reserved?
 +
|-
 +
| 0x1FE
 +
| 0x1
 +
| Support for this was implemented with [[9.6.0-24|9.6.0-X]] FIRM. Bit0=1 enables using bits 1-2, it's unknown what these two bits are actually used for(the value of these two bits get compared with some other value during NCSD verification/loading). This appears to enable a new, likely hardware-based, antipiracy check on cartridges.
 +
|-
 +
| 0x1FF
 +
| 0x1
 +
| Support for this was implemented with [[9.6.0-24|9.6.0-X]] FIRM, see below regarding save crypto.
 +
|}
 +
 
 +
For NAND,
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
!  Offset
 +
!  Size
 +
!  Description
 +
|-
 +
0x160
 +
0x5E
 
|  Unknown
 
|  Unknown
 
|-
 
|-
0x200
+
0x1BE
4
+
0x42
Always 0xFFFFFFFF
+
Encrypted MBR partition-table, for the TWL partitions(key-data used for this keyslot is console-unique).
 +
|}
 +
 
 +
=== NCSD Signature ===
 +
The RSA public key used for gamecard NCSD is stored in [[Memory_layout|ITCM]]. The separate public key used for NAND NCSD is stored in Process9 .(ro)data instead of ITCM, and in [[Bootloader|boot ROM]].
 +
 
 +
For the boot ROM check, sighax may be used to fake-sign NAND headers.  Process9's check will fail, however, unless patched.
 +
 
 +
=== Partition Flags ===
 +
{| class="wikitable" border="1"
 +
|-
 +
!  Byte Index
 +
!  Description
 +
|-
 +
| 0
 +
| Backup Write Wait Time (The time to wait to write save to backup after the card is recognized (0-255 seconds)).NATIVE_FIRM loads this flag from the gamecard NCSD header starting with [[6.0.0-11]].
 
|-
 
|-
| 0x204
+
| 3
| 252
+
| Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
|  Padding?
 
 
|-
 
|-
| 0x300
+
| 4
4
+
| Media Platform Index (1 = CTR)
| Used ROM size in bytes
 
 
|-
 
|-
| 0x304
+
| 5
| 28
+
| Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
|  Padding
 
 
|-
 
|-
| 0x320
+
| 6
| 8
+
| Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
[[NVer]] Title ID (Only Present in retail .CCI)
 
 
|-
 
|-
| 0x328
+
| 7
| 8
+
| Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
|  [[NVer]] Title Version (Only Present in retail .CCI)
 
 
|}
 
|}
  
Parts of the first NCCH block's header are found around 0x1000 for whatever reason. NCSD can hold up to 8 partitions. (i.e. Mario Kart 7 holds 4 NCCHs, most we've seen so far)
+
=== Partition Flags (In Terms of Save Crypto Determination) ===
 +
{| class="wikitable" border="1"
 +
|-
 +
!  Byte Index
 +
!  Description
 +
|-
 +
| 1
 +
| Starting with [[6.0.0-11]] NATIVE_FIRM will use this flag to determine the gamecard [[Savegames|savegame]] keyY method, when flag[3] is set. 0 = [[2.0.0-2]] hashed keyY, 1 = [[Savegames|new]] keyY method implemented with [[6.0.0-11]]. 0x0A = implemented with [[9.3.0-21|9.3.0-X]]. On Old3DS this is identical to the [[2.2.0-4]] crypto. On New3DS this is identical to the [[2.2.0-4]] crypto, except with New3DS-only gamecard savedata [[AES|keyslots]].
 +
Starting with [[9.6.0-24|9.6.0-X]] FIRM, Process9 now sets <savecrypto_stateval> to partitionflag[1] + <the u8 value from NCSD+0x1FF>, instead of just setting it to partitionflag[1].
 +
|-
 +
| 3
 +
| Support for this flag was implemented in NATIVE_FIRM with [[2.0.0-2]]. When this flag is set the hashed gamecard [[Savegames|savegame]] keyY method is used, this likely still uses the repeating-CTR however. With [[6.0.0-11]] the system will determine the gamecard savegame keyY method via flag[1], instead of just using the hashed keyY via this flag.
 +
|-th
 +
| 7
 +
| This flag enables using the hashed gamecard [[Savegames|savegame]] keyY method, support for this flag was implemented in NATIVE_FIRM with [[2.2.0-4]]. All games with the NCSD image finalized since [[2.2.0-4]](and contains [[2.2.0-4]]+ in the system update partition) have this flag set, this flag also enables using new CTR method as well.
 +
|}
  
The region-specific NVer title for this NCSD presumably must exist on NAND. The NVer title version may also be used to check whether a system update is required before running the app.
+
Starting with [[9.6.0-24|9.6.0-X]] FIRM, Process9 will just write val0 to a state field then return 0, instead of returning an error when the save crypto type isn't recognized. This was the *only* actual functionality change in the Old3DS Process9 function for gamecard savedata crypto init.
  
== NCSD "Unknown Section" ==
+
== Card Info Header ==
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 106: Line 192:
 
!  DESCRIPTION
 
!  DESCRIPTION
 
|-
 
|-
0x1000
+
0x200
8
+
4
Title ID of main NCCH
+
CARD2: Writable Address In Media Units (For 'On-Chip' Savedata). CARD1: Always 0xFFFFFFFF.
 
|-
 
|-
0x1008
+
0x204
8
+
4
Reserved
+
Card Info Bitmask
 
|-
 
|-
0x1010
+
0x208
0x30
+
0xF8
|  Initial Data
 
|-
 
|  0x1040
 
|  0xC0
 
 
|  Reserved
 
|  Reserved
 
|-
 
|-
0x1100
+
0x300
 
|  4
 
|  4
Magic ID, always 'NCCH'
+
Filled size of cartridge
 
|-
 
|-
0x1104
+
0x304
4
+
0xC
Content size, in media units (1 media unit = 0x200 bytes)
+
Reserved
 
|-
 
|-
0x1108
+
0x310
|  8
 
|  Partition ID
 
|-
 
|  0x1110
 
 
|  2
 
|  2
Maker code
+
Title version
 
|-
 
|-
0x1112
+
0x312
 
|  2
 
|  2
Version
+
Card revision
 
|-
 
|-
0x1114
+
0x314
4
+
0xC
 
|  Reserved
 
|  Reserved
 
|-
 
|-
0x1118
+
0x320
 
|  8
 
|  8
Program ID
+
Title ID of [[CVer]] in included update partition
 
|-
 
|-
0x1120
+
0x328
1
+
2
Temp flag
+
Version number of [[CVer]] in included update partition
 
|-
 
|-
0x1121
+
0x32A
0x2F
+
0xCD6
 
|  Reserved
 
|  Reserved
 
|-
 
|-
0x1150
+
0x1000
 +
|  0x200
 +
|  InitialData
 +
|}
 +
 
 +
=== InitialData ===
 +
 
 +
This data is returned by [[Gamecards|16-byte cartridge command]] 0x82.
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
!  OFFSET
 +
!  SIZE
 +
!  DESCRIPTION
 +
|-
 +
|  0x00
 
|  0x10
 
|  0x10
Product code
+
Seed (keyY used to decrypt the title key - keyX is keyslot 0x3B for production cards, or a key of all zeroes for development cards), consisting of the title ID (little-endian) followed by reserved data (normally all-zero)
 +
|-
 +
|  0x10
 +
|  0x10
 +
|  TitleKey (AES-CCM encrypted)
 
|-
 
|-
|  0x1160
 
 
|  0x20
 
|  0x20
Extended header hash (SHA-256), over the cleartext ExHeader
+
0x10
 +
|  AES-CCM MAC
 +
|-
 +
|  0x30
 +
|  0xC
 +
|  AES-CCM nonce
 +
|-
 +
|  0x3C
 +
|  0xC4
 +
|  Reserved (normally all-zero)
 +
|-
 +
|  0x100
 +
|  0x100
 +
|  NcchHeader (copy of the first NCCH header, excluding the RSA signature)
 +
|}
 +
 
 +
== Development Card Info Header Extension ==
 +
{| class="wikitable" border="1"
 +
|-
 +
!  OFFSET
 +
!  SIZE
 +
!  DESCRIPTION
 
|-
 
|-
0x1180
+
0x1200
4
+
0x200
Extended header size
+
CardDeviceReserved1
 +
|-
 +
|  0x1400
 +
|  0x10
 +
|  TitleKeyData
 +
|-
 +
|  0x1410
 +
|  0x1BF0
 +
|  CardDeviceReserved2
 +
|-
 +
|  0x3000
 +
|  0x1000
 +
|  TestData
 +
|}
 +
 
 +
TitleKeyData contains the decrypted version of the title key found in the InitialData. This field appears to be what development--and maybe production?--cards read to know what card encryption seed to use in the CTR protocol.
 +
 
 +
The CardDeviceReserved areas have random-looking data whose purpose is unknown, other than perhaps to hide the TitleKey.
 +
 
 +
Note that a particular flashcard vendor, namely Gateway, puts what many refer to as "private headers" at CardDeviceReserved1 in place of actual development card information. This header consists of:
 +
 
 +
{| class="wikitable" border="1"
 
|-
 
|-
| 0x1184
+
! OFFSET
| 4
+
! SIZE
| Reserved
+
! DESCRIPTION
 
|-
 
|-
0x1188
+
0x0
8
+
0x40
Flags: byte[5]-byte[7] indicate content type ( system update, application, manual, ... ) size of media unit ( 512*2^byte[6] ) and encryption.
+
Unique cartridge ID; only the first 0x10 bytes are meaningful, the rest are 0xff; obtainable using encrypted [[Gamecards|16-byte cartridge command]] 0xc6; the first 0x10 bytes can also be obtained in userland via [[Process_Services_PXI|pxi:ps9::GetRomId]]
 
|-
 
|-
0x1190
+
0x40
4
+
0x4
Plain region offset, in media units
+
Cartridge ID1; obtainable using 8-byte cartridge command 0x90 or 16-byte cartridge command 0xa2
 
|-
 
|-
0x1194
+
0x44
4
+
0x4
Plain region size, in media units
+
Cartridge ID2; obtainable using 8-byte cartridge command 0xa0 or 16-byte cartridge command 0xa4
 
|-
 
|-
0x1198
+
0x48
8
+
0x8
Reserved
+
Padding (all-0xff)
 +
|}
 +
 
 +
The legitimacy of the unique cartridge ID can be validated by online services.
 +
 
 +
Some dumping tools, notably GodMode9 as of 2024-05-26, erroneously always write 0x00000000 into the position of the Cartridge ID2. This is presumably because the cartridge ID2 is always zero for retail carts.
 +
 
 +
=== TestData ===
 +
The test data is the same one encountered in development DS/DSi cartridges. Its layout is as follows:
 +
{| class="wikitable" border="1"
 
|-
 
|-
| 0x11A0
+
! OFFSET
| 4
+
! SIZE
| ExeFS offset, in media units
+
! DESCRIPTION
 
|-
 
|-
0x11A4
+
0x0
4
+
0x8
ExeFS size, in media units
+
The bytes FF 00 FF 00 AA 55 AA 55.
 
|-
 
|-
0x11A8
+
0x8
4
+
0x1F8
ExeFS hash region size, in media units
+
An ascending byte sequence equal to the offset mod 256 (08 09 0A ... FE FF 00 01 ... FF).
 
|-
 
|-
0x11AC
+
0x200
4
+
0x200
Reserved
+
A descending byte sequence equal to 255 minus the offset mod 256 (FF FE FD ... 00 FF DE ... 00).
 
|-
 
|-
0x11B0
+
0x400
4
+
0x200
RomFS offset, in media units
+
Filled with 00 (0b00000000) bytes.
 
|-
 
|-
0x11B4
+
0x600
4
+
0x200
RomFS size, in media units
+
Filled with FF (0b11111111) bytes.
 
|-
 
|-
0x11B8
+
0x800
4
+
0x200
RomFS hash region size, in media units
+
Filled with 0F (0b00001111) bytes.
 
|-
 
|-
0x11BC
+
0xA00
4
+
0x200
Reserved
+
Filled with F0 (0b11110000) bytes.
 
|-
 
|-
0x11C0
+
0xC00
0x20
+
0x200
ExeFS superblock hash, over the cleartext ExeFS
+
Filled with 55 (0b01010101) bytes.
 
|-
 
|-
0x11E0
+
0xE00
0x20
+
0x1FF
RomFS superblock hash
+
Filled with AA (0b10101010) bytes.
 
|-
 
|-
0x1200
+
0xFFF
0x2E00
+
0x1
Reserved/Unused (Is filled with either 'F's or '0's)  
+
The final byte is 00 (0b00000000).
 
|}
 
|}
From the magic 'NCCH' to the end of the 'RomFS superblock hash' the data is identical to the [[NCCH#NCCH Header|NCCH Header]] of the [[NCCH#CXI|Executable NCCH]] Partition (NCCH 0).
+
 
 +
Production cards always return FF when attempting to read 0x1200-0x3FFF. They probably actually have the same data as development cards, but there's no way to read it.
 +
 
 +
== Tools ==
 +
 
 +
[https://github.com/3dshax/ctr/tree/master/ctrtool ctrtool] - (CMD)(Windows/Linux) Parsing NCSD files
 +
 
 +
[[3DSExplorer]] - (GUI)(Windows Only) Parsing NCSD files

Latest revision as of 12:17, 2 June 2024

This page documents the format of NCSD.

Overview[edit]

There are two known specialisations of the NCSD container format. The CTR Cart Image (CCI) format and the 3DS' raw NAND format. CCI is the format of game ROM images.

CTR System Update (CSU) is a variant of CCI, where the only difference is in the file extension. This is used with developer System Updates and associated Tools.


NCSD images start with a NCSD header, followed by up to a maximum of 8 NCCH partitions.

For CCI images, the partitions are reserved as follows:

NCCH Index Reserved Use
0 Executable Content (CXI)
1 E-Manual (CFA)
2 Download Play Child container (CFA)
6 New3DS Update Data (CFA)
7 Update Data (CFA)

The format of partitions can be determined from the partition FS flags (normally these are zero for CCI/CSU NCSD Images).

NCSD header[edit]

Offset Size Description
0x000 0x100 RSA-2048 SHA-256 signature of the NCSD header
0x100 4 Magic Number 'NCSD'
0x104 4 Size of the NCSD image, in media units (1 media unit = 0x200 bytes)
0x108 8 Media ID
0x110 8 Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save)
0x118 8 Partitions crypt type (each byte corresponds to a partition in the partition table)
0x120 0x40=(4+4)*8 Offset & Length partition table, in media units
0x160 0xA0 ...

For carts,

Offset Size Description
0x160 0x20 Exheader SHA-256 hash
0x180 0x4 Additional header size
0x184 0x4 Sector zero offset
0x188 8 Partition Flags (See Below)
0x190 0x40=8*8 Partition ID table
0x1D0 0x20 Reserved
0x1F0 0xE Reserved?
0x1FE 0x1 Support for this was implemented with 9.6.0-X FIRM. Bit0=1 enables using bits 1-2, it's unknown what these two bits are actually used for(the value of these two bits get compared with some other value during NCSD verification/loading). This appears to enable a new, likely hardware-based, antipiracy check on cartridges.
0x1FF 0x1 Support for this was implemented with 9.6.0-X FIRM, see below regarding save crypto.

For NAND,

Offset Size Description
0x160 0x5E Unknown
0x1BE 0x42 Encrypted MBR partition-table, for the TWL partitions(key-data used for this keyslot is console-unique).

NCSD Signature[edit]

The RSA public key used for gamecard NCSD is stored in ITCM. The separate public key used for NAND NCSD is stored in Process9 .(ro)data instead of ITCM, and in boot ROM.

For the boot ROM check, sighax may be used to fake-sign NAND headers. Process9's check will fail, however, unless patched.

Partition Flags[edit]

Byte Index Description
0 Backup Write Wait Time (The time to wait to write save to backup after the card is recognized (0-255 seconds)).NATIVE_FIRM loads this flag from the gamecard NCSD header starting with 6.0.0-11.
3 Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
4 Media Platform Index (1 = CTR)
5 Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
6 Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
7 Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)

Partition Flags (In Terms of Save Crypto Determination)[edit]

Byte Index Description
1 Starting with 6.0.0-11 NATIVE_FIRM will use this flag to determine the gamecard savegame keyY method, when flag[3] is set. 0 = 2.0.0-2 hashed keyY, 1 = new keyY method implemented with 6.0.0-11. 0x0A = implemented with 9.3.0-X. On Old3DS this is identical to the 2.2.0-4 crypto. On New3DS this is identical to the 2.2.0-4 crypto, except with New3DS-only gamecard savedata keyslots.

Starting with 9.6.0-X FIRM, Process9 now sets <savecrypto_stateval> to partitionflag[1] + <the u8 value from NCSD+0x1FF>, instead of just setting it to partitionflag[1].

3 Support for this flag was implemented in NATIVE_FIRM with 2.0.0-2. When this flag is set the hashed gamecard savegame keyY method is used, this likely still uses the repeating-CTR however. With 6.0.0-11 the system will determine the gamecard savegame keyY method via flag[1], instead of just using the hashed keyY via this flag.
7 This flag enables using the hashed gamecard savegame keyY method, support for this flag was implemented in NATIVE_FIRM with 2.2.0-4. All games with the NCSD image finalized since 2.2.0-4(and contains 2.2.0-4+ in the system update partition) have this flag set, this flag also enables using new CTR method as well.

Starting with 9.6.0-X FIRM, Process9 will just write val0 to a state field then return 0, instead of returning an error when the save crypto type isn't recognized. This was the *only* actual functionality change in the Old3DS Process9 function for gamecard savedata crypto init.

Card Info Header[edit]

OFFSET SIZE DESCRIPTION
0x200 4 CARD2: Writable Address In Media Units (For 'On-Chip' Savedata). CARD1: Always 0xFFFFFFFF.
0x204 4 Card Info Bitmask
0x208 0xF8 Reserved
0x300 4 Filled size of cartridge
0x304 0xC Reserved
0x310 2 Title version
0x312 2 Card revision
0x314 0xC Reserved
0x320 8 Title ID of CVer in included update partition
0x328 2 Version number of CVer in included update partition
0x32A 0xCD6 Reserved
0x1000 0x200 InitialData

InitialData[edit]

This data is returned by 16-byte cartridge command 0x82.

OFFSET SIZE DESCRIPTION
0x00 0x10 Seed (keyY used to decrypt the title key - keyX is keyslot 0x3B for production cards, or a key of all zeroes for development cards), consisting of the title ID (little-endian) followed by reserved data (normally all-zero)
0x10 0x10 TitleKey (AES-CCM encrypted)
0x20 0x10 AES-CCM MAC
0x30 0xC AES-CCM nonce
0x3C 0xC4 Reserved (normally all-zero)
0x100 0x100 NcchHeader (copy of the first NCCH header, excluding the RSA signature)

Development Card Info Header Extension[edit]

OFFSET SIZE DESCRIPTION
0x1200 0x200 CardDeviceReserved1
0x1400 0x10 TitleKeyData
0x1410 0x1BF0 CardDeviceReserved2
0x3000 0x1000 TestData

TitleKeyData contains the decrypted version of the title key found in the InitialData. This field appears to be what development--and maybe production?--cards read to know what card encryption seed to use in the CTR protocol.

The CardDeviceReserved areas have random-looking data whose purpose is unknown, other than perhaps to hide the TitleKey.

Note that a particular flashcard vendor, namely Gateway, puts what many refer to as "private headers" at CardDeviceReserved1 in place of actual development card information. This header consists of:

OFFSET SIZE DESCRIPTION
0x0 0x40 Unique cartridge ID; only the first 0x10 bytes are meaningful, the rest are 0xff; obtainable using encrypted 16-byte cartridge command 0xc6; the first 0x10 bytes can also be obtained in userland via pxi:ps9::GetRomId
0x40 0x4 Cartridge ID1; obtainable using 8-byte cartridge command 0x90 or 16-byte cartridge command 0xa2
0x44 0x4 Cartridge ID2; obtainable using 8-byte cartridge command 0xa0 or 16-byte cartridge command 0xa4
0x48 0x8 Padding (all-0xff)

The legitimacy of the unique cartridge ID can be validated by online services.

Some dumping tools, notably GodMode9 as of 2024-05-26, erroneously always write 0x00000000 into the position of the Cartridge ID2. This is presumably because the cartridge ID2 is always zero for retail carts.

TestData[edit]

The test data is the same one encountered in development DS/DSi cartridges. Its layout is as follows:

OFFSET SIZE DESCRIPTION
0x0 0x8 The bytes FF 00 FF 00 AA 55 AA 55.
0x8 0x1F8 An ascending byte sequence equal to the offset mod 256 (08 09 0A ... FE FF 00 01 ... FF).
0x200 0x200 A descending byte sequence equal to 255 minus the offset mod 256 (FF FE FD ... 00 FF DE ... 00).
0x400 0x200 Filled with 00 (0b00000000) bytes.
0x600 0x200 Filled with FF (0b11111111) bytes.
0x800 0x200 Filled with 0F (0b00001111) bytes.
0xA00 0x200 Filled with F0 (0b11110000) bytes.
0xC00 0x200 Filled with 55 (0b01010101) bytes.
0xE00 0x1FF Filled with AA (0b10101010) bytes.
0xFFF 0x1 The final byte is 00 (0b00000000).

Production cards always return FF when attempting to read 0x1200-0x3FFF. They probably actually have the same data as development cards, but there's no way to read it.

Tools[edit]

ctrtool - (CMD)(Windows/Linux) Parsing NCSD files

3DSExplorer - (GUI)(Windows Only) Parsing NCSD files