From db8bf37a2a91f16d6ef14534464ed98d83e3abbe Mon Sep 17 00:00:00 2001 From: RocketRobz Date: Thu, 30 Jan 2020 01:19:47 -0700 Subject: [PATCH] Add DS(i) save dumping --- README.md | 9 +- arm9/source/auxspi.cpp | 443 +++++++++++++++++++++++++++++++++ arm9/source/auxspi.h | 82 ++++++ arm9/source/auxspi_core.inc | 92 +++++++ arm9/source/driveMenu.cpp | 210 +--------------- arm9/source/dumpOperations.cpp | 251 +++++++++++++++++++ arm9/source/dumpOperations.h | 7 + 7 files changed, 881 insertions(+), 213 deletions(-) create mode 100644 arm9/source/auxspi.cpp create mode 100644 arm9/source/auxspi.h create mode 100644 arm9/source/auxspi_core.inc create mode 100644 arm9/source/dumpOperations.cpp create mode 100644 arm9/source/dumpOperations.h diff --git a/README.md b/README.md index 81906e0..fc216aa 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ Once everything is downloaded and installed, `git clone` this repository, naviga ![](https://gbatemp.b-cdn.net/attachments/snap_212809-png.147117/)![](https://gbatemp.b-cdn.net/attachments/snap_211051-png.147114/)![](https://gbatemp.b-cdn.net/attachments/file-options-v1-3-0-no-border-png.147118/) ## Credits -* RocketRobz: Creator of GodMode9i. -* zacchi4k: Creator of the GodMode9i logo used in v1.3.1 and onwards. -* devkitPro/WinterMute: devkitARM, libnds, original nds-hb-menu code, and screenshot code. -* d0k3: Original GM9 app and name for the Nintendo 3DS, which this is inspired by. +* @RocketRobz: Creator of GodMode9i. +* @zacchi4k: Creator of the GodMode9i logo used in v1.3.1 and onwards. +* @Edo9300: Save reading code from his save manager tool. +* @devkitPro: devkitARM, libnds, original nds-hb-menu code, and screenshot code. +* @d0k3: Original GM9 app and name for the Nintendo 3DS, which this is inspired by. diff --git a/arm9/source/auxspi.cpp b/arm9/source/auxspi.cpp new file mode 100644 index 0000000..13f12e6 --- /dev/null +++ b/arm9/source/auxspi.cpp @@ -0,0 +1,443 @@ +/* + * savegame_manager: a tool to backup and restore savegames from Nintendo + * DS cartridges. Nintendo DS and all derivative names are trademarks + * by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash. + * + * auxspi.cpp: A thin reimplementation of the AUXSPI protocol + * (high level functions) + * + * Copyright (C) Pokedoc (2010) + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "auxspi.h" +#include "tonccpy.h" + +#include +#include + +using std::max; + +#include "auxspi_core.inc" + + +static uint8 data[0x8000] = {0}; + +#define EXTRA_ARRAY_SIZE 16 + +static u32 extra_id[EXTRA_ARRAY_SIZE]; +static u8 extra_size[EXTRA_ARRAY_SIZE]; + +// ======================================================== +// local functions +uint8 jedec_table(uint32 id) +{ + switch (id) { + // 256 kB + case 0x204012: + case 0x621600: + return 0x12; + // 512 kB + case 0x204013: + case 0x621100: + return 0x13; + // 1 MB + case 0x204014: + return 0x14; + // 2 MB (not sure if this exists, but I vaguely remember something...) + case 0x204015: + return 0x15; + // 8 MB (Band Brothers DX) + case 0x202017: // which one? (more work is required to unlock this save chip!) + case 0x204017: + return 0x17; + default: { + for (int i = 0; i < EXTRA_ARRAY_SIZE; i++) { + if (extra_id[i] == id) + return extra_size[i]; + } + return 0; // unknown save type! + } + }; +} + +uint8 type2_size(auxspi_extra extra) +{ + static const uint32 offset0 = (8*1024-1); // 8KB + static const uint32 offset1 = (2*8*1024-1); // 16KB + u8 buf1; // +0k data read -> write + u8 buf2; // +8k data read -> read + u8 buf3; // +0k ~data write + u8 buf4; // +8k data new comp buf2 + auxspi_read_data(offset0, &buf1, 1, 2, extra); + auxspi_read_data(offset1, &buf2, 1, 2, extra); + buf3=~buf1; + auxspi_write_data(offset0, &buf3, 1, 2, extra); + auxspi_read_data (offset1, &buf4, 1, 2, extra); + auxspi_write_data(offset0, &buf1, 1, 2, extra); + if(buf4!=buf2) // +8k + return 0x0d; // 8KB(64kbit) + else + return 0x10; // 64KB(512kbit) +} + +// ======================================================== +uint8 auxspi_save_type(auxspi_extra extra) +{ + uint32 jedec = auxspi_save_jedec_id(extra); // 9f + int8 sr = auxspi_save_status_register(extra); // 05 + + if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) return 1; + if ((sr & 0xfd) == 0x00 && (jedec == 0x00ffffff)) return 2; + if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) return 3; + + return 0; +} + +uint32 auxspi_save_size(auxspi_extra extra) +{ + return 1 << auxspi_save_size_log_2(extra); +} + +uint8 auxspi_save_size_log_2(auxspi_extra extra) +{ + uint8 type = auxspi_save_type(extra); + switch (type) { + case 1: + return 0x09; // 512 bytes + break; + case 2: + return type2_size(extra); + break; + case 3: + return jedec_table(auxspi_save_jedec_id(extra)); + break; + default: + return 0; + } +} + +uint32 auxspi_save_jedec_id(auxspi_extra extra) +{ + uint32 id = 0; + if (extra) + auxspi_disable_extra(extra); + + auxspi_open(0); + auxspi_write(0x9f); + id |= auxspi_read() << 16; + id |= auxspi_read() << 8; + id |= auxspi_read(); + auxspi_close(); + + return id; +} + +uint8 auxspi_save_status_register(auxspi_extra extra) +{ + uint8 sr = 0; + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + auxspi_write(0x05); + sr = auxspi_read(); + auxspi_close(); + return sr; +} + +void auxspi_read_data(uint32 addr, uint8* buf, uint32 cnt, uint8 type, auxspi_extra extra) +{ + if (type == 0) + type = auxspi_save_type(extra); + if (type == 0) + return; + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + auxspi_write(0x03 | ((type == 1) ? addr>>8<<3 : 0)); + if (type == 3) { + auxspi_write((addr >> 16) & 0xFF); + } + if (type >= 2) { + auxspi_write((addr >> 8) & 0xFF); + } + auxspi_write(addr & 0xFF); + while (cnt > 0) { + *buf++ = auxspi_read(); + cnt--; + } + auxspi_close(); +} + +void auxspi_write_data(uint32 addr, uint8 *buf, uint32 cnt, uint8 type, auxspi_extra extra) +{ + if (type == 0) + type = auxspi_save_type(); + if (type == 0) + return; + + uint32 addr_end = addr + cnt; + unsigned int i; + unsigned int maxblocks = 32; + if(type == 1) maxblocks = 16; + if(type == 2) maxblocks = 32; + if(type == 3) maxblocks = 256; + + // we can only write a finite amount of data at once, so we need a separate loop + // for multiple passes. + while (addr < addr_end) { + if (extra) + auxspi_disable_extra(extra); + // swiWaitForVBlank(); + // for (int i = 0; i < 30; i++) { swiWaitForVBlank(); } + auxspi_open(0); + // set WEL (Write Enable Latch) + auxspi_write(0x06); + auxspi_close_lite(); + + if (extra) + auxspi_disable_extra(extra); + // swiWaitForVBlank(); + // for (int i = 0; i < 30; i++) { swiWaitForVBlank(); } + auxspi_open(0); + // send initial "write" command + if(type == 1) { + auxspi_write(0x02 | (addr & BIT(8)) >> (8-3)); + auxspi_write(addr & 0xFF); + } + else if(type == 2) { + auxspi_write(0x02); + auxspi_write((addr >> 8) & 0xff); + auxspi_write(addr & 0xFF); + } + else if(type == 3) { + auxspi_write(0x02); + auxspi_write((addr >> 16) & 0xff); + auxspi_write((addr >> 8) & 0xff); + auxspi_write(addr & 0xFF); + } + + for (i=0; addr < addr_end && i < maxblocks; i++, addr++) { + auxspi_write(*buf++); + } + // iprintf("%d\n",addr); + auxspi_close_lite(); + // swiWaitForVBlank(); + // iprintf("wrote\n"); + // Delay a second for the DS card to stabilise + // swiWaitForVBlank(); + // for (int i = 0; i < 30; i++) { swiWaitForVBlank(); } + // while(1) { + // swiWaitForVBlank(); + // scanKeys(); + // if(keysDown()&KEY_A) break; + // } + + // wait programming to finish + if (extra) + auxspi_disable_extra(extra); + // swiWaitForVBlank(); + // for (int i = 0; i < 30; i++) { swiWaitForVBlank(); } + auxspi_open(0); + auxspi_write(5); + auxspi_wait_wip(); + auxspi_wait_busy(); + auxspi_close(); + // swiWaitForVBlank(); + // for (int i = 0; i < 30; i++) { swiWaitForVBlank(); } + } +} + +void auxspi_disable_extra(auxspi_extra extra) +{ + switch (extra) { + case AUXSPI_INFRARED: + auxspi_disable_infrared_core(); + break; + case AUXSPI_BBDX: + // TODO: + auxspi_disable_big_protection(); + break; + case AUXSPI_BLUETOOTH: + // TODO + //auxspi_disable_bluetooth(); + break; + default:; + } + swiWaitForVBlank(); +} + +void auxspi_disable_infrared() +{ + auxspi_disable_infrared_core(); +} + +void auxspi_disable_big_protection() +{ + static bool doonce = false; + if (doonce) + return; + doonce = true; + sysSetBusOwners(true, true); + + auxspi_open(3); + auxspi_write(0xf1); + auxspi_wait_busy(); + auxspi_close_lite(); + + auxspi_open(3); + auxspi_write(0x6); + auxspi_wait_busy(); + auxspi_close_lite(); + + auxspi_open(3); + auxspi_write(0xfa); + auxspi_wait_busy(); + auxspi_write(0x1); + auxspi_wait_busy(); + auxspi_write(0x31); + auxspi_wait_busy(); + auxspi_close(); + // -- + + auxspi_open(3); + auxspi_write(0x14); + auxspi_wait_busy(); + auxspi_close_lite(); + + auxspi_open(3); + auxspi_write(0x6); + auxspi_wait_busy(); + auxspi_close_lite(); + + auxspi_open(3); + auxspi_write(0xf8); + auxspi_wait_busy(); + auxspi_write(0x1); + auxspi_wait_busy(); + auxspi_write(0x0); + auxspi_wait_busy(); + auxspi_close(); + // -- + + auxspi_open(3); + auxspi_write(0xe); + auxspi_wait_busy(); + auxspi_close(); + +} + +auxspi_extra auxspi_has_extra() +{ + sysSetBusOwners(true, true); + + // Trying to read the save size in IR mode will fail on non-IR devices. + // If we have success, it is an IR device. + u8 size2 = auxspi_save_size_log_2(AUXSPI_INFRARED); + if (size2 > 0) + return AUXSPI_INFRARED; + + // It is not an IR game, so maybe it is a regular game. + u8 size1 = auxspi_save_size_log_2(); + if (size1 > 0) + return AUXSPI_DEFAULT; + +#if 0 + // EXPERIMENTAL: verify that flash cards do not answer with the same signature! + // Look for BBDX (which always returns "ff" on every command) + uint32 jedec = auxspi_save_jedec_id(); // 9f + //int8 sr = auxspi_save_status_register(); // 05 + if (jedec == 0x00ffffff) + return AUXSPI_BBDX; +#endif + + // TODO: add support for Pokemon Typing DS (as soon as we figure out how) + + return AUXSPI_FLASH_CARD; +} + +void auxspi_erase(auxspi_extra extra) +{ + uint8 type = auxspi_save_type(extra); + if (type == 3) { + uint32 size; + size = 1 << (auxspi_save_size_log_2(extra) - 16); + for (unsigned int i = 0; i < size; i++) { + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + // set WEL (Write Enable Latch) + auxspi_write(0x06); + auxspi_close_lite(); + + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + auxspi_write(0xd8); + auxspi_write(i); + auxspi_write(0); + auxspi_write(0); + auxspi_close_lite(); + + // wait for programming to finish + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + auxspi_write(5); + auxspi_wait_wip(); + auxspi_wait_busy(); + auxspi_close(); + } + } else { + int32 size = 1 << max(0, (auxspi_save_size_log_2(extra) - 15)); + toncset(data, 0, 0x8000); + for (int i = 0; i < size; i++) { + auxspi_write_data(i << 15, data, 0x8000, type, extra); + } + } +} + +void auxspi_erase_sector(u32 sector, auxspi_extra extra) +{ + uint8 type = auxspi_save_type(extra); + if (type == 3) { + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + // set WEL (Write Enable Latch) + auxspi_write(0x06); + auxspi_close_lite(); + + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + auxspi_write(0xd8); + auxspi_write(sector & 0xff); + auxspi_write((sector >> 8) & 0xff); + auxspi_write((sector >> 8) & 0xff); + auxspi_close_lite(); + + // wait for programming to finish + if (extra) + auxspi_disable_extra(extra); + auxspi_open(0); + auxspi_write(5); + auxspi_wait_wip(); + auxspi_wait_busy(); + auxspi_close(); + } +} diff --git a/arm9/source/auxspi.h b/arm9/source/auxspi.h new file mode 100644 index 0000000..e60fe3b --- /dev/null +++ b/arm9/source/auxspi.h @@ -0,0 +1,82 @@ +/* + * savegame_manager: a tool to backup and restore savegames from Nintendo + * DS cartridges. Nintendo DS and all derivative names are trademarks + * by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash. + * + * auxspi.h: Header for auxspi.cpp + * + * Copyright (C) Pokedoc (2010) + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + This is a thin reimplementation of the AUXSPI protocol at low levels. + It is used to implement various experimental procedures to test accessing the HG/SS save chip. */ + +#ifndef SPI_BUS_H +#define SPI_BUS_H + +#include + +// This is a handy typedef for nonstandard SPI buses, i.e. games with some +// extra hardware on the cartridge. The following values mean: +// AUXSPI_DEFAULT: A regular game with no exotic hardware. +// AUXSPI_INFRARED: A game with an infrared transceiver. +// Games known to use this hardware: +// - Personal Trainer: Walking (aka Laufrhytmus DS, Walk With Me, ...) +// - Pokemon HeartGold/SoulSilver/Black/White +// AUXSPI_BBDX: A game with what seems to be an extra protection against reading +// out the chip. Exclusively found on Band Brothers DX. +// AUXSPI_BLUETOOTH: A game with a Bluetooth transceiver. The only game using this +// hardware is Pokemon Typing DS. +// +// NOTE: This library does *not* support BBDX (I do have the game, but did not find the +// time to reverse engineer this; besides, a separate homebrew for this game already exists), +// and also *not* BLUETOOTH (the game is Japan-only, and I am from Europe. Plus I can't +// read Japanese. And it is unlikely that this game will ever make it here.) +// +typedef enum { + AUXSPI_DEFAULT, + AUXSPI_INFRARED, + AUXSPI_BBDX, + AUXSPI_BLUETOOTH, + AUXSPI_FLASH_CARD = 999 +} auxspi_extra; + +// These functions reimplement relevant parts of "card.cpp", in a way that is easier to modify. +uint8 auxspi_save_type(auxspi_extra extra = AUXSPI_DEFAULT); +uint32 auxspi_save_size(auxspi_extra extra = AUXSPI_DEFAULT); +uint8 auxspi_save_size_log_2(auxspi_extra extra = AUXSPI_DEFAULT); +uint32 auxspi_save_jedec_id(auxspi_extra extra = AUXSPI_DEFAULT); +uint8 auxspi_save_status_register(auxspi_extra extra = AUXSPI_DEFAULT); +void auxspi_read_data(uint32 addr, uint8* buf, uint32 cnt, uint8 type = 0,auxspi_extra extra = AUXSPI_DEFAULT); +void auxspi_write_data(uint32 addr, uint8 *buf, uint32 cnt, uint8 type = 0,auxspi_extra extra = AUXSPI_DEFAULT); +void auxspi_erase(auxspi_extra extra = AUXSPI_DEFAULT); +void auxspi_erase_sector(u32 sector, auxspi_extra extra = AUXSPI_DEFAULT); + +// These functions are used to identify exotic hardware. +auxspi_extra auxspi_has_extra(); +//bool auxspi_has_infrared(); + +void auxspi_disable_extra(auxspi_extra extra = AUXSPI_DEFAULT); +void auxspi_disable_infrared(); +void auxspi_disable_big_protection(); + +// The following function returns true if this is a type 3 save (big saves, Flash memory), but +// the JEDEC ID is not known. +bool auxspi_is_unknown_type3(auxspi_extra extra = AUXSPI_DEFAULT); + +#endif \ No newline at end of file diff --git a/arm9/source/auxspi_core.inc b/arm9/source/auxspi_core.inc new file mode 100644 index 0000000..2fe0f9d --- /dev/null +++ b/arm9/source/auxspi_core.inc @@ -0,0 +1,92 @@ +/* + * savegame_manager: a tool to backup and restore savegames from Nintendo + * DS cartridges. Nintendo DS and all derivative names are trademarks + * by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash. + * + * auxspi_core.inc: A thin reimplementation of the AUXSPI protocol + * (low level functions) + * + * Copyright (C) Pokedoc (2010) + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + + +inline void auxspi_wait_busy() +{ + while (REG_AUXSPICNT & 0x80); +} + +inline void auxspi_wait_wip() +{ + do { REG_AUXSPIDATA = 0; auxspi_wait_busy(); } while (REG_AUXSPIDATA & 0x01); // WIP (Write In Progress) ? +} + +inline void auxspi_open(uint8 device) +{ + REG_AUXSPICNT = 0xa040 | (device & 3); + auxspi_wait_busy(); +} + +inline void auxspi_close() +{ + REG_AUXSPIDATA = 0; + auxspi_wait_busy(); + REG_AUXSPICNT = 0; + auxspi_wait_busy(); +} + +inline void auxspi_close_lite() +{ + REG_AUXSPICNT = 0x40; + auxspi_wait_busy(); +} + +inline uint8 auxspi_transfer(uint8 out) +{ + REG_AUXSPIDATA = out; + auxspi_wait_busy(); + return REG_AUXSPIDATA; +} + +inline void auxspi_write(uint8 out) +{ + auxspi_transfer(out); +} + +inline uint8 auxspi_read() +{ + return auxspi_transfer(0); +} + +inline uint16 auxspi_read_16() +{ + REG_AUXSPIDATA = 0; + auxspi_wait_busy(); + return REG_AUXSPIDATA; +} + +inline void auxspi_disable_infrared_core() +{ + auxspi_open(0); + swiDelay(1200); + auxspi_open(2); + auxspi_write(0); + swiDelay(1200); +} diff --git a/arm9/source/driveMenu.cpp b/arm9/source/driveMenu.cpp index c5e9131..4212fe2 100644 --- a/arm9/source/driveMenu.cpp +++ b/arm9/source/driveMenu.cpp @@ -29,11 +29,9 @@ #include "main.h" #include "date.h" #include "screenshot.h" +#include "dumpOperations.h" #include "driveOperations.h" #include "fileOperations.h" -#include "ndsheaderbanner.h" -#include "read_card.h" -#include "tonccpy.h" #define SCREEN_COLS 32 #define ENTRIES_PER_SCREEN 22 @@ -53,212 +51,6 @@ static int dmMaxCursors = -1; static u8 gbaFixedValue = 0; -void ndsCardDump(void) { - int pressed = 0; - - printf ("\x1b[0;27H"); - printf ("\x1B[42m"); // Print green color - printf ("_____"); // Clear time - consoleInit(NULL, 1, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); - printf ("\x1B[47m"); // Print foreground white color - printf("Dump NDS card ROM to\n"); - printf("\"%s:/gm9i/out\"?\n", (sdMounted ? "sd" : "fat")); - printf("( yes, trim, no)"); - while (true) { - // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do - do { - scanKeys(); - pressed = keysDownRepeat(); - swiWaitForVBlank(); - } while (!(pressed & KEY_A) && !(pressed & KEY_Y) && !(pressed & KEY_B)); - - if ((pressed & KEY_A) || (pressed & KEY_Y)) { - consoleClear(); - char folderPath[2][256]; - sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat")); - sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat")); - if (access(folderPath[0], F_OK) != 0) { - printf("Creating directory..."); - mkdir(folderPath[0], 0777); - } - if (access(folderPath[1], F_OK) != 0) { - printf ("\x1b[0;0H"); - printf("Creating directory..."); - mkdir(folderPath[1], 0777); - } - consoleClear(); - // Read header - sNDSHeaderExt* ndsCardHeader = (sNDSHeaderExt*)malloc(0x1000); - if (cardInit (ndsCardHeader) == 0) { - printf("Dumping...\n"); - printf("Do not remove the NDS card.\n"); - } else { - printf("Unable to dump the ROM.\n"); - for (int i = 0; i < 60*2; i++) { - swiWaitForVBlank(); - } - free(ndsCardHeader); - return; - } - char gameTitle[13] = {0}; - tonccpy(gameTitle, ndsCardHeader->gameTitle, 12); - char gameCode[7] = {0}; - tonccpy(gameCode, ndsCardHeader->gameCode, 6); - bool trimRom = (pressed & KEY_Y); - char romBuffer[0x200]; - char destPath[256]; - sprintf(destPath, "%s:/gm9i/out/%s_%s_%x%s.nds", (sdMounted ? "sd" : "fat"), gameTitle, gameCode, ndsCardHeader->romversion, (trimRom ? "_trim" : "")); - //char destSavPath[256]; - //sprintf(destSavPath, "fat:/gm9i/out/%s_%s%s_%x.sav", gbaHeaderGameTitle, gbaHeaderGameCode, gbaHeaderMakerCode, gbaHeaderSoftwareVersion); - // Determine ROM size - u32 romSize = 0; - if (trimRom) { - romSize = ((ndsCardHeader->unitCode != 0) && (ndsCardHeader->twlRomSize > 0)) - ? ndsCardHeader->twlRomSize : ndsCardHeader->romSize; - } else switch (ndsCardHeader->deviceSize) { - case 0x00: - romSize = 0x20000; - break; - case 0x01: - romSize = 0x40000; - break; - case 0x02: - romSize = 0x80000; - break; - case 0x03: - romSize = 0x100000; - break; - case 0x04: - romSize = 0x200000; - break; - case 0x05: - romSize = 0x400000; - break; - case 0x06: - romSize = 0x800000; - break; - case 0x07: - romSize = 0x1000000; - break; - case 0x08: - romSize = 0x2000000; - break; - case 0x09: - romSize = 0x4000000; - break; - case 0x0A: - romSize = 0x8000000; - break; - case 0x0B: - romSize = 0x10000000; - break; - case 0x0C: - romSize = 0x20000000; - break; - } - // Dump! - remove(destPath); - FILE* destinationFile = fopen(destPath, "wb"); - for (u32 src = 0; src < romSize; src += 0x200) { - printf ("\x1b[8;0H"); - printf ("Progress:\n"); - printf ("%i/%i Bytes ", (int)src, (int)romSize); - cardRead (src, romBuffer); - fwrite(romBuffer, 1, 0x200, destinationFile); - } - fclose(destinationFile); - free(ndsCardHeader); - break; - } - if (pressed & KEY_B) { - break; - } - } -} - -void gbaCartDump(void) { - int pressed = 0; - - printf ("\x1b[0;27H"); - printf ("\x1B[42m"); // Print green color - printf ("_____"); // Clear time - consoleInit(NULL, 1, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); - printf ("\x1B[47m"); // Print foreground white color - printf("Dump GBA cart ROM to\n"); - printf("\"fat:/gm9i/out\"?\n"); - printf("( yes, no)"); - while (true) { - // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do - do { - scanKeys(); - pressed = keysDownRepeat(); - swiWaitForVBlank(); - } while (!(pressed & KEY_A) && !(pressed & KEY_B)); - - if (pressed & KEY_A) { - consoleClear(); - if (access("fat:/gm9i", F_OK) != 0) { - printf("Creating directory..."); - mkdir("fat:/gm9i", 0777); - } - if (access("fat:/gm9i/out", F_OK) != 0) { - printf ("\x1b[0;0H"); - printf("Creating directory..."); - mkdir("fat:/gm9i/out", 0777); - } - char gbaHeaderGameTitle[13] = "\0"; - char gbaHeaderGameCode[5] = "\0"; - char gbaHeaderMakerCode[3] = "\0"; - for (int i = 0; i < 12; i++) { - gbaHeaderGameTitle[i] = *(char*)(0x080000A0+i); - if (*(u8*)(0x080000A0+i) == 0) { - break; - } - } - for (int i = 0; i < 4; i++) { - gbaHeaderGameCode[i] = *(char*)(0x080000AC+i); - if (*(u8*)(0x080000AC+i) == 0) { - break; - } - } - for (int i = 0; i < 2; i++) { - gbaHeaderMakerCode[i] = *(char*)(0x080000B0+i); - } - u8 gbaHeaderSoftwareVersion = *(u8*)(0x080000BC); - char destPath[256]; - char destSavPath[256]; - snprintf(destPath, sizeof(destPath), "fat:/gm9i/out/%s_%s%s_%x.gba", gbaHeaderGameTitle, gbaHeaderGameCode, gbaHeaderMakerCode, gbaHeaderSoftwareVersion); - snprintf(destSavPath, sizeof(destSavPath), "fat:/gm9i/out/%s_%s%s_%x.sav", gbaHeaderGameTitle, gbaHeaderGameCode, gbaHeaderMakerCode, gbaHeaderSoftwareVersion); - consoleClear(); - printf("Dumping...\n"); - printf("Do not remove the GBA cart.\n"); - // Determine ROM size - u32 romSize = 0x02000000; - for (u32 i = 0x09FE0000; i > 0x08000000; i -= 0x20000) { - if (*(u32*)(i) == 0xFFFE0000) { - romSize -= 0x20000; - } else { - break; - } - } - // Dump! - remove(destPath); - FILE* destinationFile = fopen(destPath, "wb"); - fwrite((void*)0x08000000, 1, romSize, destinationFile); - fclose(destinationFile); - // Save file - remove(destSavPath); - destinationFile = fopen(destSavPath, "wb"); - fwrite((void*)0x0A000000, 1, 0x10000, destinationFile); - fclose(destinationFile); - break; - } - if (pressed & KEY_B) { - break; - } - } -} - void dm_drawTopScreen(void) { /*if (!ramDumped) { printf ("Dumping RAM..."); diff --git a/arm9/source/dumpOperations.cpp b/arm9/source/dumpOperations.cpp new file mode 100644 index 0000000..cf7879c --- /dev/null +++ b/arm9/source/dumpOperations.cpp @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include + +#include "auxspi.h" +#include "driveOperations.h" +#include "ndsheaderbanner.h" +#include "read_card.h" +#include "tonccpy.h" + +void ndsCardSaveDump(const char* filename) { + std::ofstream output(filename, std::ofstream::binary); + if(output.is_open()) { + auxspi_extra card_type = auxspi_has_extra(); + consoleClear(); + printf("Dumping save...\n"); + printf("Do not remove the NDS card.\n"); + unsigned char* buffer; + if(card_type == AUXSPI_INFRARED) { + int size = auxspi_save_size_log_2(card_type); + int size_blocks = 1 << std::max(0, (int8(size) - 18)); + 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); + output.write((char*)buffer, LEN*size_blocks); + } else { + int type = cardEepromGetType(); + int size = cardEepromGetSize(); + buffer = new unsigned char[size]; + cardReadEeprom(0, buffer, size, type); + output.write((char*)buffer, size); + } + delete[] buffer; + } + output.close(); +} + +void ndsCardDump(void) { + int pressed = 0; + + printf ("\x1b[0;27H"); + printf ("\x1B[42m"); // Print green color + printf ("_____"); // Clear time + consoleInit(NULL, 1, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); + printf ("\x1B[47m"); // Print foreground white color + printf("Dump NDS card ROM to\n"); + printf("\"%s:/gm9i/out\"?\n", (sdMounted ? "sd" : "fat")); + printf("( yes, trim, no)"); + while (true) { + // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do + do { + scanKeys(); + pressed = keysDownRepeat(); + swiWaitForVBlank(); + } while (!(pressed & KEY_A) && !(pressed & KEY_Y) && !(pressed & KEY_B)); + + if ((pressed & KEY_A) || (pressed & KEY_Y)) { + consoleClear(); + char folderPath[2][256]; + sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat")); + sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat")); + if (access(folderPath[0], F_OK) != 0) { + printf("Creating directory..."); + mkdir(folderPath[0], 0777); + } + if (access(folderPath[1], F_OK) != 0) { + printf ("\x1b[0;0H"); + printf("Creating directory..."); + mkdir(folderPath[1], 0777); + } + consoleClear(); + // Read header + sNDSHeaderExt* ndsCardHeader = (sNDSHeaderExt*)malloc(0x1000); + if (cardInit (ndsCardHeader) == 0) { + printf("Dumping...\n"); + printf("Do not remove the NDS card.\n"); + } else { + printf("Unable to dump the ROM.\n"); + for (int i = 0; i < 60*2; i++) { + swiWaitForVBlank(); + } + free(ndsCardHeader); + return; + } + char gameTitle[13] = {0}; + tonccpy(gameTitle, ndsCardHeader->gameTitle, 12); + char gameCode[7] = {0}; + tonccpy(gameCode, ndsCardHeader->gameCode, 6); + bool trimRom = (pressed & KEY_Y); + char romBuffer[0x200]; + char destPath[256]; + sprintf(destPath, "%s:/gm9i/out/%s_%s_%x%s.nds", (sdMounted ? "sd" : "fat"), gameTitle, gameCode, ndsCardHeader->romversion, (trimRom ? "_trim" : "")); + char destSavPath[256]; + sprintf(destSavPath, "%s:/gm9i/out/%s_%s_%x%s.sav", (sdMounted ? "sd" : "fat"), gameTitle, gameCode, ndsCardHeader->romversion, (trimRom ? "_trim" : "")); + // Determine ROM size + u32 romSize = 0; + if (trimRom) { + romSize = ((ndsCardHeader->unitCode != 0) && (ndsCardHeader->twlRomSize > 0)) + ? ndsCardHeader->twlRomSize : ndsCardHeader->romSize; + } else switch (ndsCardHeader->deviceSize) { + case 0x00: + romSize = 0x20000; + break; + case 0x01: + romSize = 0x40000; + break; + case 0x02: + romSize = 0x80000; + break; + case 0x03: + romSize = 0x100000; + break; + case 0x04: + romSize = 0x200000; + break; + case 0x05: + romSize = 0x400000; + break; + case 0x06: + romSize = 0x800000; + break; + case 0x07: + romSize = 0x1000000; + break; + case 0x08: + romSize = 0x2000000; + break; + case 0x09: + romSize = 0x4000000; + break; + case 0x0A: + romSize = 0x8000000; + break; + case 0x0B: + romSize = 0x10000000; + break; + case 0x0C: + romSize = 0x20000000; + break; + } + // Dump! + remove(destPath); + FILE* destinationFile = fopen(destPath, "wb"); + for (u32 src = 0; src < romSize; src += 0x200) { + printf ("\x1b[8;0H"); + printf ("Progress:\n"); + printf ("%i/%i Bytes ", (int)src, (int)romSize); + cardRead (src, romBuffer); + fwrite(romBuffer, 1, 0x200, destinationFile); + } + fclose(destinationFile); + ndsCardSaveDump(destSavPath); + free(ndsCardHeader); + break; + } + if (pressed & KEY_B) { + break; + } + } +} + +void gbaCartDump(void) { + int pressed = 0; + + printf ("\x1b[0;27H"); + printf ("\x1B[42m"); // Print green color + printf ("_____"); // Clear time + consoleInit(NULL, 1, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); + printf ("\x1B[47m"); // Print foreground white color + printf("Dump GBA cart ROM to\n"); + printf("\"fat:/gm9i/out\"?\n"); + printf("( yes, no)"); + while (true) { + // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do + do { + scanKeys(); + pressed = keysDownRepeat(); + swiWaitForVBlank(); + } while (!(pressed & KEY_A) && !(pressed & KEY_B)); + + if (pressed & KEY_A) { + consoleClear(); + if (access("fat:/gm9i", F_OK) != 0) { + printf("Creating directory..."); + mkdir("fat:/gm9i", 0777); + } + if (access("fat:/gm9i/out", F_OK) != 0) { + printf ("\x1b[0;0H"); + printf("Creating directory..."); + mkdir("fat:/gm9i/out", 0777); + } + char gbaHeaderGameTitle[13] = "\0"; + char gbaHeaderGameCode[5] = "\0"; + char gbaHeaderMakerCode[3] = "\0"; + for (int i = 0; i < 12; i++) { + gbaHeaderGameTitle[i] = *(char*)(0x080000A0+i); + if (*(u8*)(0x080000A0+i) == 0) { + break; + } + } + for (int i = 0; i < 4; i++) { + gbaHeaderGameCode[i] = *(char*)(0x080000AC+i); + if (*(u8*)(0x080000AC+i) == 0) { + break; + } + } + for (int i = 0; i < 2; i++) { + gbaHeaderMakerCode[i] = *(char*)(0x080000B0+i); + } + u8 gbaHeaderSoftwareVersion = *(u8*)(0x080000BC); + char destPath[256]; + char destSavPath[256]; + snprintf(destPath, sizeof(destPath), "fat:/gm9i/out/%s_%s%s_%x.gba", gbaHeaderGameTitle, gbaHeaderGameCode, gbaHeaderMakerCode, gbaHeaderSoftwareVersion); + snprintf(destSavPath, sizeof(destSavPath), "fat:/gm9i/out/%s_%s%s_%x.sav", gbaHeaderGameTitle, gbaHeaderGameCode, gbaHeaderMakerCode, gbaHeaderSoftwareVersion); + consoleClear(); + printf("Dumping...\n"); + printf("Do not remove the GBA cart.\n"); + // Determine ROM size + u32 romSize = 0x02000000; + for (u32 i = 0x09FE0000; i > 0x08000000; i -= 0x20000) { + if (*(u32*)(i) == 0xFFFE0000) { + romSize -= 0x20000; + } else { + break; + } + } + // Dump! + remove(destPath); + FILE* destinationFile = fopen(destPath, "wb"); + fwrite((void*)0x08000000, 1, romSize, destinationFile); + fclose(destinationFile); + // Save file + remove(destSavPath); + destinationFile = fopen(destSavPath, "wb"); + fwrite((void*)0x0A000000, 1, 0x10000, destinationFile); + fclose(destinationFile); + break; + } + if (pressed & KEY_B) { + break; + } + } +} diff --git a/arm9/source/dumpOperations.h b/arm9/source/dumpOperations.h new file mode 100644 index 0000000..d59a345 --- /dev/null +++ b/arm9/source/dumpOperations.h @@ -0,0 +1,7 @@ +#ifndef DUMPING_H +#define DUMPING_H + +extern void ndsCardDump(void); +extern void gbaCartDump(void); + +#endif //DUMPING_H