diff --git a/README.MD b/README.MD index 669cdb5..be3a354 100644 --- a/README.MD +++ b/README.MD @@ -1,6 +1,6 @@ # JKSV -Multipurpose tool for Switch. +Data Dump/Restore tool for Switch. ## Info JKSV on Switch started as a small project/port to test some things and get familiar with libnx. A list of what it currently can do: @@ -13,11 +13,10 @@ JKSV on Switch started as a small project/port to test some things and get famil * BIS Storage is opened inside a basic filebrowser. The partition's listing is on the left. Your SD is on the right. * Only copying to SD and file properties work on BIS partitions. Writing to and deleting are disabled for now. 4. Misc Extras: - * NAND Dumping * Ability to remove downloaded firmware updates from NAND. * Terminating processes by [ID](https://switchbrew.org/wiki/Title_list#System_Modules). Allowing you to dump normally unopenable system archives. * Mount by System Save [ID](https://switchbrew.org/wiki/Flash_Filesystem#System_Savegames). Normally used when the terminated process makes JKSV unable to rescan titles without the Switch crashing. - * Mount and open RomFS of process the homebrew menu takes over (if launched as NRO) + * Mount and open RomFS of process the homebrew menu takes over (if launched as NRO) (To be re-added later) * Hold R while opening a game or applet with Atmosphere so the homebrew menu loads. Open JKSV and press minus and select **Mount Process RomFS**. The romfs of the app should appear in the browser along with your SD on the right. # Building: diff --git a/inc/clsui.h b/inc/clsui.h deleted file mode 100644 index d06a2a1..0000000 --- a/inc/clsui.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef CLSUI_H -#define CLSUI_H - -#include "data.h" - -namespace ui -{ - void clsUserPrep(); - void clsTitlePrep(data::user& u); - - void classicUserMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p); - void classicTitleMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p); - //I don't think this matched very well - void classicFolderMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p); - - void updateExMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p); - void exMenuPrep(); -} - -#endif // CLSUI_H diff --git a/inc/data.h b/inc/data.h index 819a888..8783b9a 100644 --- a/inc/data.h +++ b/inc/data.h @@ -13,10 +13,12 @@ namespace data extern bool forceMount; //Loads user + title info - void loadDataInfo(); + void init(); void loadBlacklist(); void exit(); bool isAppletMode(); + void loadCfg(); + void saveCfg(); //Class to help not load the same icons over and over class icn @@ -111,12 +113,13 @@ namespace data //User vector extern std::vector users; - //Stores current data we're using so I don't have to type so much. + //Stores current data we're using so I don't have to type so much. + Options and info extern titledata curData; extern user curUser; extern int selUser, selData; extern std::string sysLang; extern AppletType appletMode; + extern bool incDev, autoBack; } #endif // DATA_H diff --git a/inc/txtui.h b/inc/txtui.h new file mode 100644 index 0000000..a5961c3 --- /dev/null +++ b/inc/txtui.h @@ -0,0 +1,24 @@ +#ifndef TXTUI_H +#define TXTUI_H + +#include "data.h" + +namespace ui +{ + void textUserPrep(); + void textTitlePrep(data::user& u); + + void textUserMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p); + void textTitleMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p); + //I don't think this matched very well + void textFolderMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p); + + void updateExMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p); + void exMenuPrep(); + + //Options + void optMenuInit(); + void updateOptMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p); +} + +#endif diff --git a/inc/ui.h b/inc/ui.h index 87bd148..6421206 100644 --- a/inc/ui.h +++ b/inc/ui.h @@ -10,7 +10,7 @@ //ui headers - split up to keep a bit more organized #include "menu.h" #include "miscui.h" -#include "clsui.h" +#include "txtui.h" #include "uiupdate.h" enum menuState @@ -19,17 +19,18 @@ enum menuState TTL_SEL, FLD_SEL, ADV_MDE, - CLS_USR, - CLS_TTL, - CLS_FLD, - EX_MNU + TXT_USR, + TXT_TTL, + TXT_FLD, + EX_MNU, + OPT_MNU }; namespace ui { - //Classic mode/text menus - extern bool clsMode; + //Text menus + extern bool textMode; //Current menu/ui state extern int mstate, prevState; diff --git a/src/data.cpp b/src/data.cpp index 03bfd44..a3f69a4 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -88,7 +88,10 @@ namespace data //AppletType AppletType appletMode; - void loadDataInfo() + //Options + bool incDev = false, autoBack = true; + + void init() { //Clear titles + users just in case for(unsigned i = 0; i < users.size(); i++) @@ -97,6 +100,7 @@ namespace data users.clear(); loadBlacklist(); + loadCfg(); //Get system language and copy to std::string uint64_t lang; @@ -179,6 +183,13 @@ namespace data } } + if(data::incDev) + { + //Copy device saves to all accounts + for(unsigned i = 0; i < users.size() - 3; i++) + users[i].titles.insert(users[i].titles.end(), users[users.size() - 3].titles.begin(), users[users.size() - 3].titles.end()); + } + fsSaveDataInfoReaderClose(&saveIt); for(unsigned i = 0; i < users.size(); i++) @@ -194,6 +205,8 @@ namespace data for(unsigned i = 0; i < icons.size(); i++) icons[i].deleteData(); + + saveCfg(); } bool isAppletMode() @@ -391,4 +404,23 @@ namespace data int uInd = getUserIndex(u.getUID()); u = users[uInd]; } + + void loadCfg() + { + if(fs::fileExists(fs::getWorkDir() + "cfg.bin")) + { + FILE *cfg = fopen(std::string(fs::getWorkDir() + "cfg.bin").c_str(), "rb"); + data::incDev = fgetc(cfg); + data::autoBack = fgetc(cfg); + fclose(cfg); + } + } + + void saveCfg() + { + FILE *cfg = fopen(std::string(fs::getWorkDir() + "cfg.bin").c_str(), "wb"); + fputc(data::incDev, cfg); + fputc(data::autoBack, cfg); + fclose(cfg); + } } diff --git a/src/main.cpp b/src/main.cpp index cfdff8b..5bedbbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,7 +40,7 @@ int main(int argc, const char *argv[]) graphicsInit(1280, 720); //Needed for icon gen ui::initTheme(); - data::loadDataInfo(); + data::init(); ui::init(); //built with 'make debug CFLAGS:=-D__debug__' diff --git a/src/ui.cpp b/src/ui.cpp index 75dd32d..d8de6cf 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -19,17 +19,18 @@ std::vector usrNav, ttlNav, fldNav; static tex *top, *bot; //Help text -static const std::string userHelp = "\ue0e0 Select \ue0e3 Dump All \ue0e2 UI Mode \ue0f0 Extras"; +static const std::string userHelp = "\ue0e0 Select \ue0e3 Dump All \ue0e2 UI Mode \ue0e7 Options \ue0f0 Extras"; static const std::string titleHelp = "\ue0e0 Select \ue0e4\ue0e5 Change User \ue0e3 Dump All \ue0e2 BlackList \ue0e1 Back"; static const std::string folderHelp = "\ue0f0 File Mode \ue0e4\ue0e5 AutoName \ue0e0 Backup \ue0e3 Restore \ue0e2 Delete \ue0e1 Back"; +static const std::string optHelp = "\ue0e0 Toggle \ue0e1 Back"; //X position of help texts. Calculated to make editing quicker/easier -static unsigned userHelpX, titleHelpX, folderHelpX; +static unsigned userHelpX, titleHelpX, folderHelpX, optHelpX; namespace ui { - //Classic mode - bool clsMode = false; + //text mode + bool textMode = false; //Current menu state int mstate = USR_SEL, prevState = USR_SEL; @@ -116,9 +117,9 @@ namespace ui if(fs::fileExists(fs::getWorkDir() + "cls.txt")) { - clsUserPrep(); - clsMode = true; - mstate = CLS_USR; + textUserPrep(); + textMode = true; + mstate = TXT_USR; } setupSelButtons(); @@ -146,8 +147,11 @@ namespace ui userHelpX = 1220 - textGetWidth(userHelp.c_str(), ui::shared, 18); titleHelpX = 1220 - textGetWidth(titleHelp.c_str(), ui::shared, 18); folderHelpX = 1220 - textGetWidth(folderHelp.c_str(), ui::shared, 18); + optHelpX = 1220 - textGetWidth(optHelp.c_str(), ui::shared, 18); advCopyMenuPrep(); + ui::exMenuPrep(); + ui::optMenuInit(); } void exit() @@ -232,10 +236,11 @@ namespace ui drawRect(frameBuffer, 640, 87, 1, 561, divClr); break; - case CLS_TTL: - case CLS_USR: - case CLS_FLD: + case TXT_TTL: + case TXT_USR: + case TXT_FLD: case EX_MNU: + case OPT_MNU: texDrawNoAlpha(sideBar, frameBuffer, 0, 88); break; } @@ -243,19 +248,23 @@ namespace ui switch(mstate) { case USR_SEL: - case CLS_USR: + case TXT_USR: drawText(userHelp.c_str(), frameBuffer, shared, userHelpX, 676, 18, mnuTxt); break; case TTL_SEL: - case CLS_TTL: + case TXT_TTL: drawText(titleHelp.c_str(), frameBuffer, shared, titleHelpX, 676, 18, mnuTxt); break; case FLD_SEL: - case CLS_FLD: + case TXT_FLD: drawText(folderHelp.c_str(), frameBuffer, shared, folderHelpX, 676, 18, mnuTxt); break; + + case OPT_MNU: + drawText(optHelp.c_str(), frameBuffer, ui::shared, optHelpX, 676, 18, ui::mnuTxt); + break; } } @@ -323,21 +332,25 @@ namespace ui updateAdvMode(down, held, p); break; - case CLS_USR: - classicUserMenuUpdate(down, held, p); + case TXT_USR: + textUserMenuUpdate(down, held, p); break; - case CLS_TTL: - classicTitleMenuUpdate(down, held, p); + case TXT_TTL: + textTitleMenuUpdate(down, held, p); break; - case CLS_FLD: - classicFolderMenuUpdate(down, held, p); + case TXT_FLD: + textFolderMenuUpdate(down, held, p); break; case EX_MNU: updateExMenu(down, held, p); break; + + case OPT_MNU: + updateOptMenu(down, held, p); + break; } drawPopup(down); diff --git a/src/ui/advmode.cpp b/src/ui/advmode.cpp index 18e275d..4a95e83 100644 --- a/src/ui/advmode.cpp +++ b/src/ui/advmode.cpp @@ -494,8 +494,8 @@ namespace ui mstate = EX_MNU; else if(prevState == TTL_SEL) mstate = TTL_SEL; - else if(ui::clsMode) - mstate = CLS_FLD; + else if(ui::textMode) + mstate = TXT_FLD; else mstate = FLD_SEL; } diff --git a/src/ui/fldrsel.cpp b/src/ui/fldrsel.cpp index f26d846..98f1aed 100644 --- a/src/ui/fldrsel.cpp +++ b/src/ui/fldrsel.cpp @@ -100,7 +100,7 @@ namespace ui } else if(down & KEY_Y || fldNav[1].getEvent() == BUTTON_RELEASED) { - if(data::curData.getType() != FsSaveDataType_SystemBcat && folderMenu.getSelected() > 0) + if(data::curData.getType() != FsSaveDataType_System && folderMenu.getSelected() > 0) { std::string scanPath = util::getTitleDir(data::curUser, data::curData); fs::dirList list(scanPath); @@ -108,6 +108,16 @@ namespace ui std::string folderName = list.getItem(folderMenu.getSelected() - 1); if(confirm("Are you sure you want to restore \"" + folderName + "\"?", false)) { + if(data::autoBack) + { + std::string autoFolder = util::getTitleDir(data::curUser, data::curData) + "/AUTO - " + data::curUser.getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD); + mkdir(autoFolder.c_str(), 777); + autoFolder += "/"; + + std::string root = "sv:/"; + fs::copyDirToDir(root, autoFolder); + } + std::string fromPath = util::getTitleDir(data::curUser, data::curData) + folderName + "/"; std::string root = "sv:/"; @@ -115,6 +125,10 @@ namespace ui fsdevCommitDevice("sv"); fs::copyDirToDirCommit(fromPath, root, "sv"); + + //Rescan init folder menu if autobak to show changes + if(data::autoBack) + folderMenuPrepare(data::curUser, data::curData); } } } diff --git a/src/ui/miscui.cpp b/src/ui/miscui.cpp index 4e81b66..3b0389c 100644 --- a/src/ui/miscui.cpp +++ b/src/ui/miscui.cpp @@ -249,6 +249,8 @@ namespace ui yes.update(p); no.update(p); + std::string holdText; + if(hold && (held & KEY_A || yes.getEvent() == BUTTON_PRESSED)) { heldDown = true; @@ -266,7 +268,15 @@ namespace ui break; } - yes.setText(std::string("(Hold) " + loadGlyphArray[loadFrame])); + if(holdCount <= 60) + holdText = "(Hold) "; + else if(holdCount <= 120) + holdText = "(Keep Holding) "; + else if(holdCount < 180) + holdText = "(Almost There!) "; + + holdText += loadGlyphArray[loadFrame]; + yes.setText(holdText); } else if((hold && heldDown) && (up & KEY_A || yes.getEvent() == BUTTON_RELEASED)) { diff --git a/src/ui/clsui.cpp b/src/ui/txtui.cpp similarity index 81% rename from src/ui/clsui.cpp rename to src/ui/txtui.cpp index 421f386..76415da 100644 --- a/src/ui/clsui.cpp +++ b/src/ui/txtui.cpp @@ -10,13 +10,30 @@ #include "util.h" #include "ex.h" -static ui::menu userMenu, titleMenu, devMenu; +static ui::menu userMenu, titleMenu, exMenu, optMenu; extern ui::menu folderMenu; extern std::vector usrNav, ttlNav, fldNav; +//Help text for options +static const std::string optHelp[] = +{ + "Includes all Device Save data in Account Saves.", + "Automatically create a backup before restoring a save. Just to be safe." +}; + +static inline void switchBool(bool& sw) +{ + sw ? sw = false : sw = true; +} + +static inline std::string getBoolText(bool b) +{ + return b ? "On" : "Off"; +} + namespace ui { - void clsUserPrep() + void textUserPrep() { userMenu.reset(); @@ -26,7 +43,7 @@ namespace ui userMenu.addOpt(data::users[i].getUsername()); } - void clsTitlePrep(data::user& u) + void textTitlePrep(data::user& u) { titleMenu.reset(); titleMenu.setParams(76, 98, 310); @@ -35,7 +52,7 @@ namespace ui titleMenu.addOpt(u.titles[i].getTitle()); } - void clsFolderPrep(data::user& usr, data::titledata& dat) + void textFolderPrep(data::user& usr, data::titledata& dat) { folderMenu.setParams(466, 98, 730); folderMenu.reset(); @@ -51,7 +68,7 @@ namespace ui folderMenu.adjust(); } - void classicUserMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p) + void textUserMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p) { userMenu.handleInput(down, held, p); userMenu.draw(mnuTxt); @@ -62,9 +79,9 @@ namespace ui if(down & KEY_A || usrNav[0].getEvent() == BUTTON_RELEASED) { data::curUser = data::users[userMenu.getSelected()]; - clsTitlePrep(data::curUser); + textTitlePrep(data::curUser); - mstate = CLS_TTL; + mstate = TXT_TTL; } else if(down & KEY_Y || usrNav[1].getEvent() == BUTTON_RELEASED) { @@ -74,7 +91,7 @@ namespace ui else if(down & KEY_X || usrNav[2].getEvent() == BUTTON_RELEASED) { std::remove(std::string(fs::getWorkDir() + "cls.txt").c_str()); - clsMode = false; + ui::textMode = false; mstate = USR_SEL; } else if(down & KEY_MINUS || usrNav[3].getEvent() == BUTTON_RELEASED) @@ -85,7 +102,7 @@ namespace ui } } - void classicTitleMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p) + void textTitleMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p) { titleMenu.handleInput(down, held, p); titleMenu.draw(mnuTxt); @@ -100,9 +117,9 @@ namespace ui if(fs::mountSave(data::curUser, data::curData)) { util::makeTitleDir(data::curUser, data::curData); - clsFolderPrep(data::curUser, data::curData); + textFolderPrep(data::curUser, data::curData); - mstate = CLS_FLD; + mstate = TXT_FLD; } } else if(down & KEY_Y || ttlNav[1].getEvent() == BUTTON_RELEASED) @@ -117,7 +134,7 @@ namespace ui data::blacklistAdd(data::curUser, data::curUser.titles[titleMenu.getSelected()]); } else if(down & KEY_B || ttlNav[3].getEvent() == BUTTON_RELEASED) - mstate = CLS_USR; + mstate = TXT_USR; else if(down & KEY_L) { data::selUser--; @@ -125,7 +142,7 @@ namespace ui data::selUser = data::users.size() -1; data::curUser = data::users[data::selUser]; - clsTitlePrep(data::curUser); + textTitlePrep(data::curUser); ui::showPopup(data::curUser.getUsername(), POP_FRAME_DEFAULT); } @@ -136,13 +153,13 @@ namespace ui data::selUser = 0; data::curUser = data::users[data::selUser]; - clsTitlePrep(data::curUser); + textTitlePrep(data::curUser); ui::showPopup(data::curUser.getUsername(), POP_FRAME_DEFAULT); } } - void classicFolderMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p) + void textFolderMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p) { titleMenu.draw(mnuTxt); folderMenu.handleInput(down, held, p); @@ -184,7 +201,7 @@ namespace ui std::string root = "sv:/"; fs::copyDirToDir(root, path); - clsFolderPrep(data::curUser, data::curData); + textFolderPrep(data::curUser, data::curData); } } else @@ -240,7 +257,7 @@ namespace ui fs::delDir(delPath); } - clsFolderPrep(data::curUser, data::curData); + textFolderPrep(data::curUser, data::curData); } } else if(down & KEY_MINUS) @@ -251,34 +268,34 @@ namespace ui else if(down & KEY_B || fldNav[3].getEvent() == BUTTON_RELEASED) { fsdevUnmountDevice("sv"); - mstate = CLS_TTL; + mstate = TXT_TTL; } } void exMenuPrep() { - devMenu.reset(); - devMenu.setParams(76, 98, 310); - devMenu.addOpt("SD to SD Browser"); - devMenu.addOpt("Bis: PRODINFOF"); - devMenu.addOpt("Bis: SAFE"); - devMenu.addOpt("Bis: SYSTEM"); - devMenu.addOpt("Bis: USER"); - devMenu.addOpt("Remove Downloaded Update"); - devMenu.addOpt("Terminate Process ID"); - devMenu.addOpt("Mount System Save ID"); - devMenu.addOpt("Mount Process RomFS"); + exMenu.reset(); + exMenu.setParams(76, 98, 310); + exMenu.addOpt("SD to SD Browser"); + exMenu.addOpt("Bis: PRODINFOF"); + exMenu.addOpt("Bis: SAFE"); + exMenu.addOpt("Bis: SYSTEM"); + exMenu.addOpt("Bis: USER"); + exMenu.addOpt("Remove Downloaded Update"); + exMenu.addOpt("Terminate Process ID"); + exMenu.addOpt("Mount System Save ID"); + exMenu.addOpt("Mount Process RomFS"); } void updateExMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p) { - devMenu.handleInput(down, held, p); + exMenu.handleInput(down, held, p); if(down & KEY_A) { FsFileSystem sv; - data::curData.setType(FsSaveDataType_SystemBcat); - switch(devMenu.getSelected()) + data::curData.setType(FsSaveDataType_System); + switch(exMenu.getSelected()) { case 0: data::curData.setType(FsSaveDataType_Bcat); @@ -401,14 +418,51 @@ namespace ui else if(down & KEY_B) { fsdevUnmountDevice("sv"); - if(ui::clsMode) - mstate = CLS_USR; + if(ui::textMode) + mstate = TXT_USR; else mstate = USR_SEL; prevState = USR_SEL; } - devMenu.draw(mnuTxt); + exMenu.draw(mnuTxt); + } + + void optMenuInit() + { + optMenu.setParams(76, 98, 310); + optMenu.addOpt("Inc. Dev Sv"); + optMenu.addOpt("Auto Backup"); + } + + void updateOptMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p) + { + optMenu.handleInput(down, held, p); + + //Update Menu Options to reflect changes + optMenu.editOpt(0, "Include Dev Sv: " + getBoolText(data::incDev)); + optMenu.editOpt(1, "Auto Backup: " + getBoolText(data::autoBack)); + + if(down & KEY_A) + { + switch(optMenu.getSelected()) + { + case 0: + switchBool(data::incDev); + break; + + case 1: + switchBool(data::autoBack); + break; + } + } + else if(down & KEY_B) + { + ui::mstate = ui::textMode ? TXT_USR : USR_SEL; + } + + optMenu.draw(ui::mnuTxt); + drawTextWrap(optHelp[optMenu.getSelected()].c_str(), frameBuffer, ui::shared, 466, 98, 24, ui::mnuTxt, 730); } } diff --git a/src/ui/usrsel.cpp b/src/ui/usrsel.cpp index 8564b46..5e9ad5e 100644 --- a/src/ui/usrsel.cpp +++ b/src/ui/usrsel.cpp @@ -137,22 +137,26 @@ namespace ui } else if(down & KEY_Y || usrNav[1].getEvent() == BUTTON_RELEASED) { - for(unsigned i = 0; i < data::users.size() - 3; i++) + for(unsigned i = 0; i < data::users.size() - 2; i++) fs::dumpAllUserSaves(data::users[i]); } else if(down & KEY_X || usrNav[2].getEvent() == BUTTON_RELEASED) { FILE *cls = fopen(std::string(fs::getWorkDir() + "cls.txt").c_str(), "w"); fclose(cls); - clsUserPrep(); - mstate = CLS_USR; - clsMode = true; + textUserPrep(); + mstate = TXT_USR; + textMode = true; } else if(down & KEY_MINUS || usrNav[3].getEvent() == BUTTON_RELEASED) { fsdevUnmountDevice("sv"); - ui::exMenuPrep(); + ui::mstate = EX_MNU; } + else if(down & KEY_ZR) + { + ui::mstate = OPT_MNU; + } } }