Add options menu

This commit is contained in:
J-D-K 2020-04-02 20:16:16 -04:00
parent 5abd0f0a3a
commit 6b555ad656
13 changed files with 231 additions and 97 deletions

View File

@ -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:

View File

@ -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

View File

@ -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<user> 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

24
inc/txtui.h Normal file
View File

@ -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

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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__'

View File

@ -19,17 +19,18 @@ std::vector<ui::button> 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);

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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))
{

View File

@ -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<ui::button> 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);
}
}

View File

@ -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;
}
}
}