Original research provided mostly by lifehackerhansol.
31 bit 0 ???? ???? ???? ???? ???? ???L LLml lsss | |||| |||| | |||| |+++- Initialization status: | |||| | 0 - (in progress) | |||| | 1 - SD/TF card not found | |||| | 2 - SD/TF card read error | |||| | 3 - could not find _DS_MENU.DAT | |||| | 4 - success | |||+-+---- Language: | ||| 1 - Japanese | ||| 2 - English | ||| 3 - Chinese | ||+------- Model: | || 0 - M3DS Simply | || 1 - R4 Revolution for DS +-++-------- Sub-language: 2:2 - French 2:4 - Korean 3:0? - Simplified Chinese 3:1? - Traditional Chinese
SD read request, but using the “save” FAT entry table.
SD read data, but using the “save” FAT entry table.
31 bit 0 ssss ssss ssss ssss ssss ssss sss? ???t |||| |||| |||| |||| |||| |||| ||| | |||| |||| |||| |||| |||| |||| ||| +- Type: |||| |||| |||| |||| |||| |||| ||| 0 - ROM |||| |||| |||| |||| |||| |||| ||| 1 - Save ++++-++++-++++-++++-++++-++++-+++------- Directory table address (aligned to 0x20 per FAT spec)
SD read request, but using the “ROM” FAT entry table.
Points to _DS_MENU.DAT on initialization.
SD read data, but using the “ROM” FAT entry table.
Card ID commands are not patched and use the timings from the retail game ROM header, but without L1 latency (that's what the SDK does). Those can be as fast as a single cycle of L2 latency, depending on the game.
Returns 0x00000000
when data is ready to access, 0x00000001
if ongoing.
Returns the 512 bytes read via the “SD read start” command.
Followed by 512 bytes of SD card data.
Returns 0x00000000
when data is ready to access, 0x00000001
if ongoing.
SD write start, but using the “save” FAT entry table.
SD write status, but using the “save” FAT entry table.
Same as SD read data, but applying a XOR-based obfuscation algorithm to the buffer's contents.
This is used by the stage 1 loader to decrypt _DS_MENU.DAT.
The algorithm was initially discovered by Yasu in 2007, and example source code is provided as follows, where key
is a 16-bit constant:
uint8_t[512] in; uint8_t[512] out; uint16_t key1 = key ^ currentSector; for (int i = 0; i < 512; i++) { // Derive key2 from key1. uint8_t key2 = ((key1 >> 7) & 0x80) | ((key1 >> 6) & 0x60) | ((key1 >> 5) & 0x10) | ((key1 >> 4) & 0x0C) | (key1 & 0x03); // Decrypt byte. dest[i] = src[i] ^ key2; // Derive next key1 from key2. uint16_t tmp = ((src[i] << 8) ^ key1); uint16_t tmpXor = 0; for (int ii = 0; ii < 16; ii++) tmpXor ^= (tmp >> ii); uint16_t newKey1 = 0; newKey1 |= ((tmpXor & 0x80) | (tmp & 0x7C)) << 8; newKey1 |= ((tmp ^ (tmpXor >> 14)) << 8) & 0x0300; newKey1 |= (((tmp >> 1) ^ tmp) >> 6) & 0xFC; newKey1 |= ((tmp ^ (tmpXor >> 1)) >> 8) & 0x03; key1 = newKey1; }