From 429c4c60c9352e9fe9a4e4623fcff997e42d0708 Mon Sep 17 00:00:00 2001 From: msiewert76 Date: Mon, 25 Apr 2011 20:14:35 +0000 Subject: [PATCH] Source code for the 0.2.4 release (4-10-2011). This commit finishes the upload of the old code, before starting to fill this repository. Following updates will reflect the current development process. --- arm9/source/display.cpp | 34 ++++++++++- arm9/source/display.h | 8 ++- arm9/source/dsCard.cpp | 2 +- arm9/source/ftplib.c | 3 +- arm9/source/hardware.cpp | 119 +++++++++++++++++++++++---------------- arm9/source/main.cpp | 47 ++++++++++------ arm9/source/strings.cpp | 109 +++++++++++++++++++++++++++++++++++ arm9/source/strings.h | 66 ++++++++++++++++++++++ savegame_manager.odt | Bin 0 -> 26070 bytes savegame_manager.pnproj | 2 +- savegame_manager.pnps | 2 +- 11 files changed, 320 insertions(+), 72 deletions(-) create mode 100644 arm9/source/strings.cpp create mode 100644 arm9/source/strings.h create mode 100644 savegame_manager.odt diff --git a/arm9/source/display.cpp b/arm9/source/display.cpp index be9687d..7c77c8d 100644 --- a/arm9/source/display.cpp +++ b/arm9/source/display.cpp @@ -38,6 +38,7 @@ #include "auxspi_core.cpp" #include "globals.h" +#include "strings.h" PrintConsole upperScreen; PrintConsole lowerScreen; @@ -59,7 +60,7 @@ void displayInit() consoleInit(&lowerScreen, 3,BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true); consoleSelect(&upperScreen); - iprintf("\n\n\n\n\nDS savegame manager\nVersion 0.2.3 Beta\nBy Pokedoc"); + iprintf("\n\n\n\n\nDS savegame manager\nVersion 0.2.4 Beta\nBy Pokedoc"); displayPrintState("Press (B) to continue"); while (!(keysCurrent() & KEY_B)); @@ -134,7 +135,7 @@ void displayPrintUpper() sprintf(&name[0], "GBA"); break; case 2: - sprintf(&name[0], "3-in-1"); + sprintf(&name[0], "3in1"); break; case 3: sprintf(&name[0], "DSi/SD"); @@ -338,6 +339,35 @@ void displayMessage(const char *msg) iprintf("%s", msg); } +void displayMessageA(int id) +{ + displayMessage(stringsGetMessageString(id)); +} + +void displayMessage2(const char *msg, bool warn) +{ + consoleSelect(&lowerScreen); + consoleSetWindow(&lowerScreen, 0, 0, 32, 24); + consoleClear(); + + if (warn) { + iprintf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + for (int i = 0; i < 22; i++) + iprintf("! !"); + iprintf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } + + consoleSetWindow(&lowerScreen, 2, 2, 28, 20); + consoleClear(); + + iprintf("%s", msg); +} + +void displayMessage2A(int id, bool warn) +{ + displayMessage2(stringsGetMessageString(id), warn); +} + void displayPrintState(const char *txt) { swiWaitForVBlank(); diff --git a/arm9/source/display.h b/arm9/source/display.h index 78bcd8c..caad1eb 100644 --- a/arm9/source/display.h +++ b/arm9/source/display.h @@ -38,7 +38,13 @@ void displayInit(); void displayPrintUpper(); void displayPrintLower(); -void displayMessage(const char *msg); +void displayMessage(const char *msg);// upper screen +void displayMessage2(const char *msg, bool warn); // lower screen + +// ftplib includes this file, and it is pure c, so no operator overloading for now. +void displayMessageA(int id);// upper screen, string table version +void displayMessage2A(int id, bool warn); // lower screen, string table version + void displayProgressBar(int cur, int max0); void displayPrintState(const char *txt); diff --git a/arm9/source/dsCard.cpp b/arm9/source/dsCard.cpp index 45b620a..c0d6b41 100644 --- a/arm9/source/dsCard.cpp +++ b/arm9/source/dsCard.cpp @@ -103,7 +103,7 @@ void SetSerialMode() } uint32 ReadNorFlashID() { - // This function was damaged on the origonal sample, "ID mode" was never left. It is fixed now. + // This function was damaged on the original sample, "ID mode" was never left. It is fixed now. vuint16 id1,id2,id3,id4; ID=0; //check intel 512M 3in1 card diff --git a/arm9/source/ftplib.c b/arm9/source/ftplib.c index d7a9609..25a64ad 100644 --- a/arm9/source/ftplib.c +++ b/arm9/source/ftplib.c @@ -75,7 +75,8 @@ #endif #include "display.h" -#define perror iprintf +//#define perror iprintf +#define perror // struct NetBuf { char *cput,*cget; diff --git a/arm9/source/hardware.cpp b/arm9/source/hardware.cpp index 377ecbe..afdcd6f 100644 --- a/arm9/source/hardware.cpp +++ b/arm9/source/hardware.cpp @@ -46,6 +46,7 @@ #include "fileselect.h" #include "auxspi.h" +#include "strings.h" using namespace std; @@ -68,7 +69,7 @@ bool swap_cart() nds.gameTitle[0] = 0; while (!nds.gameTitle[0]) { - displayMessage("Please take out Slot 1 flash\ncart and insert a game\n\nPress A when done."); + displayMessage2A(STR_HW_SWAP_CARD, false); bool swap = false; while (!swap) { @@ -125,7 +126,7 @@ void hwFormatNor(uint32 page, uint32 count) { uint32 ime = hwGrab3in1(); SetSerialMode(); - displayPrintState("Formating NOR memory"); + //displayPrintState("Formating NOR memory"); displayProgressBar(0, count); for (uint32 i = page; i < page+count; i++) { Block_Erase(i << 18); @@ -162,12 +163,10 @@ void hwBackup3in1() int size_blocks = 1 << max(0, (int8(size) - 18)); // ... in units of 0x40000 bytes - that's 256 kB uint8 type = auxspi_save_type(); - displayPrintState("Format NOR"); - hwFormatNor(1, size_blocks); + displayMessage2A(STR_HW_3IN1_FORMAT_NOR, false); + hwFormatNor(0, size_blocks+1); - // Dump save and write it to NOR - displayPrintState("Writing save to NOR"); - // TODO: test smaller write size! + displayMessage2A(STR_HW_3IN1_WRITE_NOR, false); if (size < 15) size_blocks = 1; else @@ -178,7 +177,6 @@ void hwBackup3in1() for (int i = 0; i < size_blocks; i++) { displayProgressBar(i+1, size_blocks); auxspi_read_data(i << 15, data, LEN, type); - // TODO: pull out "setserialmode" uint32 ime = hwGrab3in1(); SetSerialMode(); WriteNorFlash((i << 15) + pitch, data, LEN); @@ -201,6 +199,9 @@ void hwBackup3in1() // Write a flag to tell the app what happens on restart. // This is necessary, since some DLDI drivers cease working after swapping a card. + // Fix for dead batteries: don't write "reboot" values to SRAM, but to NOR. + displayMessage2A(STR_HW_3IN1_PREPARE_REBOOT, false); + // sNDSHeader nds; cardReadHeader((u8*)&nds); // on a Cyclops Evolution, this call *will* mess up your DLDI driver! dsCardData data2; @@ -209,12 +210,23 @@ void hwBackup3in1() data2.data[1] = 0; data2.data[2] = size; data2.data[3] = 0xffff00ff; + // We need to write the reboot flags to NOR, and we can only write in 32kB-blocks, at aligned offsets. + // Therefore we prepare a bigger block and copy lots of data now. memcpy(&data2.name[0], &nds.gameTitle[0], 12); - WriteSram(0x0a000000, (u8*)&data2, sizeof(data2)); - char txt[128]; - sprintf(txt, "%.12s", &nds.gameTitle[0]); + memset(data, 0, 0x8000); + memcpy(&data[0x1000], (u8*)&data2, sizeof(data2)); + uint32 ime = hwGrab3in1(); + WriteNorFlash(0, data, 0x8000); // this should work - but it does not! + hwRelease3in1(ime); - displayMessage("Save has been written to 3in1.\nPlease power off and restart\nthis tool."); + displayMessage2A(STR_HW_3IN1_PLEASE_REBOOT, false); + + ime = hwGrab3in1(); + ReadNorFlash((u8*)&data2, 0x1000, sizeof(data2)); // this should work - but it does not! + hwRelease3in1(ime); + char txt[128]; + sprintf(txt, "%.12s", &data2.name[0]); + displayMessage(txt); while(1) {}; } @@ -223,27 +235,33 @@ void hwDump3in1(uint32 size, const char *gamename) { u32 size_blocks = 1 << max(0, (uint8(size) - 18)); + displayMessageA(STR_HW_SELECT_FILE_OW); + char path[256]; char fname[256] = ""; fileSelect("/", path, fname, 0, true, false); if (!fname[0]) { uint32 cnt = 0; sprintf(fname, "/%s.%i.sav", gamename, cnt); + char txt[256]; + sprintf(txt, stringsGetMessageString(STR_HW_SEEK_UNUSED_FNAME), fname); + displayMessage2(txt, false); while (fileExists(fname)) { if (cnt < 65536) cnt++; else { - displayMessage("Unable to get a filename!\nThis means that you have more\nthan 65536 saves! (wow!)\nOops!"); + displayMessage2A(STR_ERR_NO_FNAME, true); while(1); } - sprintf(fname, "/%s.%i.sav", gamename, cnt); + sprintf(fname, "%s.%i.sav", gamename, cnt); } } - char fullpath[512]; + char fullpath[256]; sprintf(fullpath, "%s/%s", path, fname); - displayMessage(fname); + char txt[512]; + sprintf(txt, stringsGetMessageString(STR_HW_3IN1_DUMP), fullpath); + displayMessage2(txt, false); - displayPrintState("Writing save to flash card"); FILE *file = fopen(fullpath, "wb"); if (size < 15) size_blocks = 1; @@ -259,7 +277,8 @@ void hwDump3in1(uint32 size, const char *gamename) } fclose(file); - displayPrintState("Done! Please power off..."); + sprintf(txt, stringsGetMessageString(STR_HW_3IN1_DONE_DUMP), fullpath); + displayMessage2(txt, false); while (1); } @@ -268,24 +287,26 @@ void hwRestore3in1() char path[256]; char fname[256]; memset(fname, 0, 256); - displayMessage("Please select a .sav file."); + displayMessageA(STR_HW_SELECT_FILE); // lower screen is used for file browser fileSelect("/", path, fname, 0, false, false); - displayMessage(""); char msg[256]; - sprintf(msg, "%s%s", path, fname); + sprintf(msg, "%s/%s", path, fname); + displayMessage(msg); FILE *file = fopen(msg, "rb"); uint32 size0 = fileSize(msg); + uint8 size = 1; + // crude log2 function while (size0 > (1 << size)) { size++; } int size_blocks = 1 << max(0, int8(size) - 18); // ... in units of 0x40000 bytes - that's 256 kB + displayMessage2A(STR_HW_3IN1_FORMAT_NOR, false); hwFormatNor(1, size_blocks); - displayPrintState("Writing save to NOR"); - // Read save and write it to NOR + displayMessage2A(STR_HW_3IN1_WRITE_NOR, false); if (size < 15) size_blocks = 1; else @@ -312,6 +333,7 @@ void hwRestore3in1() while(1); } } + displayProgressBar(1, 1); fclose(file); free(test); @@ -323,6 +345,7 @@ void hwRestore3in1_b(uint32 size_file) // Third, swap in a new game uint32 size = auxspi_save_size_log_2(); while ((size_file < size) || flash_card) { + // TODO: make THIS error message more meaningful! displayPrintState("File too small or no save chip!"); swap_cart(); size = auxspi_save_size_log_2(); @@ -331,12 +354,12 @@ void hwRestore3in1_b(uint32 size_file) uint8 type = auxspi_save_type(); if (type == 3) { - displayPrintState("Formating Flash chip"); + displayMessage2A(STR_HW_FORMAT_GAME, false); auxspi_erase(); } // And finally, write the save! - displayPrintState("Restoring save"); + displayMessage2A(STR_HW_WRITE_GAME, false); u32 LEN = 0, num_blocks = 0, shift = 0; switch (type) { case 1: @@ -364,8 +387,7 @@ void hwRestore3in1_b(uint32 size_file) } displayProgressBar(1, 1); - - displayPrintState("Done! Please turn your DS off..."); + displayMessage2A(STR_HW_3IN1_RESTORE, false); while (1) {}; } @@ -373,10 +395,10 @@ void hwRestore3in1_b(uint32 size_file) // ------------------------------------------------------------ void hwErase() { - displayMessage("This will WIPE OUT your entire\nsave! ARE YOU SURE?\n\nPress R+up+Y to confim!"); + displayMessage2A(STR_HW_WARN_DELETE, true); while (!(keysCurrent() & (KEY_UP | KEY_R | KEY_Y))) {}; auxspi_erase(); - displayMessage("Done! Your save is gone!"); + displayMessage2A(STR_HW_DID_DELETE, true); while(1); } @@ -431,7 +453,7 @@ void hwBackupFTP() char fdir[256] = ""; char fname[256] =""; memset(fname, 0, 256); - displayMessage("Please select a file name to\noverwrite, or press L+R in a\nfolder to create a new file."); + displayMessageA(STR_HW_SELECT_FILE_OW); displayPrintState("FTP: dir"); fileSelect("/", fdir, fname, buf, true, false); bool newfile; @@ -443,17 +465,19 @@ void hwBackupFTP() // Third: get a new target filename FtpChdir(fdir, buf); if (!fname[0]) { - displayMessage("Looking for an unused filename"); sNDSHeader nds; cardReadHeader((u8*)&nds); uint32 cnt = 0; int tsize = 0; sprintf(fname, "%.12s.%i.sav", nds.gameTitle, cnt); while (FtpSize(fname, &tsize, FTPLIB_IMAGE, buf) != 0) { + char txt[256]; + sprintf(txt, stringsGetMessageString(STR_HW_SEEK_UNUSED_FNAME), fname); + displayMessage2(txt, false); if (cnt < 65536) cnt++; else { - displayMessage("Unable to get a filename!\nThis means that you have more\nthan 65536 saves! (wow!)\nOops!"); + displayMessage2A(STR_ERR_NO_FNAME, true); while(1); } sprintf(fname, "%.12s.%i.sav", nds.gameTitle, cnt); @@ -473,29 +497,30 @@ void hwBackupFTP() displayProgressBar(i+1, (size_blocks << 6)); auxspi_read_data(i << 9, (u8*)&data[0], length, type); int out = 0; + int debug = 0; while (out < 512) { out += FtpWrite((u8*)&data[out], 512-out, ndata); - if (out < 512) - displayPrintState("!!!"); - else + if (out < 512) { + displayPrintState(stringsGetMessageString(STR_HW_FTP_SLOW)); + /* + debug++; + if (debug >= 2048) { + debug = 0; + displayMessage2("Unable to reach FTP server. Make sure that you have WRITE ACCESS!" + } + */ + } else { displayPrintState(""); + } } - /* - if ((out = FtpWrite((u8*)&data[0], length, ndata)) < length) { - char dings[512]; - sprintf(dings, "Error: wrote %i, got %i", length, out); - displayPrintState(dings); - while(1); - } - */ } FtpClose(ndata); FtpQuit(ndata); Wifi_DisconnectAP(); - displayMessage("Done! Please turn off your DS."); - while(1); + //displayMessage("Done! Please turn off your DS."); + //while(1); } void hwRestoreFTP() @@ -595,7 +620,7 @@ void hwRestoreFTP() while (in < 512) { in += FtpRead((u8*)&pdata[in], 512-in, ndata); if (in < 512) - displayPrintState("!!!"); + displayPrintState(stringsGetMessageString(STR_HW_FTP_SLOW)); else displayPrintState(""); } @@ -613,7 +638,6 @@ void hwRestoreFTP() //char bums[512]; //sprintf(bums, "addr = %x", (i << 9)+(j << shift)); displayPrintState("Writing save (insecure!)"); - //displayPrintState(bums); for (int j = 0; j < 1 << (9-shift); j++) { auxspi_write_data((i << 9)+(j << shift), ((u8*)pdata)+(j< 8*0) && (touchXY.py < 8*8)) { hwBackupFTP(); + displayPrintLower(); } // restore if ((touchXY.py > 8*8) && (touchXY.py < 8*16)) { hwRestoreFTP(); + displayPrintLower(); } // erase @@ -251,6 +250,7 @@ void mode_wifi() swap_cart(); displayPrintUpper(); hwErase(); + displayPrintLower(); } } } @@ -269,16 +269,24 @@ int main(int argc, char* argv[]) while (1) {}; } + // test if our flash card supports argv at all. some R4 clones seem to be very picky! + // (untested due to lack of an R4 myself, but I hope it works) + bool has_argv = false; + if (argc) + has_argv = true; + // load ini file... ini_fd_t ini = 0; char inipath[256]; - if (argv[0]) { - char *last = strrchr(argv[0], '/'); - int len = (last - argv[0])+1; - strncpy(bootdir, argv[0], len); - sprintf(inipath, "%s/savegame_manager.ini", bootdir); - if (fileExists(inipath)) - ini = ini_open(inipath, "r", ""); + if (has_argv) { + if (argv[0]) { + char *last = strrchr(argv[0], '/'); + int len = (last - argv[0])+1; + strncpy(bootdir, argv[0], len); + sprintf(inipath, "%s/savegame_manager.ini", bootdir); + if (fileExists(inipath)) + ini = ini_open(inipath, "r", ""); + } } if (!ini) { sprintf(inipath, "/savegame_manager.ini"); @@ -308,6 +316,9 @@ int main(int argc, char* argv[]) // delete temp file (which is a remnant of inilib) remove("/tmpfile"); + // load strings + stringsLoadFile(0); + // Identify hardware and branch to corresponding mode //displayMessage("Identifying hardware..."); diff --git a/arm9/source/strings.cpp b/arm9/source/strings.cpp new file mode 100644 index 0000000..e99e856 --- /dev/null +++ b/arm9/source/strings.cpp @@ -0,0 +1,109 @@ +/* + * 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. + * + * strings.cpp: Localised message strings + * + * 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 file implements the functions and data structures required for localised messages. + */ + + +#include "strings.h" +#include "libini.h" +#include +#include "fileselect.h" + +using namespace std; + +// a global string array +char **message_strings; +char txt[512]; + +#define ADD_STRING(id,text) strcpy(txt,text);\ + message_strings[id] = new char[strlen(txt)+1];\ + memcpy(message_strings[id], txt, strlen(txt)+1); + + +bool stringsLoadFile(const char *fname) +{ +#define NOT_FINISHED + +#ifndef NOT_FINISHED + // load ini file... + ini_fd_t ini = 0; + if (fileExists(fname)) + ini = ini_open(fname, "r", ""); + else + return false; +#endif + + message_strings = new char*[STR_LAST]; +#ifndef NOT_FINISHED + ini_locateHeading(ini, ""); + char txt[512]; + for (int i = 0; i < STR_LAST; i++) { + sprintf(txt, "%i", i); + ini_locateKey(ini, txt); + ini_readString(ini, txt, 512); + message_strings[i] = new char[strlen(txt)+1]; + memcpy(message_strings[i], txt, strlen(txt)+1); + } + + // delete temp file (which is a remnant of inilib) + remove("/tmpfile"); +#else + ADD_STRING(STR_HW_SWAP_CARD,"Please take out Slot 1\nflash card and insert a game\n\nPress A when done."); + // + ADD_STRING(STR_HW_SELECT_FILE,"Please select a .sav file."); + ADD_STRING(STR_HW_SELECT_FILE_OW,"Please select a file to\noverwrite, or press L+R in afolder to create a new file."); + ADD_STRING(STR_HW_SEEK_UNUSED_FNAME,"Please wait... searching for\nan unused filename.\n\nTrying: %s"); + ADD_STRING(STR_ERR_NO_FNAME,"ERROR: Unable to get an\nunused nfilename! This means that you have more than\n65536 saves!\n\n(wow!)"); + // + ADD_STRING(STR_HW_FORMAT_GAME,"Preparing to write to your\ngame. Please wait..."); + ADD_STRING(STR_HW_WRITE_GAME,"Writing the save to your\ngame. Please wait..."); + // + ADD_STRING(STR_HW_3IN1_FORMAT_NOR,"Preparing to write to the\n3in1. Please wait..."); + ADD_STRING(STR_HW_3IN1_WRITE_NOR,"Writing save data to the\n3in1. Please wait..."); + ADD_STRING(STR_HW_3IN1_PREPARE_REBOOT,"Preparing reboot..."); + ADD_STRING(STR_HW_3IN1_PLEASE_REBOOT,"Save has been written to\nthe 3in1. Please power off\nand restart this tool."); + ADD_STRING(STR_HW_3IN1_CLEAR_FLAG,"Preparing to dump your\nsave... Please wait..."); + ADD_STRING(STR_HW_3IN1_DUMP,"Dumping the save from the\n3in1 to your flash card.\nFilename:\n%s"); + ADD_STRING(STR_HW_3IN1_DONE_DUMP,"Done. Your game save has\nbeen dumped using your\n3in1. Filename:\n%s\n\nPlease restart your DS."); + ADD_STRING(STR_HW_3IN1_RESTORE,"Done. Your game save has\nbeen restored using your\n3in1.\n\nPlease restart your DS."); + // + ADD_STRING(STR_HW_FTP_SLOW,"FTP is slow, please wait..."); + // + ADD_STRING(STR_HW_WARN_DELETE,"This will WIPE OUT your\nentire save! ARE YOU SURE?\n\nPress R+up+Y to confim!"); + ADD_STRING(STR_HW_DID_DELETE,"Done. Your game save has\nbeen PERMANENTLY deleted.\n\nPlease restart your DS."); + // +#endif + + return true; +} + +const char *stringsGetMessageString(int id) +{ + if (id > STR_LAST) + return 0; + else + return message_strings[id]; +} diff --git a/arm9/source/strings.h b/arm9/source/strings.h new file mode 100644 index 0000000..56b9598 --- /dev/null +++ b/arm9/source/strings.h @@ -0,0 +1,66 @@ +/* + * 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. + * + * strings.h: Header for strings.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 files is used to allow translations of the program. It defines many + string IDs which may be used to overwrite default messages with an INI file. + */ + +#ifndef STRINGS_H +#define STRINGS_H + +enum { + STR_TITLE_MSG, + // + STR_HW_SWAP_CARD, + // + STR_HW_SELECT_FILE, + STR_HW_SELECT_FILE_OW, + STR_HW_SEEK_UNUSED_FNAME, + STR_ERR_NO_FNAME, + // + STR_HW_FORMAT_GAME, + STR_HW_WRITE_GAME, + // + STR_HW_3IN1_FORMAT_NOR, + STR_HW_3IN1_WRITE_NOR, + STR_HW_3IN1_PREPARE_REBOOT, + STR_HW_3IN1_PLEASE_REBOOT, + STR_HW_3IN1_CLEAR_FLAG, + STR_HW_3IN1_DUMP, + STR_HW_3IN1_DONE_DUMP, + STR_HW_3IN1_RESTORE, + // + STR_HW_FTP_SLOW, + // + STR_HW_WARN_DELETE, + STR_HW_DID_DELETE, + // + STR_LAST +}; + +bool stringsLoadFile(const char *fname); +const char *stringsGetMessageString(int id = 0); + +#endif // STRINGS_H diff --git a/savegame_manager.odt b/savegame_manager.odt new file mode 100644 index 0000000000000000000000000000000000000000..25d10c60b93798c22cdb5f7cb14bc076afc7dd2a GIT binary patch literal 26070 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 WRYXSlc^E$IQwrl*5}W+|^?v}%b5&UY literal 0 HcmV?d00001 diff --git a/savegame_manager.pnproj b/savegame_manager.pnproj index ce5efc0..534ed6c 100644 --- a/savegame_manager.pnproj +++ b/savegame_manager.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/savegame_manager.pnps b/savegame_manager.pnps index daaa261..60a5214 100644 --- a/savegame_manager.pnps +++ b/savegame_manager.pnps @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file