diff --git a/arm9/source/dumpOperations.cpp b/arm9/source/dumpOperations.cpp
index 1cdafc5..2d4355a 100644
--- a/arm9/source/dumpOperations.cpp
+++ b/arm9/source/dumpOperations.cpp
@@ -20,6 +20,36 @@ extern bool expansionPakFound;
static sNDSHeaderExt ndsCardHeader;
+void dumpFailMsg(bool save) {
+ font->clear(false);
+ font->printf(0, 0, false, Alignment::left, Palette::white, "Failed to dump the %s.", save ? "save" : "ROM");
+ font->update(false);
+
+ for (int i = 0; i < 60*2; i++) {
+ swiWaitForVBlank();
+ }
+}
+
+void saveWriteFailMsg(void) {
+ const std::string_view sizeError = "The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled!";
+
+ font->clear(false);
+ font->print(0, 0, false, sizeError, Alignment::left, Palette::red);
+ font->print(0, font->calcHeight(sizeError) + 1, false, "( OK)");
+ font->update(false);
+
+ u16 pressed;
+ do {
+ // Print time
+ font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
+ font->update(true);
+
+ scanKeys();
+ pressed = keysDownRepeat();
+ swiWaitForVBlank();
+ } while (!(pressed & KEY_A));
+}
+
//---------------------------------------------------------------------------------
// https://github.com/devkitPro/libnds/blob/master/source/common/cardEeprom.c#L74
// with Pokémon Mystery Dungeon - Explorers of Sky (128 KiB EEPROM) fixed
@@ -157,6 +187,19 @@ void cardEepromChipEraseFixed(void) {
}
}
+u32 cardNandGetSaveSize(void) {
+ switch(*(u32*)ndsCardHeader.gameCode & 0x00FFFFFF) {
+ case 0x00425855: // 'UXB'
+ return 8 << 20; // 8MByte - Jam with the Band
+ case 0x00524F55: // 'UOR'
+ return 16 << 20; // 16MByte - WarioWare D.I.Y.
+ case 0x004B5355: // 'USK'
+ return 64 << 20; // 64MByte - Face Training
+ }
+
+ return 0;
+}
+
void ndsCardSaveDump(const char* filename) {
FILE *out = fopen(filename, "wb");
if(out) {
@@ -165,29 +208,69 @@ void ndsCardSaveDump(const char* filename) {
font->print(0, 1, false, "Do not remove the NDS card.");
font->update(false);
- unsigned char *buffer;
- auxspi_extra card_type = auxspi_has_extra();
- if(card_type == AUXSPI_INFRARED) {
- int size = auxspi_save_size_log_2(card_type);
- int size_blocks;
- int type = auxspi_save_type(card_type);
- if(size < 16)
- size_blocks = 1;
- else
- size_blocks = 1 << (size - 16);
- u32 LEN = std::min(1 << size, 1 << 16);
- buffer = new unsigned char[LEN*size_blocks];
- auxspi_read_data(0, buffer, LEN*size_blocks, type, card_type);
- fwrite(buffer, 1, LEN*size_blocks, out);
- } else {
- int type = cardEepromGetTypeFixed();
- int size = cardEepromGetSizeFixed();
- buffer = new unsigned char[size];
- cardReadEeprom(0, buffer, size, type);
- fwrite(buffer, 1, size, out);
+ int type = cardEepromGetTypeFixed();
+
+ if(type == -1) { // NAND
+ u32 saveSize = cardNandGetSaveSize();
+
+ if(saveSize == 0) {
+ dumpFailMsg(true);
+ return;
+ }
+
+ u32 currentSize = saveSize;
+ FILE* destinationFile = fopen(filename, "wb");
+ if (destinationFile) {
+
+ font->print(0, 4, false, "Progress:");
+ font->print(0, 5, false, "[");
+ font->print(-1, 5, false, "]");
+ for (u32 src = 0; src < saveSize; src += 0x8000) {
+ // Print time
+ font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
+ font->update(true);
+
+ font->print((src / (saveSize / (SCREEN_COLS - 2))) + 1, 5, false, "=");
+ font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", src, saveSize);
+ font->update(false);
+
+ for (u32 i = 0; i < 0x8000; i += 0x200) {
+ cardRead(cardNandRwStart + src + i, copyBuf + i, true);
+ }
+ if (fwrite(copyBuf, 1, (currentSize >= 0x8000 ? 0x8000 : currentSize), destinationFile) < 1) {
+ dumpFailMsg(true);
+ break;
+ }
+ currentSize -= 0x8000;
+ }
+ fclose(destinationFile);
+ } else {
+ dumpFailMsg(true);
+ }
+ } else { // SPI
+ unsigned char *buffer;
+ auxspi_extra card_type = auxspi_has_extra();
+ if(card_type == AUXSPI_INFRARED) {
+ int size = auxspi_save_size_log_2(card_type);
+ int size_blocks;
+ int type = auxspi_save_type(card_type);
+ if(size < 16)
+ size_blocks = 1;
+ else
+ size_blocks = 1 << (size - 16);
+ u32 LEN = std::min(1 << size, 1 << 16);
+ buffer = new unsigned char[LEN*size_blocks];
+ auxspi_read_data(0, buffer, LEN*size_blocks, type, card_type);
+ fwrite(buffer, 1, LEN*size_blocks, out);
+ } else {
+ int size = cardEepromGetSizeFixed();
+ buffer = new unsigned char[size];
+ cardReadEeprom(0, buffer, size, type);
+ fwrite(buffer, 1, size, out);
+ }
+ delete[] buffer;
+ fclose(out);
}
- delete[] buffer;
- fclose(out);
}
}
@@ -210,118 +293,151 @@ void ndsCardSaveRestore(const char *filename) {
} while (!(pressed & (KEY_A | KEY_B)));
if(pressed & KEY_A) {
- auxspi_extra card_type = auxspi_has_extra();
- bool auxspi = card_type == AUXSPI_INFRARED;
- FILE *in = fopen(filename, "rb");
- if(in) {
- unsigned char *buffer;
- int size;
- int type;
- int length;
- unsigned int num_blocks = 0, shift = 0, LEN = 0;
- if(auxspi) {
- size = auxspi_save_size_log_2(card_type);
- type = auxspi_save_type(card_type);
- switch(type) {
- case 1:
- shift = 4; // 16 bytes
- break;
- case 2:
- shift = 5; // 32 bytes
- break;
- case 3:
- shift = 8; // 256 bytes
- break;
- default:
- return;
+ int type = cardEepromGetTypeFixed();
+
+ if(type == -1) { // NAND
+ if (cardInit(&ndsCardHeader) != 0) {
+ font->clear(false);
+ font->print(0, 0, false, "Unable to restore the save.");
+ font->update(false);
+ for (int i = 0; i < 60 * 2; i++) {
+ swiWaitForVBlank();
}
- LEN = 1 << shift;
- num_blocks = 1 << (size - shift);
- } else {
- type = cardEepromGetTypeFixed();
- size = cardEepromGetSizeFixed();
+ return;
}
+
+ u32 saveSize = cardNandGetSaveSize();
+
+ if(saveSize == 0) {
+ font->print(0, 0, false, "Unable to restore the save.");
+ font->update(false);
+ for (int i = 0; i < 60 * 2; i++) {
+ swiWaitForVBlank();
+ }
+ return;
+ }
+
+ FILE* in = fopen(filename, "rb");
+
fseek(in, 0, SEEK_END);
- length = ftell(in);
+ size_t length = ftell(in);
fseek(in, 0, SEEK_SET);
- if(length != (auxspi ? (int)(LEN * num_blocks) : size)) {
+ if(length != saveSize) {
fclose(in);
- const std::string_view sizeError = "The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled!";
+ saveWriteFailMsg();
+ return;
+ }
- font->clear(false);
- font->print(0, 0, false, sizeError, Alignment::left, Palette::red);
- font->print(0, font->calcHeight(sizeError) + 1, false, "( OK)");
- font->update(false);
-
- do {
+ u32 currentSize = saveSize;
+ if (in) {
+ font->print(0, 4, false, "Progress:");
+ font->print(0, 5, false, "[");
+ font->print(-1, 5, false, "]");
+ for (u32 dest = 0; dest < saveSize; dest += 0x8000) {
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
- scanKeys();
- pressed = keysDownRepeat();
- swiWaitForVBlank();
- } while (!(pressed & KEY_A));
- return;
- }
-
- font->clear(false);
- font->print(0, 0, false, "Restoring save...");
- font->print(0, 1, false, "Do not remove the NDS card.");
- font->print(0, 4, false, "Progress:");
- font->update(false);
-
- if(type == 3) {
- if(auxspi)
- auxspi_erase(card_type);
- else
- cardEepromChipEraseFixed();
- }
- if(auxspi){
- buffer = new unsigned char[LEN];
- font->print(0, 5, false, "[");
- font->print(-1, 5, false, "]");
- for(unsigned int i = 0; i < num_blocks; i++) {
- font->print((i * (SCREEN_COLS - 2) / num_blocks) + 1, 5, false, "=");
- font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", i * LEN, length);
+ font->print((dest / (saveSize / (SCREEN_COLS - 2))) + 1, 5, false, "=");
+ font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", dest, saveSize);
font->update(false);
- fread(buffer, 1, LEN, in);
- auxspi_write_data(i << shift, buffer, LEN, type, card_type);
- }
- } else {
- int blocks = size / 32;
- int written = 0;
- buffer = new unsigned char[blocks];
- font->print(0, 5, false, "[");
- font->print(-1, 5, false, "]");
- for(unsigned int i = 0; i < 32; i++) {
- font->print((i * (SCREEN_COLS - 2) / 32) + 1, 5, false, "=");
- font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", written, size);
- font->update(false);
-
- fread(buffer, 1, blocks, in);
- cardWriteEeprom(written, buffer, blocks, type);
- written += blocks;
+ fread(copyBuf, 1, 0x8000, in);
+ for (u32 i = 0; i < 0x8000; i += 0x800) {
+ cardWriteNand(copyBuf + i, cardNandRwStart + dest + i);
+ }
+ currentSize -= 0x8000;
}
+ fclose(in);
+ }
+ } else { // SPI
+ auxspi_extra card_type = auxspi_has_extra();
+ bool auxspi = card_type == AUXSPI_INFRARED;
+ FILE *in = fopen(filename, "rb");
+ if(in) {
+ unsigned char *buffer;
+ int size;
+ int length;
+ unsigned int num_blocks = 0, shift = 0, LEN = 0;
+ if(auxspi) {
+ size = auxspi_save_size_log_2(card_type);
+ type = auxspi_save_type(card_type);
+ switch(type) {
+ case 1:
+ shift = 4; // 16 bytes
+ break;
+ case 2:
+ shift = 5; // 32 bytes
+ break;
+ case 3:
+ shift = 8; // 256 bytes
+ break;
+ default:
+ return;
+ }
+ LEN = 1 << shift;
+ num_blocks = 1 << (size - shift);
+ } else {
+ size = cardEepromGetSizeFixed();
+ }
+ fseek(in, 0, SEEK_END);
+ length = ftell(in);
+ fseek(in, 0, SEEK_SET);
+ if(length != (auxspi ? (int)(LEN * num_blocks) : size)) {
+ fclose(in);
+
+ saveWriteFailMsg();
+ return;
+ }
+
+ font->clear(false);
+ font->print(0, 0, false, "Restoring save...");
+ font->print(0, 1, false, "Do not remove the NDS card.");
+ font->print(0, 4, false, "Progress:");
+ font->update(false);
+
+ if(type == 3) {
+ if(auxspi)
+ auxspi_erase(card_type);
+ else
+ cardEepromChipEraseFixed();
+ }
+ if(auxspi){
+ buffer = new unsigned char[LEN];
+ font->print(0, 5, false, "[");
+ font->print(-1, 5, false, "]");
+ for(unsigned int i = 0; i < num_blocks; i++) {
+ font->print((i * (SCREEN_COLS - 2) / num_blocks) + 1, 5, false, "=");
+ font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", i * LEN, length);
+ font->update(false);
+
+ fread(buffer, 1, LEN, in);
+ auxspi_write_data(i << shift, buffer, LEN, type, card_type);
+ }
+ } else {
+ int blocks = size / 32;
+ int written = 0;
+ buffer = new unsigned char[blocks];
+ font->print(0, 5, false, "[");
+ font->print(-1, 5, false, "]");
+ for(unsigned int i = 0; i < 32; i++) {
+ font->print((i * (SCREEN_COLS - 2) / 32) + 1, 5, false, "=");
+ font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", written, size);
+ font->update(false);
+
+ fread(buffer, 1, blocks, in);
+ cardWriteEeprom(written, buffer, blocks, type);
+ written += blocks;
+ }
+ }
+ delete[] buffer;
+ fclose(in);
}
- delete[] buffer;
- fclose(in);
}
}
}
-void dumpFailMsg(void) {
- font->clear(false);
- font->print(0, 0, false, "Failed to dump the ROM.");
- font->update(false);
-
- for (int i = 0; i < 60*2; i++) {
- swiWaitForVBlank();
- }
-}
-
void ndsCardDump(void) {
int pressed = 0;
//bool showGameCardMsgAgain = false;
@@ -551,7 +667,7 @@ void ndsCardDump(void) {
iprintf ("\x1b[8;0H");
iprintf ("Read:\n");
iprintf ("%i/%i Bytes ", (int)src, (int)romSize);
- cardRead (src, (void*)0x09000000+(src % 0x800000));
+ cardRead (src, (void*)0x09000000+(src % 0x800000), false);
}
iprintf("\x1b[15;0H");
iprintf("Please switch to the\nflashcard, then press A.\n");
@@ -616,17 +732,17 @@ void ndsCardDump(void) {
font->update(false);
for (u32 i = 0; i < 0x8000; i += 0x200) {
- cardRead (src+i, copyBuf+i);
+ cardRead (src+i, copyBuf+i, false);
}
if (fwrite(copyBuf, 1, (currentSize>=0x8000 ? 0x8000 : currentSize), destinationFile) < 1) {
- dumpFailMsg();
+ dumpFailMsg(false);
break;
}
currentSize -= 0x8000;
}
fclose(destinationFile);
} else {
- dumpFailMsg();
+ dumpFailMsg(false);
}
ndsCardSaveDump(destSavPath);
//}
@@ -752,7 +868,7 @@ void gbaCartDump(void) {
FILE* destinationFile = fopen(destPath, "wb");
if (destinationFile) {
if (fwrite(GBAROM, 1, romSize, destinationFile) < 1) {
- dumpFailMsg();
+ dumpFailMsg(false);
} else
// Check for 64MB GBA Video ROM
if (strncmp((char*)0x080000AC, "MSAE", 4)==0 // Shark Tale
@@ -774,14 +890,14 @@ void gbaCartDump(void) {
writeChange(cmd);
readChange();
if (fwrite(GBAROM + (0x1000 >> 1), 0x1000, 1, destinationFile) < 1) {
- dumpFailMsg();
+ dumpFailMsg(false);
break;
}
}
}
fclose(destinationFile);
} else {
- dumpFailMsg();
+ dumpFailMsg(false);
}
// Save file
diff --git a/arm9/source/ndsheaderbanner.h b/arm9/source/ndsheaderbanner.h
index 7d0c42f..133b2e3 100644
--- a/arm9/source/ndsheaderbanner.h
+++ b/arm9/source/ndsheaderbanner.h
@@ -85,7 +85,10 @@ typedef struct {
u32 romSize; //!< total size of the ROM.
u32 headerSize; //!< ROM header size.
- u32 zeros88[14];
+ u32 zeros88[3];
+ u16 nandRomEnd; //!< ROM region end for NAND games.
+ u16 nandRwStart; //!< RW region start for NAND games.
+ u32 zeros98[10];
u8 gbaLogo[156]; //!< Nintendo logo needed for booting the game.
u16 logoCRC16; //!< Nintendo Logo Checksum, CRC-16.
u16 headerCRC16; //!< header checksum, CRC-16.
diff --git a/arm9/source/read_card.c b/arm9/source/read_card.c
index 41f45c1..c247511 100644
--- a/arm9/source/read_card.c
+++ b/arm9/source/read_card.c
@@ -42,6 +42,18 @@ enum {
ERR_HEAD_CRC = 0x16,
} ERROR_CODES;
+// NAND Card commands
+// https://problemkaputt.de/gbatek-ds-cartridge-nand.htm
+#define CARD_CMD_NAND_WRITE_BUFFER 0x81
+#define CARD_CMD_NAND_COMMIT_BUFFER 0x82
+#define CARD_CMD_NAND_DISCARD_BUFFER 0x84
+#define CARD_CMD_NAND_WRITE_ENABLE 0x85
+#define CARD_CMD_NAND_ROM_MODE 0x8B
+#define CARD_CMD_NAND_RW_MODE 0xB2
+#define CARD_CMD_NAND_READ_STATUS 0xD6
+#define CARD_CMD_NAND_UNKNOWN 0xBB
+#define CARD_CMD_NAND_READ_ID 0x94
+
typedef union
{
char title[4];
@@ -55,11 +67,42 @@ static u32 portFlags = 0;
static u32 headerData[0x1000/sizeof(u32)] = {0};
static u32 secureArea[CARD_SECURE_AREA_SIZE/sizeof(u32)] = {0};
+static bool nandChip = false;
+static int nandSection = -1; // -1 = ROM, above that is the current 128 KiB section in RW
+u32 cardNandRomEnd = 0;
+u32 cardNandRwStart = 0;
+
static const u8 cardSeedBytes[] = {0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5};
static u32 getRandomNumber(void) {
- return 4; // chosen by fair dice roll.
- // guaranteed to be random.
+ return rand();
+}
+
+//---------------------------------------------------------------------------------
+// https://github.com/devkitPro/libnds/blob/105d4943dbac8f2bd99a47b22cd3ed48f96af083/source/common/card.c#L47-L62
+// but modified to write if CARD_WR is set.
+static void cardPolledTransferWrite(u32 flags, u32 *buffer, u32 length, const u8 *command) {
+//---------------------------------------------------------------------------------
+ cardWriteCommand(command);
+ REG_ROMCTRL = flags | CARD_BUSY;
+ u32 * target = buffer + length;
+ do {
+ // Read/write data if available
+ if (REG_ROMCTRL & CARD_DATA_READY) {
+ if (flags & CARD_WR) { // Write
+ if (NULL != buffer && buffer < target)
+ REG_CARD_DATA_RD = *buffer++;
+ else
+ REG_CARD_DATA_RD = 0;
+ } else { // Read
+ u32 data = REG_CARD_DATA_RD;
+ if (NULL != buffer && buffer < target)
+ *buffer++ = REG_CARD_DATA_RD;
+ else
+ (void)data;
+ }
+ }
+ } while (REG_ROMCTRL & CARD_BUSY);
}
static void decryptSecureArea (u32 gameCode, u32* secureArea, int iCardDevice)
@@ -276,7 +319,9 @@ static void switchToTwlBlowfish(sNDSHeaderExt* ndsHeader) {
int cardInit (sNDSHeaderExt* ndsHeader)
{
u32 portFlagsKey1, portFlagsSecRead;
- normalChip = false; // As defined by GBAtek, normal chip secure area is accessed in blocks of 0x200, other chip in blocks of 0x1000
+ normalChip = false; // As defined by GBAtek, normal chip secure area and header are accessed in blocks of 0x200, other chip in blocks of 0x1000
+ nandChip = false;
+ nandSection = -1;
int secureBlockNumber;
int i;
u8 cmdData[8] __attribute__ ((aligned));
@@ -285,7 +330,7 @@ int cardInit (sNDSHeaderExt* ndsHeader)
twlBlowfish = false;
sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart
- if (isDSiMode()) {
+ if (isDSiMode()) {
// Reset card slot
disableSlot1();
for(i = 0; i < 25; i++) { swiWaitForVBlank(); }
@@ -296,23 +341,25 @@ int cardInit (sNDSHeaderExt* ndsHeader)
cardParamCommand (CARD_CMD_DUMMY, 0,
CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F),
NULL, 0);
- } else {
- REG_ROMCTRL=0;
- REG_AUXSPICNT=0;
- //ioDelay2(167550);
- for(i = 0; i < 25; i++) { swiWaitForVBlank(); }
- REG_AUXSPICNT=CARD_CR1_ENABLE|CARD_CR1_IRQ;
- REG_ROMCTRL=CARD_nRESET|CARD_SEC_SEED;
- while(REG_ROMCTRL&CARD_BUSY) ;
- cardReset();
- while(REG_ROMCTRL&CARD_BUSY) ;
}
+ REG_ROMCTRL=0;
+ REG_AUXSPICNT=0;
+ //ioDelay2(167550);
+ for(i = 0; i < 25; i++) { swiWaitForVBlank(); }
+ REG_AUXSPICNT=CARD_CR1_ENABLE|CARD_CR1_IRQ;
+ REG_ROMCTRL=CARD_nRESET|CARD_SEC_SEED;
+ while(REG_ROMCTRL&CARD_BUSY) ;
+ cardReset();
+ while(REG_ROMCTRL&CARD_BUSY) ;
+
toncset(headerData, 0, 0x1000);
- u32 iCardId=cardReadID(CARD_CLK_SLOW);
+ u32 iCardId=cardReadID(CARD_CLK_SLOW);
while(REG_ROMCTRL & CARD_BUSY);
- //u32 iCheapCard=iCardId&0x80000000;
+
+ normalChip = (iCardId & BIT(31)) != 0; // ROM chip ID MSB
+ nandChip = (iCardId & BIT(27)) != 0; // Card has a NAND chip
// Read the header
cardParamCommand (CARD_CMD_HEADER_READ, 0,
@@ -324,9 +371,17 @@ int cardInit (sNDSHeaderExt* ndsHeader)
if ((ndsHeader->unitCode != 0) || (ndsHeader->dsi_flags != 0))
{
// Extended header found
- cardParamCommand (CARD_CMD_HEADER_READ, 0,
- CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(4) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F),
- (void*)headerData, 0x1000/sizeof(u32));
+ if(normalChip) {
+ for(int i = 0; i < 8; i++) {
+ cardParamCommand (CARD_CMD_HEADER_READ, i * 0x200,
+ CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F),
+ headerData + i * 0x200 / sizeof(u32), 0x200/sizeof(u32));
+ }
+ } else {
+ cardParamCommand (CARD_CMD_HEADER_READ, 0,
+ CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(4) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F),
+ (void*)headerData, 0x1000/sizeof(u32));
+ }
if (ndsHeader->dsi1[0]==0xFFFFFFFF && ndsHeader->dsi1[1]==0xFFFFFFFF
&& ndsHeader->dsi1[2]==0xFFFFFFFF && ndsHeader->dsi1[3]==0xFFFFFFFF)
{
@@ -358,7 +413,6 @@ int cardInit (sNDSHeaderExt* ndsHeader)
((ndsHeader->cardControlBF & (CARD_CLK_SLOW|CARD_DELAY1(0x1FFF))) + ((ndsHeader->cardControlBF & CARD_DELAY2(0x3F)) >> 16));
// Adjust card transfer method depending on the most significant bit of the chip ID
- normalChip = (iCardId & 0x80000000) != 0; // ROM chip ID MSB
if (!normalChip) {
portFlagsKey1 |= CARD_SEC_LARGE;
}
@@ -440,10 +494,23 @@ int cardInit (sNDSHeaderExt* ndsHeader)
//return normalChip ? ERR_SEC_NORM : ERR_SEC_OTHR;
}
+ // Set NAND card section location variables
+ if (nandChip) {
+ if(ndsHeader->nandRomEnd != 0) {
+ // TWL cards (Face Training) multiply by 0x80000 instead of 0x20000
+ cardNandRomEnd = ndsHeader->nandRomEnd * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000);
+ cardNandRwStart = ndsHeader->nandRwStart * (ndsHeader->unitCode == 0 ? 0x20000 : 0x80000);
+ } else {
+ // Jam with the Band (J) (大合奏!バンドブラザーズ) doesn't have the RW section in the header
+ cardNandRomEnd = 0x7200000;
+ cardNandRwStart = 0x7200000;
+ }
+ }
+
return ERR_NONE;
}
-void cardRead (u32 src, void* dest)
+void cardRead (u32 src, void* dest, bool nandSave)
{
sNDSHeaderExt* ndsHeader = (sNDSHeaderExt*)headerData;
@@ -464,12 +531,53 @@ void cardRead (u32 src, void* dest)
return;
}
+ if (nandChip) {
+ if ((src < cardNandRomEnd || !nandSave) && nandSection != -1) {
+ cardParamCommand(CARD_CMD_NAND_ROM_MODE, 0, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+ nandSection = -1;
+ } else if (src >= cardNandRwStart && nandSection != (src - cardNandRwStart) / (128 << 10) && nandSave) {
+ if(nandSection != -1) // Need to switch back to ROM mode before switching to another RW section
+ cardParamCommand(CARD_CMD_NAND_ROM_MODE, 0, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+ cardParamCommand(CARD_CMD_NAND_RW_MODE, src, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+ nandSection = (src - cardNandRwStart) / (128 << 10);
+ }
+ }
+
cardParamCommand (CARD_CMD_DATA_READ, src,
portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1),
dest, 0x200/sizeof(u32));
- if (src > ndsHeader->romSize) {
+ if (src > ndsHeader->romSize && !(nandSave && src >= cardNandRwStart)) {
switchToTwlBlowfish(ndsHeader);
}
}
+// src must be a 0x800 byte array
+void cardWriteNand (void* src, u32 dest)
+{
+ if (dest < cardNandRwStart || !nandChip)
+ return;
+
+ if (nandSection != (dest - cardNandRwStart) / (128 << 10)) {
+ if(nandSection != -1) // Need to switch back to ROM mode before switching to another RW section
+ cardParamCommand(CARD_CMD_NAND_ROM_MODE, 0, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+ cardParamCommand(CARD_CMD_NAND_RW_MODE, dest, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+ nandSection = (dest - cardNandRwStart) / (128 << 10);
+ }
+
+ cardParamCommand(CARD_CMD_NAND_WRITE_ENABLE, 0, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+
+ const u8 cmdData[8] = {0, 0, 0, dest, dest >> 8, dest >> 16, dest >> 24, CARD_CMD_NAND_WRITE_BUFFER};
+ for (int i = 0; i < 4; i++) {
+ cardPolledTransferWrite(portFlags | CARD_ACTIVATE | CARD_WR | CARD_nRESET | CARD_BLK_SIZE(1), src + (i * 0x200), 0x200 / sizeof(u32), cmdData);
+ }
+
+ cardParamCommand(CARD_CMD_NAND_COMMIT_BUFFER, 0, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+
+ u32 status;
+ do {
+ cardParamCommand(CARD_CMD_NAND_READ_STATUS, 0, portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(7), &status, 1);
+ } while((status & BIT(5)) == 0);
+
+ cardParamCommand(CARD_CMD_NAND_DISCARD_BUFFER, 0, portFlags | CARD_ACTIVATE | CARD_nRESET, NULL, 0);
+}
diff --git a/arm9/source/read_card.h b/arm9/source/read_card.h
index e54d37a..56f94a8 100644
--- a/arm9/source/read_card.h
+++ b/arm9/source/read_card.h
@@ -36,9 +36,14 @@
extern "C" {
#endif
+extern u32 cardNandRomEnd;
+extern u32 cardNandRwStart;
+
int cardInit (sNDSHeaderExt* ndsHeader);
-void cardRead (u32 src, void* dest);
+void cardRead (u32 src, void* dest, bool nandSave);
+
+void cardWriteNand (void* src, u32 dest);
#ifdef __cplusplus
}