From d75ab87bfb20ab61e006cbd5555fd8bd2f49c763 Mon Sep 17 00:00:00 2001 From: Pk11 Date: Mon, 27 Sep 2021 00:41:06 -0500 Subject: [PATCH] WIP: Fix NAND cart dumping --- arm9/source/dumpOperations.cpp | 143 ++++++++++++++++++++++++--------- arm9/source/ndsheaderbanner.h | 5 +- arm9/source/read_card.c | 34 +++++++- 3 files changed, 143 insertions(+), 39 deletions(-) diff --git a/arm9/source/dumpOperations.cpp b/arm9/source/dumpOperations.cpp index 1cdafc5..97b711c 100644 --- a/arm9/source/dumpOperations.cpp +++ b/arm9/source/dumpOperations.cpp @@ -20,6 +20,16 @@ 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(); + } +} + //--------------------------------------------------------------------------------- // 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 +167,28 @@ void cardEepromChipEraseFixed(void) { } } +u32 cardNandGetSaveSize(void) { + u32 id = cardReadID(CARD_CLK_SLOW); + + u16 flags = id >> 16; + + if ((id & 0xFF) == 0xEC) { // Samsung + switch(flags) { + case 0x8801: + return 8 << 20; // 8MByte - Jam with the Band + break; + case 0x8800: + return 16 << 20; // 16MByte - WarioWare D.I.Y. + break; + case 0xE800: + return 82 << 20; // 82MByte - Face Training + break; + } + } + + return 0; +} + void ndsCardSaveDump(const char* filename) { FILE *out = fopen(filename, "wb"); if(out) { @@ -165,29 +197,76 @@ 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; + } + + // testing print + font->clear(false); + font->printf(0, 0, false, Alignment::left, Palette::white, "Found NAND save: Size: %d MiB", saveSize >> 20); + font->update(false); + for(int i = 0; i < 120; i++) + swiWaitForVBlank(); + + 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(src+i, copyBuf+i); + } + 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); } } @@ -312,16 +391,6 @@ void ndsCardSaveRestore(const char *filename) { } } -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; @@ -619,14 +688,14 @@ void ndsCardDump(void) { cardRead (src+i, copyBuf+i); } 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 +821,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 +843,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..dcfa798 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_FLUSH_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]; @@ -51,6 +63,8 @@ typedef union static bool twlBlowfish = false; static bool normalChip = false; // As defined by GBAtek, normal chip secure area is accessed in blocks of 0x200, other chip in blocks of 0x1000 +static bool nandChip = false; +static bool nandRomMode = true; static u32 portFlags = 0; static u32 headerData[0x1000/sizeof(u32)] = {0}; static u32 secureArea[CARD_SECURE_AREA_SIZE/sizeof(u32)] = {0}; @@ -277,6 +291,7 @@ 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 + nandRomMode = true; int secureBlockNumber; int i; u8 cmdData[8] __attribute__ ((aligned)); @@ -310,10 +325,17 @@ int cardInit (sNDSHeaderExt* ndsHeader) 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; + // Check if NAND + nandChip = (iCardId >> 24) & BIT(3); + if (nandChip) { + cardParamCommand(CARD_CMD_NAND_ROM_MODE, 0, portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), NULL, 0); + nandRomMode = true; + } + // Read the header cardParamCommand (CARD_CMD_HEADER_READ, 0, CARD_ACTIVATE | CARD_nRESET | CARD_CLK_SLOW | CARD_BLK_SIZE(1) | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F), @@ -464,6 +486,16 @@ void cardRead (u32 src, void* dest) return; } + if (nandChip) { + if (src < ndsHeader->nandRomEnd * 0x20000 /*dsi: 80000h?*/ && !nandRomMode) { + cardParamCommand(CARD_CMD_NAND_ROM_MODE, 0, portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), NULL, 0); + nandRomMode = true; + } else if (src > ndsHeader->nandRwStart * 0x20000 /*dsi: 80000h?*/ && nandRomMode) { + cardParamCommand(CARD_CMD_NAND_RW_MODE, 0, portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), NULL, 0); + nandRomMode = false; + } + } + cardParamCommand (CARD_CMD_DATA_READ, src, portFlags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(1), dest, 0x200/sizeof(u32));