From ccf8d0200b63fa60b117a1b534dd7c9fecd0aacb Mon Sep 17 00:00:00 2001 From: Pk11 Date: Wed, 5 Jan 2022 04:55:35 -0600 Subject: [PATCH] Add restoring DS saves on DS/DS Lite (#141) --- arm9/source/driveOperations.cpp | 2 +- arm9/source/dumpOperations.cpp | 94 ++++++++++++++++++------- arm9/source/file_browse.cpp | 27 ++++--- arm9/source/file_browse.h | 3 +- arm9/source/language.inl | 8 ++- nitrofiles/languages/en-US/language.ini | 8 ++- 6 files changed, 102 insertions(+), 40 deletions(-) diff --git a/arm9/source/driveOperations.cpp b/arm9/source/driveOperations.cpp index 8011625..3658034 100644 --- a/arm9/source/driveOperations.cpp +++ b/arm9/source/driveOperations.cpp @@ -469,7 +469,7 @@ bool driveRemoved(Drive drive) { case Drive::sdCard: return sdRemoved; case Drive::flashcard: - return REG_SCFG_MC & BIT(0); + return isDSiMode() ? REG_SCFG_MC & BIT(0) : !flashcardMounted; case Drive::ramDrive: return !ramdriveMounted; case Drive::nand: diff --git a/arm9/source/dumpOperations.cpp b/arm9/source/dumpOperations.cpp index 48699d9..1f2db59 100644 --- a/arm9/source/dumpOperations.cpp +++ b/arm9/source/dumpOperations.cpp @@ -7,6 +7,7 @@ #include "font.h" #include "gba.h" #include "lzss.h" +#include "main.h" #include "ndsheaderbanner.h" #include "read_card.h" #include "tonccpy.h" @@ -151,7 +152,7 @@ void dumpFailMsg(std::string_view msg) { font->update(true); scanKeys(); - pressed = keysDownRepeat(); + pressed = keysDown(); swiWaitForVBlank(); } while (!(pressed & KEY_A)); } @@ -574,8 +575,10 @@ void ndsCardSaveDump(const char* filename) { } void ndsCardSaveRestore(const char *filename) { + bool usingFlashcard = (io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS) && flashcardMounted; + font->clear(false); - font->print(0, 0, false, STR_RESTORE_SELECTED_SAVE_CARD); + font->print(0, 0, false, (usingFlashcard ? STR_RESTORE_SELECTED_SAVE_CARD_FLASHCARD : STR_RESTORE_SELECTED_SAVE_CARD) + "\n\n" + STR_A_YES_B_NO); font->update(false); // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do @@ -586,21 +589,16 @@ void ndsCardSaveRestore(const char *filename) { font->update(true); scanKeys(); - pressed = keysDownRepeat(); + pressed = keysDown(); swiWaitForVBlank(); } while (!(pressed & (KEY_A | KEY_B))); if(pressed & KEY_A) { int type = cardEepromGetTypeFixed(); - if(type == -1) { // NAND + if(type == -1 && !isRegularDS) { // NAND if (cardInit(&ndsCardHeader) != 0) { - font->clear(false); - font->print(0, 0, false, STR_UNABLE_TO_RESTORE_SAVE); - font->update(false); - for (int i = 0; i < 60 * 2; i++) { - swiWaitForVBlank(); - } + dumpFailMsg(STR_UNABLE_TO_RESTORE_SAVE); return; } @@ -646,14 +644,47 @@ void ndsCardSaveRestore(const char *filename) { 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; + unsigned char *buffer = nullptr; int size; int length; unsigned int num_blocks = 0, shift = 0, LEN = 0; + + // Read save file length + fseek(in, 0, SEEK_END); + length = ftell(in); + fseek(in, 0, SEEK_SET); + + // If using flashcard, read the save and swap carts + if(usingFlashcard) { + buffer = new unsigned char[length]; + fread(buffer, 1, length, in); + fclose(in); + currentDrive = Drive::flashcard; + chdir("fat:/"); + flashcardUnmount(); + + font->clear(false); + font->print(0, 0, false, STR_EJECT_FLASHCARD_INSERT_GAME + "\n\n" + STR_A_CONTINUE); + font->update(false); + + // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do + do { + // Print time + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); + + scanKeys(); + pressed = keysDown(); + swiWaitForVBlank(); + } while (!(pressed & KEY_A)); + + type = cardEepromGetTypeFixed(); + } + + auxspi_extra card_type = auxspi_has_extra(); + bool auxspi = card_type == AUXSPI_INFRARED; if(auxspi) { size = auxspi_save_size_log_2(card_type); type = auxspi_save_type(card_type); @@ -675,11 +706,10 @@ void ndsCardSaveRestore(const char *filename) { } 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); + if(!usingFlashcard) + fclose(in); dumpFailMsg(STR_SAVE_SIZE_MISMATCH_CARD); return; } @@ -696,8 +726,12 @@ void ndsCardSaveRestore(const char *filename) { else cardEepromChipEraseFixed(); } + + // If using flashcard restore from buffer, + // otherwise from file so big saves can work if(auxspi){ - buffer = new unsigned char[LEN]; + if(!usingFlashcard) + buffer = new unsigned char[LEN]; font->print(0, 5, false, "["); font->print(-1, 5, false, "]"); for(unsigned int i = 0; i < num_blocks; i++) { @@ -705,13 +739,18 @@ void ndsCardSaveRestore(const char *filename) { font->printf(0, 6, false, Alignment::left, Palette::white, STR_N_OF_N_BYTES.c_str(), i * LEN, length); font->update(false); - fread(buffer, 1, LEN, in); - auxspi_write_data(i << shift, buffer, LEN, type, card_type); + if(usingFlashcard) { + auxspi_write_data(i << shift, buffer + (LEN * i), LEN, type, card_type); + } else { + 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]; + if(!usingFlashcard) + buffer = new unsigned char[blocks]; font->print(0, 5, false, "["); font->print(-1, 5, false, "]"); for(unsigned int i = 0; i < 32; i++) { @@ -719,13 +758,18 @@ void ndsCardSaveRestore(const char *filename) { font->printf(0, 6, false, Alignment::left, Palette::white, STR_N_OF_N_BYTES.c_str(), written, size); font->update(false); - fread(buffer, 1, blocks, in); - cardWriteEeprom(written, buffer, blocks, type); + if(usingFlashcard) { + cardWriteEeprom(written, buffer + (blocks * i), blocks, type); + } else { + fread(buffer, 1, blocks, in); + cardWriteEeprom(written, buffer, blocks, type); + } written += blocks; } } delete[] buffer; - fclose(in); + if(!usingFlashcard) + fclose(in); } } } @@ -953,7 +997,7 @@ void gbaCartSaveDump(const char *filename) { void gbaCartSaveRestore(const char *filename) { font->clear(false); - font->print(0, 0, false, STR_RESTORE_SELECTED_SAVE_CART); + font->print(0, 0, false, STR_RESTORE_SELECTED_SAVE_CART + "\n\n" + STR_A_YES_B_NO); font->update(false); // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do diff --git a/arm9/source/file_browse.cpp b/arm9/source/file_browse.cpp index 38bfe1a..f978438 100644 --- a/arm9/source/file_browse.cpp +++ b/arm9/source/file_browse.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include "main.h" @@ -187,7 +188,10 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { operations.push_back(FileOperation::trimNds); } if(extension(entry->name, {"sav", "sav1", "sav2", "sav3", "sav4", "sav5", "sav6", "sav7", "sav8", "sav9"})) { - operations.push_back(FileOperation::restoreSave); + if(!(io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS) || entry->size <= (1 << 20)) + operations.push_back(FileOperation::restoreSaveNds); + if(isRegularDS && (entry->size == 512 || entry->size == 8192 || entry->size == 32768 || entry->size == 65536 || entry->size == 131072)) + operations.push_back(FileOperation::restoreSaveGba); } if(currentDrive != Drive::fatImg && extension(entry->name, {"img", "sd", "sav", "pub", "pu1", "pu2", "pu3", "pu4", "pu5", "pu6", "pu7", "pu8", "pu9", "prv", "pr1", "pr2", "pr3", "pr4", "pr5", "pr6", "pr7", "pr8", "pr9"})) { operations.push_back(FileOperation::mountImg); @@ -238,8 +242,14 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { case FileOperation::trimNds: font->print(3, row++, false, STR_TRIM_NDS); break; - case FileOperation::restoreSave: - font->print(3, row++, false, STR_RESTORE_SAVE); + case FileOperation::restoreSaveNds: + if(!isRegularDS) + font->print(3, row++, false, STR_RESTORE_SAVE); + else + font->print(3, row++, false, STR_RESTORE_SAVE_NDS); + break; + case FileOperation::restoreSaveGba: + font->print(3, row++, false, STR_RESTORE_SAVE_GBA); break; case FileOperation::mountImg: font->print(3, row++, false, STR_MOUNT_FAT_IMG); @@ -328,12 +338,11 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { applaunch = true; return FileOperation::bootFile; break; - } case FileOperation::restoreSave: { - if(isDSiMode()) { - ndsCardSaveRestore(entry->name.c_str()); - } else { - gbaCartSaveRestore(entry->name.c_str()); - } + } case FileOperation::restoreSaveNds: { + ndsCardSaveRestore(entry->name.c_str()); + break; + } case FileOperation::restoreSaveGba: { + gbaCartSaveRestore(entry->name.c_str()); break; } case FileOperation::copySdOut: { if (access("sd:/gm9i", F_OK) != 0) { diff --git a/arm9/source/file_browse.h b/arm9/source/file_browse.h index 2bbadb6..10a0eb5 100644 --- a/arm9/source/file_browse.h +++ b/arm9/source/file_browse.h @@ -44,7 +44,8 @@ enum class FileOperation { ndsInfo, trimNds, mountImg, - restoreSave, + restoreSaveNds, + restoreSaveGba, showInfo, copySdOut, copyFatOut, diff --git a/arm9/source/language.inl b/arm9/source/language.inl index 37d399d..5cfd1ef 100644 --- a/arm9/source/language.inl +++ b/arm9/source/language.inl @@ -96,6 +96,8 @@ STRING(MOUNT_NITROFS, "Mount NitroFS") STRING(SHOW_NDS_INFO, "Show NDS file info") STRING(TRIM_NDS, "Trim NDS file") STRING(RESTORE_SAVE, "Restore save") +STRING(RESTORE_SAVE_NDS, "Restore save (Slot-1)") +STRING(RESTORE_SAVE_GBA, "Restore save (Slot-2)") STRING(MOUNT_FAT_IMG, "Mount as FAT image") STRING(OPEN_HEX, "Open in hex editor") STRING(SHOW_DIRECTORY_INFO, "Show directory info") @@ -150,8 +152,10 @@ STRING(FAILED_TO_RESTORE_SAVE, "Failed to restore the save.") STRING(UNABLE_TO_RESTORE_SAVE, "Unable to restore the save.") STRING(SAVE_SIZE_MISMATCH_CARD, "The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled!") STRING(SAVE_SIZE_MISMATCH_CART, "The size of this save doesn't match the size of the inserted game pak.\n\nWrite cancelled!") -STRING(RESTORE_SELECTED_SAVE_CARD, "Restore the selected save to the inserted game card?\n\n(\\A yes, \\B no)") -STRING(RESTORE_SELECTED_SAVE_CART, "Restore the selected save to the inserted game pak?\n\n(\\A yes, \\B no)") +STRING(RESTORE_SELECTED_SAVE_CARD, "Restore the selected save to the inserted game card?") +STRING(RESTORE_SELECTED_SAVE_CARD_FLASHCARD, "Unmount the flashcard and restore the selected save to a game card?") +STRING(RESTORE_SELECTED_SAVE_CART, "Restore the selected save to the inserted game pak?") +STRING(EJECT_FLASHCARD_INSERT_GAME, "Eject your flashcard and insert the game card to restore to.") STRING(PROGRESS, "Progress:") STRING(N_OF_N_BYTES, "%d/%d Bytes") STRING(NDS_IS_DUMPING, "%s.nds\nis dumping...") diff --git a/nitrofiles/languages/en-US/language.ini b/nitrofiles/languages/en-US/language.ini index c7dbe20..0239493 100644 --- a/nitrofiles/languages/en-US/language.ini +++ b/nitrofiles/languages/en-US/language.ini @@ -91,6 +91,8 @@ MOUNT_NITROFS=Mount NitroFS SHOW_NDS_INFO=Show NDS file info TRIM_NDS=Trim NDS file RESTORE_SAVE=Restore save +RESTORE_SAVE_NDS=Restore save (Slot-1) +RESTORE_SAVE_GBA=Restore save (Slot-2) MOUNT_FAT_IMG=Mount as FAT image OPEN_HEX=Open in hex editor SHOW_DIRECTORY_INFO=Show directory info @@ -142,8 +144,10 @@ FAILED_TO_RESTORE_SAVE=Failed to restore the save. UNABLE_TO_RESTORE_SAVE=Unable to restore the save. SAVE_SIZE_MISMATCH_CARD=The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled! SAVE_SIZE_MISMATCH_CART=The size of this save doesn't match the size of the inserted game pak.\n\nWrite cancelled! -RESTORE_SELECTED_SAVE_CARD=Restore the selected save to the inserted game card?\n\n(\A yes, \B no) -RESTORE_SELECTED_SAVE_CART=Restore the selected save to the inserted game pak?\n\n(\A yes, \B no) +RESTORE_SELECTED_SAVE_CARD=Restore the selected save to the inserted game card? +RESTORE_SELECTED_SAVE_CARD_FLASHCARD=Unmount the flashcard and restore the selected save to a game card? +RESTORE_SELECTED_SAVE_CART=Restore the selected save to the inserted game pak? +EJECT_FLASHCARD_INSERT_GAME=Eject your flashcard and insert the game card to restore to. PROGRESS=Progress: N_OF_N_BYTES=%d/%d Bytes NDS_IS_DUMPING=%s.nds\nis dumping...