NCSD

NCSD

This page documents the format of NCSD.

Overview #

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 IndexReserved Use
0Executable Content ( CXI)
1E-Manual ( CFA)
2Download Play Child container ( CFA)
6New3DS Update Data ( CFA)
7Update 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 #

OffsetSizeDescription
0x0000x100RSA-2048 SHA-256 signature of the NCSD header
0x1004Magic Number ‘NCSD’
0x1044Size of the NCSD image, in media units (1 media unit = 0x200 bytes)
0x1088Media ID
0x1108Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save)
0x1188Partitions crypt type (each byte corresponds to a partition in the partition table)
0x1200x40=(4+4)*8Offset & Length partition table, in media units
0x1600xA0

For carts,

OffsetSizeDescription
0x1600x20Exheader SHA-256 hash
0x1800x4Additional header size
0x1840x4Sector zero offset
0x1888Partition Flags (See Below)
0x1900x40=8*8Partition ID table
0x1D00x20Reserved
0x1F00xEReserved?
0x1FE0x1Support 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.
0x1FF0x1Support for this was implemented with 9.6.0-X FIRM, see below regarding save crypto.

For NAND,

OffsetSizeDescription
0x1600x5EUnknown
0x1BE0x42Encrypted 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 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 #

Byte IndexDescription
0Backup 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.
3Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
4Media Platform Index (1 = CTR)
5Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
6Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
7Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)

Partition Flags (In Terms of Save Crypto Determination) #

Byte IndexDescription
1Starting 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].
3Support 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. |-th

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 #

OFFSETSIZEDESCRIPTION
0x2004CARD2: Writable Address In Media Units (For ‘On-Chip’ Savedata). CARD1: Always 0xFFFFFFFF.
0x2044Card Info Bitmask
0x2080xF8Reserved
0x3004Filled size of cartridge
0x3040xCReserved
0x3102Title version
0x3122Card revision
0x3140xCReserved
0x3208Title ID of CVer in included update partition
0x3282Version number of CVer in included update partition
0x32A0xCD6Reserved
0x10000x200InitialData

InitialData #

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

OFFSETSIZEDESCRIPTION
0x000x10Seed (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)
0x100x10TitleKey (AES-CCM encrypted)
0x200x10AES-CCM MAC
0x300xCAES-CCM nonce
0x3C0xC4Reserved (normally all-zero)
0x1000x100NcchHeader (copy of the first NCCH header, excluding the RSA signature)

Development Card Info Header Extension #

OFFSETSIZEDESCRIPTION
0x12000x200CardDeviceReserved1
0x14000x10TitleKeyData
0x14100x1BF0CardDeviceReserved2
0x30000x1000TestData

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:

OFFSETSIZEDESCRIPTION
0x00x40Unique 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
0x400x4Cartridge ID1; obtainable using 8-byte cartridge command 0x90 or 16-byte cartridge command 0xa2
0x440x4Cartridge ID2; obtainable using 8-byte cartridge command 0xa0 or 16-byte cartridge command 0xa4
0x480x8Padding (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:

OFFSETSIZEDESCRIPTION
0x00x8The bytes FF 00 FF 00 AA 55 AA 55.
0x80x1F8An ascending byte sequence equal to the offset mod 256 (08 09 0A … FE FF 00 01 … FF).
0x2000x200A descending byte sequence equal to 255 minus the offset mod 256 (FF FE FD … 00 FF DE … 00).
0x4000x200Filled with 00 (0b00000000) bytes.
0x6000x200Filled with FF (0b11111111) bytes.
0x8000x200Filled with 0F (0b00001111) bytes.
0xA000x200Filled with F0 (0b11110000) bytes.
0xC000x200Filled with 55 (0b01010101) bytes.
0xE000x1FFFilled with AA (0b10101010) bytes.
0xFFF0x1The 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 #

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

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

Category:File formats