From 11a382794b6ffe728b86b9baba2637c7eb5e5ca1 Mon Sep 17 00:00:00 2001 From: J-D-K Date: Thu, 27 Aug 2020 16:11:18 -0400 Subject: [PATCH] Add sort options --- Makefile | 2 +- inc/data.h | 9 +++++++-- inc/uistr.h | 6 ++++-- romfs/lang/en-US.txt | 7 +++++++ src/data.cpp | 48 ++++++++++++++++++++++++++++++++++---------- src/ui.cpp | 9 ++++++++- src/ui/txtui.cpp | 37 ++++++++++++++++++++++++---------- src/ui/uistr.cpp | 8 +++++--- src/util.cpp | 10 ++++++++- 9 files changed, 104 insertions(+), 32 deletions(-) diff --git a/Makefile b/Makefile index 3d363b1..05dd62d 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ INCLUDES := inc EXEFS_SRC := exefs_src APP_TITLE := JKSV APP_AUTHOR := JK -APP_VERSION := 08.04.2020 +APP_VERSION := 08.27.2020 ROMFS := romfs ICON := romfs/icon.jpg diff --git a/inc/data.h b/inc/data.h index 432de64..592eb21 100644 --- a/inc/data.h +++ b/inc/data.h @@ -13,7 +13,7 @@ #define curData users[data::selUser].titles[data::selData] #define BLD_MON 8 -#define BLD_DAY 4 +#define BLD_DAY 27 #define BLD_YEAR 2020 namespace data @@ -72,6 +72,10 @@ namespace data tex *getIconFav() const { return favIcon; } void setPlayTime(const uint32_t& _p){ playMins = _p; } uint32_t getPlayTime() const { return playMins; } + void setLastTimeStamp(const uint32_t& _ts){ lastTimeStamp = _ts; } + uint32_t getLastTimeStamp() const { return lastTimeStamp; } + void setLaunchCount(const uint32_t& _lc) { launchCount = _lc; } + uint32_t getLaunchCount() const { return launchCount; } private: tex *icon, *favIcon; @@ -79,7 +83,7 @@ namespace data std::string title, titleSafe, author; uint64_t id, saveID; uint16_t saveIndex; - uint32_t playMins; + uint32_t playMins, lastTimeStamp, launchCount; bool favorite = false; }; @@ -136,6 +140,7 @@ namespace data extern int selUser, selData; extern SetLanguage sysLang; extern bool incDev, autoBack, ovrClk, holdDel, holdRest, holdOver, forceMount, accSysSave, sysSaveWrite, directFsCmd, skipUser, zip; + extern uint8_t sortType; } #endif // DATA_H diff --git a/inc/uistr.h b/inc/uistr.h index a2247e1..6ea67e2 100644 --- a/inc/uistr.h +++ b/inc/uistr.h @@ -14,11 +14,13 @@ namespace ui //Strings for extras menu extern std::string exMenuStr[11]; //Strings for options menu - extern std::string optMenuStr[13]; + extern std::string optMenuStr[14]; //Strings for options explanations - extern std::string optMenuExp[13]; + extern std::string optMenuExp[14]; //Strings for the holding thing extern std::string holdingText[3]; + //Strings for sort type + extern std::string sortString[3]; } #endif // UISTR_H diff --git a/romfs/lang/en-US.txt b/romfs/lang/en-US.txt index 0425fb1..2aa5c73 100644 --- a/romfs/lang/en-US.txt +++ b/romfs/lang/en-US.txt @@ -107,6 +107,7 @@ optMenu = 9, "Text UI Mode: " optMenu = 10, "Direct FS Cmd: " optMenu = 11, "Skip User Select: " optMenu = 12, "Export to ZIP: " +optMenu = 13, "Sort: " #Explanations of what options do. optMenuExp = 0, "Includes Device Save data in user accounts." @@ -122,3 +123,9 @@ optMenuExp = 9, "Changes the UI to be text menu based like the original JKSM for optMenuExp = 10, "Directly uses the Switch's FS commands to copy files instead of stdio." optMenuExp = 11, "Skips the user selection screen and jumps directly to the first user account found." optMenuExp = 12, "Exports saves to zip files." +optMenuExp = 13, ""Changes the way titles are sorted and listed." + +#Sort Types +sortType = 0, "Alphabetical" +sortType = 1, "Time Played" +sortType = 2, "Last Played" diff --git a/src/data.cpp b/src/data.cpp index cb76ae2..b3c5ea8 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -28,6 +28,7 @@ SetLanguage data::sysLang; //Options bool data::incDev = false, data::autoBack = true, data::ovrClk = false, data::holdDel = true, data::holdRest = true, data::holdOver = true; bool data::forceMount = true, data::accSysSave = false, data::sysSaveWrite = false, data::directFsCmd = false, data::skipUser = false, data::zip = false; +uint8_t data::sortType = 0; //For other save types static bool sysBCATPushed = false, cachePushed = false, tempPushed = false; @@ -37,23 +38,39 @@ static std::vector favorites; static std::unordered_map pathDefs; std::unordered_map> data::icons; -//Sorts titles sort-of alphabetically +//Sorts titles by sortType static struct { bool operator()(const data::titledata& a, const data::titledata& b) { + //Favorites override EVERYTHING if(a.getFav() != b.getFav()) return a.getFav(); - uint32_t tmpA, tmpB; - for(unsigned i = 0; i < a.getTitle().length(); ) + switch(data::sortType) { - ssize_t uCnt = decode_utf8(&tmpA, (const uint8_t *)&a.getTitle().data()[i]); - decode_utf8(&tmpB, (const uint8_t *)&b.getTitle().data()[i]); - tmpA = tolower(tmpA), tmpB = tolower(tmpB); - if(tmpA != tmpB) - return tmpA < tmpB; + case 0://Alpha + { + uint32_t tmpA, tmpB; + for(unsigned i = 0; i < a.getTitle().length(); ) + { + ssize_t uCnt = decode_utf8(&tmpA, (const uint8_t *)&a.getTitle().data()[i]); + decode_utf8(&tmpB, (const uint8_t *)&b.getTitle().data()[i]); + tmpA = tolower(tmpA), tmpB = tolower(tmpB); + if(tmpA != tmpB) + return tmpA < tmpB; - i += uCnt; + i += uCnt; + } + } + break; + + case 1://Most played + return a.getPlayTime() > b.getPlayTime(); + break; + + case 2://Last Played + return a.getLastTimeStamp() > b.getLastTimeStamp(); + break; } return false; } @@ -312,7 +329,6 @@ void data::exit() saveFav(); saveBlackList(); - saveCfg(); util::setCPU(1020000000); } @@ -441,20 +457,26 @@ void data::user::loadPlayTimes() PdmPlayStatistics stats; for(data::titledata& _d : titles) { - switch(_d.getType())//Users can hold device saves + switch(_d.getType()) { case FsSaveDataType_Account: pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(_d.getID(), userID, false, &stats); _d.setPlayTime(stats.playtimeMinutes); + _d.setLastTimeStamp(stats.last_timestampUser); + _d.setLaunchCount(stats.totalLaunches); break; case FsSaveDataType_Device: pdmqryQueryPlayStatisticsByApplicationId(_d.getID(), false, &stats); _d.setPlayTime(stats.playtimeMinutes); + _d.setLastTimeStamp(stats.last_timestampNetwork); + _d.setLaunchCount(stats.totalLaunches); break; default: _d.setPlayTime(0); + _d.setLastTimeStamp(0); + _d.setLaunchCount(0); break; } } @@ -528,6 +550,7 @@ void data::loadCfg() uint64_t cfgIn = 0; fread(&cfgIn, sizeof(uint64_t), 1, cfg); + fread(&data::sortType, 1, 1, cfg); fclose(cfg); data::incDev = cfgIn >> 63 & 1; @@ -567,6 +590,7 @@ void data::saveCfg() cfgOut |= (uint64_t)data::skipUser << 52; cfgOut |= (uint64_t)data::zip << 51; fwrite(&cfgOut, sizeof(uint64_t), 1, cfg); + fwrite(&data::sortType, 1, 1, cfg); fclose(cfg); } @@ -586,6 +610,7 @@ void data::restoreDefaultConfig() data::directFsCmd = false; data::skipUser = false; data::zip = false; + data::sortType = 0; } void data::loadFav() @@ -632,5 +657,6 @@ void data::dispStats() stats += "Current Title: " + data::curData.getTitle() + "\n"; stats += "Safe Title: " + data::curData.getTitleSafe() + "\n"; stats += "Icon count: " + std::to_string(icons.size()) + "\n"; + stats += "Sort Type: " + std::to_string(data::sortType) + "\n"; drawText(stats.c_str(), frameBuffer, ui::shared, 2, 2, 16, clrCreateU32(0xFF00DD00)); } diff --git a/src/ui.cpp b/src/ui.cpp index f2a15fb..334388d 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -60,7 +60,7 @@ static std::unordered_map uistrdef = {"confirmRestore", 11}, {"confirmDelete", 12}, {"confirmCopy", 13}, {"confirmEraseNand", 14}, {"confirmEraseFolder", 15}, {"confirmHead", 16}, {"copyHead", 17}, {"noSavesFound", 18}, {"advMenu", 19}, {"extMenu", 20}, {"optMenu", 21}, {"optMenuExp", 22}, {"holdingText", 23}, - {"errorConnecting", 24}, {"noUpdate", 25} + {"errorConnecting", 24}, {"noUpdate", 25}, {"sortType", 26} }; static void loadTrans() @@ -217,6 +217,13 @@ static void loadTrans() ui::noUpdate = lang.getNextValueStr(); break; + case 26: + { + int ind = lang.getNextValueInt(); + ui::sortString[ind] = lang.getNextValueStr(); + } + break; + default: ui::showMessage("*Translation File Error:*", "On Line: %s\n*%s* is not a known or valid string name.", lang.getLine(), lang.getName()); break; diff --git a/src/ui/txtui.cpp b/src/ui/txtui.cpp index 07ece4f..c7b5f78 100644 --- a/src/ui/txtui.cpp +++ b/src/ui/txtui.cpp @@ -12,16 +12,6 @@ static ui::menu userMenu, titleMenu, exMenu, optMenu; extern ui::menu folderMenu; -static inline void switchBool(bool& sw) -{ - sw ? sw = false : sw = true; -} - -static inline std::string getBoolText(bool b) -{ - return b ? ui::on : ui::off; -} - void ui::textUserPrep() { userMenu.reset(); @@ -358,10 +348,26 @@ void ui::drawExMenu() exMenu.draw(ui::txtCont); } +static inline void switchBool(bool& sw) +{ + sw ? sw = false : sw = true; +} + +static inline std::string getBoolText(bool b) +{ + return b ? ui::on : ui::off; +} + +static inline void changeSort() +{ + if(++data::sortType > 2) + data::sortType = 0; +} + void ui::optMenuInit() { optMenu.setParams(76, 98, 310); - for(unsigned i = 0; i < 13; i++) + for(unsigned i = 0; i < 14; i++) optMenu.addOpt(ui::optMenuStr[i]); } @@ -383,6 +389,7 @@ void ui::updateOptMenu(const uint64_t& down, const uint64_t& held) optMenu.editOpt(10, optMenuStr[10] + getBoolText(data::directFsCmd)); optMenu.editOpt(11, optMenuStr[11] + getBoolText(data::skipUser)); optMenu.editOpt(12, optMenuStr[12] + getBoolText(data::zip)); + optMenu.editOpt(13, optMenuStr[13] + ui::sortString[data::sortType]); if(down & KEY_A) { @@ -439,12 +446,20 @@ void ui::updateOptMenu(const uint64_t& down, const uint64_t& held) case 12: switchBool(data::zip); break; + + case 13: + changeSort(); + data::loadUsersTitles(false); + break; } } else if(down & KEY_X) data::restoreDefaultConfig(); else if(down & KEY_B) + { + data::saveCfg(); ui::changeState(ui::textMode ? TXT_USR : USR_SEL); + } } void ui::drawOptMenu() diff --git a/src/ui/uistr.cpp b/src/ui/uistr.cpp index 85a21cd..d5ebbd6 100644 --- a/src/ui/uistr.cpp +++ b/src/ui/uistr.cpp @@ -23,8 +23,8 @@ std::string ui::errorConnecting = "Error Connecting!"; std::string ui::noUpdate = "No updates available!"; std::string ui::advMenuStr[6] = { "Copy to ", "Delete", "Rename", "Make Dir", "Properties", "Close" }; std::string ui::exMenuStr[11] = { "SD to SD Browser", "BIS: PRODINFOF", "BIS: SAFE", "BIS: SYSTEM", "BIS: USER", "Remove Update", "Terminate Process", "Mount System Save", "Rescan Titles", "Mount Process RomFS", "Backup JKSV Folder" }; -std::string ui::optMenuStr[13] = { "Include Dev Sv: ", "AutoBackup: ", "Overclock: ", "Hold to Delete: ", "Hold to Restore: ", "Hold to Overwrite: ", "Force Mount: ", "Account Sys. Saves: ", "Write to Sys. Saves: ", "Text UI Mode: ", "Direct FS Cmd: ", "Skip User Select: ", "Export to ZIP: " }; -std::string ui::optMenuExp[13] = +std::string ui::optMenuStr[14] = { "Include Dev Sv: ", "AutoBackup: ", "Overclock: ", "Hold to Delete: ", "Hold to Restore: ", "Hold to Overwrite: ", "Force Mount: ", "Account Sys. Saves: ", "Write to Sys. Saves: ", "Text UI Mode: ", "Direct FS Cmd: ", "Skip User Select: ", "Export to ZIP: ", "Sort: " }; +std::string ui::optMenuExp[14] = { "Includes Device Save data in user accounts.", "Automatically creates a save backup before restoring a save.", @@ -38,6 +38,8 @@ std::string ui::optMenuExp[13] = "Changes the UI to be text menu based like the original JKSM for 3DS.", "Directly uses the Switch's FS commands to copy files instead of stdio.", "Skips the user selection screen and jumps directly to the first user account found.", - "Exports saves to ZIP files." + "Exports saves to ZIP files.", + "Changes the way titles are sorted and listed." }; std::string ui::holdingText[3] = { "(Hold) ", "(Keep Holding) ", "(Almost there!) " }; +std::string ui::sortString[3] = { "Alphabetical", "Time Played", "Last Played" }; diff --git a/src/util.cpp b/src/util.cpp index ebf48b7..d56f4ca 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -179,6 +179,13 @@ std::string util::safeString(const std::string& s) return ret; } +static inline std::string getTimeString(const uint32_t& _h, const uint32_t& _m) +{ + char tmp[32]; + sprintf(tmp, "%02d:%02d", _h, _m); + return std::string(tmp); +} + std::string util::getInfoString(const data::user& u, const data::titledata& d) { std::string ret = d.getTitle() + "\n"; @@ -190,7 +197,8 @@ std::string util::getInfoString(const data::user& u, const data::titledata& d) hours = d.getPlayTime() / 60; mins = d.getPlayTime() - (hours * 60); - ret += "Play Time: " + std::to_string(hours) + ":" + std::to_string(mins) + "\n"; + ret += "Play Time: " + getTimeString(hours, mins) + "\n"; + ret += "Total Launches: " + std::to_string(d.getLaunchCount()) + "\n"; switch(d.getType()) {