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 }