diff --git a/Makefile b/Makefile index 2529838..362f337 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ INCLUDES := inc inc/ui inc/fs EXEFS_SRC := exefs_src APP_TITLE := JKSV APP_AUTHOR := JK -APP_VERSION := 11.15.2021 +APP_VERSION := 11.28.2021 ROMFS := romfs ICON := icon.jpg diff --git a/inc/cfg.h b/inc/cfg.h index 5bd49b1..c743fcc 100644 --- a/inc/cfg.h +++ b/inc/cfg.h @@ -34,4 +34,5 @@ namespace cfg extern std::vector blacklist; extern std::vector favorites; extern uint8_t sortType; + extern std::string driveClientID, driveClientSecret, driveRefreshToken, driveAuthCode; } diff --git a/inc/curlfuncs.h b/inc/curlfuncs.h index 3c21ed1..3a5a7dc 100644 --- a/inc/curlfuncs.h +++ b/inc/curlfuncs.h @@ -1,10 +1,35 @@ -#ifndef CURLFUNCS_H -#define CURLFUNCS_H +#pragma once #include #include -std::string getJSONURL(std::vector *headers, const std::string& _url); -bool getBinURL(std::vector *out, const std::string& _url); +#define HEADER_ERROR "ERROR" -#endif // CURLFUNCS_H +namespace curlFuncs +{ + typedef struct + { + FILE *f; + uint64_t *o; + } curlUpArgs; + + typedef struct + { + std::string path; + unsigned int size; + uint64_t *o; + } curlDlArgs; + + size_t writeDataString(const char *buff, size_t sz, size_t cnt, void *u); + size_t writeHeaders(const char *buff, size_t sz, size_t cnt, void *u); + size_t readDataFile(char *buff, size_t sz, size_t cnt, void *u); + size_t readDataBuffer(char *buff, size_t sz, size_t cnt, void *u); + size_t writeDataFile(const char *buff, size_t sz, size_t cnt, void *u); + size_t writeDataBuffer(const char *buff, size_t sz, size_t cnt, void *u); + + std::string getHeader(const std::string& _name, std::vector *h); + + //Shortcuts/legacy + std::string getJSONURL(std::vector *headers, const std::string& url); + bool getBinURL(std::vector *out, const std::string& url); +} diff --git a/inc/data.h b/inc/data.h index b0eaed3..3aaba21 100644 --- a/inc/data.h +++ b/inc/data.h @@ -8,13 +8,11 @@ #include "gfx.h" #define BLD_MON 11 -#define BLD_DAY 15 +#define BLD_DAY 28 #define BLD_YEAR 2021 namespace data { - extern bool forceMount; - //Loads user + title info void init(); void exit(); @@ -101,4 +99,4 @@ namespace data SDL_Texture *getTitleIconByTID(const uint64_t& tid); int getTitleIndexInUser(const data::user& u, const uint64_t& tid); extern SetLanguage sysLang; -} +} \ No newline at end of file diff --git a/inc/fs.h b/inc/fs.h index fe1147b..e4f899b 100644 --- a/inc/fs.h +++ b/inc/fs.h @@ -8,9 +8,12 @@ #include "fs/dir.h" #include "fs/zip.h" #include "fs/fsfile.h" +#include "fs/drive.h" #include "ui/miscui.h" -#define BUFF_SIZE 0xC0000 +#define BUFF_SIZE 0x8000 +#define ZIP_BUFF_SIZE 0x10000 +#define TRANSFER_BUFFER_LIMIT 0xA00000 namespace fs { diff --git a/inc/fs/dir.h b/inc/fs/dir.h index f817d29..ae3988d 100644 --- a/inc/fs/dir.h +++ b/inc/fs/dir.h @@ -45,6 +45,7 @@ namespace fs std::string getItemExt(int index) const { return item[index].getExt(); } bool isDir(int index) const { return item[index].isDir(); } unsigned getCount() const { return item.size(); } + fs::dirItem *getDirItemAt(unsigned int _ind) { return &item[_ind]; } private: std::string path; diff --git a/inc/fs/drive.h b/inc/fs/drive.h new file mode 100644 index 0000000..f155214 --- /dev/null +++ b/inc/fs/drive.h @@ -0,0 +1,10 @@ +#pragma once + +#include "../gd.h" + +namespace fs +{ + extern drive::gd *gDrive; + void driveInit(); + void driveExit(); +} \ No newline at end of file diff --git a/inc/fs/fstype.h b/inc/fs/fstype.h index f2b7ffb..4f9e2d5 100644 --- a/inc/fs/fstype.h +++ b/inc/fs/fstype.h @@ -1,7 +1,7 @@ #pragma once #include "data.h" -#include "ui.h" +#include "miscui.h" #include "type.h" namespace fs diff --git a/inc/gd.h b/inc/gd.h new file mode 100644 index 0000000..f4b269a --- /dev/null +++ b/inc/gd.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "curlfuncs.h" + +#define HEADER_CONTENT_TYPE_APP_JSON "Content-Type: application/json; charset=UTF-8" +#define HEADER_AUTHORIZATION "Authorization: Bearer " + +#define MIMETYPE_FOLDER "application/vnd.google-apps.folder" + +namespace drive +{ + typedef struct + { + std::string name, id, mimeType; + unsigned int size; + std::vector parents; + } gdDirItem; + + class gd + { + public: + gd(const std::string& _clientID, const std::string& _secretID, const std::string& _authCode, const std::string& _rToken); + + void exhangeAuthCode(const std::string& _authCode); + bool hasToken() { return token.empty() == false; } + void refreshToken(); + bool tokenIsValid(); + //Drive query parameters are appened to the default + void loadDriveList(const std::string& _qParams); + + void debugWriteList(); + + bool createDir(const std::string& _dirName); + bool dirExists(const std::string& _dirName); + void setRootDir(const std::string& _dirID) + { + rootDir = _dirID; + parentDir = _dirID; + loadDriveList(""); + } + void returnToRoot(){ parentDir = rootDir; loadDriveList(""); } + void chDir(const std::string& _dirID) { parentDir = _dirID; loadDriveList(""); } + + bool fileExists(const std::string& _filename); + void uploadFile(const std::string& _filename, curlFuncs::curlUpArgs *_upload); + void updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_upload); + void downloadFile(const std::string& _fileID, curlFuncs::curlDlArgs *_download); + void deleteFile(const std::string& _fileID); + + std::string getClientID() const { return clientID; } + std::string getClientSecret() const { return secretID; } + std::string getRefreshToken() const { return rToken; } + std::string getFileID(const std::string& name); + + size_t getDriveListCount() const { return driveList.size(); } + drive::gdDirItem *getDirItemAt(unsigned int _ind) { return &driveList[_ind]; } + + private: + std::vector driveList; + std::string clientID, secretID, token, rToken; + std::string rootDir = "", parentDir = ""; + }; +} \ No newline at end of file diff --git a/inc/ui.h b/inc/ui.h index 3d779c5..ed91dbb 100644 --- a/inc/ui.h +++ b/inc/ui.h @@ -15,6 +15,7 @@ #include "ui/sldpanel.h" #include "ui/usr.h" #include "ui/ttl.h" +#include "ui/fld.h" #include "ui/sett.h" #include "ui/ext.h" #include "ui/fm.h" diff --git a/inc/ui/fld.h b/inc/ui/fld.h new file mode 100644 index 0000000..a1947ab --- /dev/null +++ b/inc/ui/fld.h @@ -0,0 +1,19 @@ +#pragma once + +#include "ui.h" +#include "dir.h" + +namespace ui +{ + //extern ui::menu *fldMenu; + extern ui::slideOutPanel *fldPanel; + //extern fs::dirList *fldList; + + void fldInit(); + void fldExit(); + void fldUpdate(); + + //Populate to open menu, refresh for updating after actions + void fldPopulateMenu(); + void fldRefreshMenu(bool _updateDrive); +} \ No newline at end of file diff --git a/inc/ui/ttl.h b/inc/ui/ttl.h index 35dc708..053dfe2 100644 --- a/inc/ui/ttl.h +++ b/inc/ui/ttl.h @@ -4,9 +4,8 @@ namespace ui { void ttlInit(); void ttlExit(); - void ttlSetActive(int usr); + void ttlSetActive(int usr, bool _set, bool _showSel); void ttlRefresh(); - void populateFldMenu(); //JIC for func ptr void ttlReset(); diff --git a/inc/util.h b/inc/util.h index 8b4e33c..22fc442 100644 --- a/inc/util.h +++ b/inc/util.h @@ -1,5 +1,4 @@ -#ifndef UTIL_H -#define UTIL_H +#pragma once #include "data.h" #include "ui.h" @@ -37,6 +36,37 @@ namespace util CPU_SPEED_1785MHz = 1785000000 } cpuSpds; + typedef enum + { + GPU_SPEED_0MHz = 0, + GPU_SPEED_76MHz = 76800000, + GPU_SPEED_153MHz = 153600000, + GPU_SPEED_203MHz = 230400000, + GPU_SPEED_307MHz = 307200000, //Handheld 1 + GPU_SPEED_384MHz = 384000000, //Handheld 2 + GPU_SPEED_460MHz = 460800000, + GPU_SPEED_537MHz = 537600000, + GPU_SPEED_614MHz = 614400000, + GPU_SPEED_768MHz = 768000000, //Docked + GPU_SPEED_844MHz = 844800000, + GPU_SPEED_921MHZ = 921600000 + } gpuSpds; + + typedef enum + { + RAM_SPEED_0MHz = 0, + RAM_SPEED_40MHz = 40800000, + RAM_SPEED_68MHz = 68000000, + RAM_SPEED_102MHz = 102000000, + RAM_SPEED_204MHz = 204000000, + RAM_SPEED_408MHz = 408000000, + RAM_SPEED_665MHz = 665600000, + RAM_SPEED_800MHz = 800000000, + RAM_SPEED_1065MHz = 1065600000, + RAM_SPEED_1331MHz = 1331200000, + RAM_SPEED_1600MHz = 1600000000 + } ramSpds; + //Returns string with date S+ time std::string getDateTime(int fmt); @@ -112,7 +142,8 @@ namespace util Result accountDeleteUser(AccountUid *uid); - void setCPU(uint32_t hz); + void sysBoost(); + void sysNormal(); + void checkForUpdate(void *a); } -#endif // UTIL_H diff --git a/src/cfg.cpp b/src/cfg.cpp index 90675f8..9b301fb 100644 --- a/src/cfg.cpp +++ b/src/cfg.cpp @@ -14,13 +14,16 @@ std::vector cfg::blacklist; std::vector cfg::favorites; static std::unordered_map pathDefs; uint8_t cfg::sortType; +std::string cfg::driveClientID, cfg::driveClientSecret, cfg::driveRefreshToken, cfg::driveAuthCode; + const char *cfgPath = "sdmc:/config/JKSV/JKSV.cfg", *titleDefPath = "sdmc:/config/JKSV/titleDefs.txt", *workDirLegacy = "sdmc:/switch/jksv_dir.txt"; static std::unordered_map cfgStrings = { {"workDir", 0}, {"includeDeviceSaves", 1}, {"autoBackup", 2}, {"overclock", 3}, {"holdToDelete", 4}, {"holdToRestore", 5}, {"holdToOverwrite", 6}, {"forceMount", 7}, {"accountSystemSaves", 8}, {"allowSystemSaveWrite", 9}, {"directFSCommands", 10}, {"exportToZIP", 11}, {"languageOverride", 12}, {"enableTrashBin", 13}, {"titleSortType", 14}, {"animationScale", 15}, - {"favorite", 16}, {"blacklist", 17}, {"autoName", 18} + {"favorite", 16}, {"blacklist", 17}, {"autoName", 18}, {"driveClientID", 19}, {"driveClientSecret", 20}, {"driveRefreshToken", 21}, + {"driveAuthCode", 22} }; const std::string _true_ = "true", _false_ = "false"; @@ -102,12 +105,13 @@ bool cfg::isDefined(const uint64_t& tid) void cfg::pathDefAdd(const uint64_t& tid, const std::string& newPath) { - std::string oldSafe = data::titles[tid].safeTitle; + data::titleInfo *t = data::getTitleInfoByTID(tid); + std::string oldSafe = t->safeTitle; std::string tmp = util::safeString(newPath); if(!tmp.empty()) { pathDefs[tid] = tmp; - data::titles[tid].safeTitle = tmp; + t->safeTitle = tmp; std::string oldOutput = fs::getWorkDir() + oldSafe; std::string newOutput = fs::getWorkDir() + tmp; @@ -302,103 +306,123 @@ void cfg::loadConfig() fs::dataFile cfgRead(cfgPath); while(cfgRead.readNextLine(true)) { - switch(cfgStrings[cfgRead.getName()]) + std::string varName = cfgRead.getName(); + if(cfgStrings.find(varName) != cfgStrings.end()) { - case 0: - fs::setWorkDir(cfgRead.getNextValueStr()); - break; + switch(cfgStrings[varName]) + { + case 0: + fs::setWorkDir(cfgRead.getNextValueStr()); + break; - case 1: - cfg::config["incDev"] = textToBool(cfgRead.getNextValueStr()); - break; + case 1: + cfg::config["incDev"] = textToBool(cfgRead.getNextValueStr()); + break; - case 2: - cfg::config["autoBack"] = textToBool(cfgRead.getNextValueStr()); - break; + case 2: + cfg::config["autoBack"] = textToBool(cfgRead.getNextValueStr()); + break; - case 3: - cfg::config["ovrClk"] = textToBool(cfgRead.getNextValueStr()); - break; + case 3: + cfg::config["ovrClk"] = textToBool(cfgRead.getNextValueStr()); + break; - case 4: - cfg::config["holdDel"] = textToBool(cfgRead.getNextValueStr()); - break; + case 4: + cfg::config["holdDel"] = textToBool(cfgRead.getNextValueStr()); + break; - case 5: - cfg::config["holdRest"] = textToBool(cfgRead.getNextValueStr()); - break; + case 5: + cfg::config["holdRest"] = textToBool(cfgRead.getNextValueStr()); + break; - case 6: - cfg::config["holdOver"] = textToBool(cfgRead.getNextValueStr()); - break; + case 6: + cfg::config["holdOver"] = textToBool(cfgRead.getNextValueStr()); + break; - case 7: - cfg::config["forceMount"] = textToBool(cfgRead.getNextValueStr()); - break; + case 7: + cfg::config["forceMount"] = textToBool(cfgRead.getNextValueStr()); + break; - case 8: - cfg::config["accSysSave"] = textToBool(cfgRead.getNextValueStr()); - break; + case 8: + cfg::config["accSysSave"] = textToBool(cfgRead.getNextValueStr()); + break; - case 9: - cfg::config["sysSaveWrite"] = textToBool(cfgRead.getNextValueStr()); - break; + case 9: + cfg::config["sysSaveWrite"] = textToBool(cfgRead.getNextValueStr()); + break; - case 10: - cfg::config["directFsCmd"] = textToBool(cfgRead.getNextValueStr()); - break; + case 10: + cfg::config["directFsCmd"] = textToBool(cfgRead.getNextValueStr()); + break; - case 11: - cfg::config["zip"] = textToBool(cfgRead.getNextValueStr()); - break; + case 11: + cfg::config["zip"] = textToBool(cfgRead.getNextValueStr()); + break; - case 12: - cfg::config["langOverride"] = textToBool(cfgRead.getNextValueStr()); - break; + case 12: + cfg::config["langOverride"] = textToBool(cfgRead.getNextValueStr()); + break; - case 13: - cfg::config["trashBin"] = textToBool(cfgRead.getNextValueStr()); - break; + case 13: + cfg::config["trashBin"] = textToBool(cfgRead.getNextValueStr()); + break; - case 14: - { - std::string getSort = cfgRead.getNextValueStr(); - if(getSort == "ALPHA") - cfg::sortType = cfg::ALPHA; - else if(getSort == "MOST_PLAYED") - cfg::sortType = cfg::MOST_PLAYED; - else - cfg::sortType = cfg::LAST_PLAYED; - } - break; + case 14: + { + std::string getSort = cfgRead.getNextValueStr(); + if(getSort == "ALPHA") + cfg::sortType = cfg::ALPHA; + else if(getSort == "MOST_PLAYED") + cfg::sortType = cfg::MOST_PLAYED; + else + cfg::sortType = cfg::LAST_PLAYED; + } + break; - case 15: - { - std::string animFloat = cfgRead.getNextValueStr(); - ui::animScale = strtof(animFloat.c_str(), NULL); - } - break; + case 15: + { + std::string animFloat = cfgRead.getNextValueStr(); + ui::animScale = strtof(animFloat.c_str(), NULL); + } + break; - case 16: - { - std::string tid = cfgRead.getNextValueStr(); - cfg::favorites.push_back(strtoul(tid.c_str(), NULL, 16)); - } - break; + case 16: + { + std::string tid = cfgRead.getNextValueStr(); + cfg::favorites.push_back(strtoul(tid.c_str(), NULL, 16)); + } + break; - case 17: - { - std::string tid = cfgRead.getNextValueStr(); - cfg::blacklist.push_back(strtoul(tid.c_str(), NULL, 16)); - } - break; + case 17: + { + std::string tid = cfgRead.getNextValueStr(); + cfg::blacklist.push_back(strtoul(tid.c_str(), NULL, 16)); + } + break; - case 18: - cfg::config["autoName"] = textToBool(cfgRead.getNextValueStr()); - break; + case 18: + cfg::config["autoName"] = textToBool(cfgRead.getNextValueStr()); + break; - default: - break; + case 19: + cfg::driveClientID = cfgRead.getNextValueStr(); + break; + + case 20: + cfg::driveClientSecret = cfgRead.getNextValueStr(); + break; + + case 21: + cfg::driveRefreshToken = cfgRead.getNextValueStr(); + break; + + case 22: + cfg::driveAuthCode = cfgRead.getNextValueStr(); + break; + + default: + break; + } } } } @@ -433,6 +457,15 @@ void cfg::saveConfig() fprintf(cfgOut, "titleSortType = %s\n", sortTypeText().c_str()); fprintf(cfgOut, "animationScale = %f\n", ui::animScale); + if(!cfg::driveClientID.empty()) + fprintf(cfgOut, "driveClientID = %s\n", cfg::driveClientID.c_str()); + + if(!cfg::driveClientSecret.empty()) + fprintf(cfgOut, "driveClientSecret = %s\n", cfg::driveClientSecret.c_str()); + + if(!cfg::driveRefreshToken.empty()) + fprintf(cfgOut, "driveRefreshToken = %s\n", cfg::driveRefreshToken.c_str()); + if(!cfg::favorites.empty()) { fprintf(cfgOut, "\n#favorites\n"); diff --git a/src/curlfuncs.cpp b/src/curlfuncs.cpp index 680df17..8ae0389 100644 --- a/src/curlfuncs.cpp +++ b/src/curlfuncs.cpp @@ -2,21 +2,54 @@ #include #include -size_t writeDataString(const char *buff, size_t sz, size_t cnt, void *u) +#include "curlfuncs.h" +#include "util.h" + +size_t curlFuncs::writeDataString(const char *buff, size_t sz, size_t cnt, void *u) { std::string *str = (std::string *)u; str->append(buff, 0, sz * cnt); return sz * cnt; } -size_t writeDataHead(const char *buff, size_t sz, size_t cnt, void *u) +size_t curlFuncs::writeHeaders(const char *buff, size_t sz, size_t cnt, void *u) { std::vector *headers = (std::vector *)u; headers->push_back(buff); return sz * cnt; } -std::string getJSONURL(std::vector *headers, const std::string& _url) +size_t curlFuncs::readDataFile(char *buff, size_t sz, size_t cnt, void *u) +{ + curlFuncs::curlUpArgs*in = (curlFuncs::curlUpArgs *)u; + + size_t ret = fread(buff, sz, cnt, in->f); + + if(in->o) + *in->o = ftell(in->f); + + return ret; +} + +std::string curlFuncs::getHeader(const std::string& name, std::vector *h) +{ + std::string ret = HEADER_ERROR; + for (unsigned i = 0; i < h->size(); i++) + { + std::string curHeader = h->at(i); + size_t colonPos = curHeader.find_first_of(':'); + if (curHeader.substr(0, colonPos) == name) + { + ret = curHeader.substr(colonPos + 2); + break; + } + } + util::stripChar('\n', ret); + util::stripChar('\r', ret); + return ret; +} + +std::string curlFuncs::getJSONURL(std::vector *headers, const std::string& _url) { std::string ret; CURL *handle = curl_easy_init(); @@ -29,7 +62,7 @@ std::string getJSONURL(std::vector *headers, const std::string& _ur curl_easy_setopt(handle, CURLOPT_TIMEOUT, 15); if(headers) { - curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, writeDataHead); + curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, writeHeaders); curl_easy_setopt(handle, CURLOPT_HEADERDATA, headers); } @@ -49,7 +82,7 @@ size_t writeDataBin(uint8_t *buff, size_t sz, size_t cnt, void *u) return sizeIn; } -bool getBinURL(std::vector *out, const std::string& _url) +bool curlFuncs::getBinURL(std::vector *out, const std::string& _url) { bool ret = false; CURL *handle = curl_easy_init(); diff --git a/src/data.cpp b/src/data.cpp index 072003d..5d7aa4c 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -27,7 +27,6 @@ SetLanguage data::sysLang; //For other save types static bool sysBCATPushed = false, tempPushed = false; std::unordered_map data::titles; -static SDL_Texture *iconMask; //Sorts titles by sortType static struct @@ -355,9 +354,6 @@ void data::sortUserTitles() void data::init() { - if(cfg::config["ovrClk"]) - util::setCPU(util::CPU_SPEED_1224MHz); - data::loadUsersTitles(true); } @@ -366,11 +362,6 @@ void data::exit() for(data::user& u : data::users) u.delIcon(); for(auto& tinfo : titles) SDL_DestroyTexture(tinfo.second.icon); - - SDL_DestroyTexture(iconMask); - - if(cfg::config["ovrClk"]) - util::setCPU(util::CPU_SPEED_1020MHz); } void data::setUserIndex(unsigned _sUser) @@ -510,4 +501,4 @@ void data::dispStats() stats += ui::getUICString("debugStatus", 3) + data::getTitleSafeNameByTID(d->tid) + "\n"; stats += ui::getUICString("debugStatus", 4) + std::to_string(cfg::sortType) + "\n"; gfx::drawTextf(NULL, 16, 2, 2, &green, stats.c_str()); -} +} \ No newline at end of file diff --git a/src/fs.cpp b/src/fs.cpp index 5450358..69552ce 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -431,7 +431,7 @@ void fs::createNewBackup(void *a) path += "/"; fs::copyDirToDirThreaded("sv:/", path); } - ui::populateFldMenu(); + ui::fldRefreshMenu(false); } } @@ -537,7 +537,7 @@ void fs::restoreBackup(void *a) } } if(cfg::config["autoBack"]) - ui::populateFldMenu(); + ui::fldRefreshMenu(false); delete restore; t->finished = true; @@ -572,7 +572,7 @@ void fs::deleteBackup(void *a) fs::delfile(*deletePath); ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupDeleted", 0), backupName.c_str()); } - ui::populateFldMenu(); + ui::fldRefreshMenu(false); delete deletePath; t->finished = true; } diff --git a/src/fs/drive.cpp b/src/fs/drive.cpp new file mode 100644 index 0000000..224ddc1 --- /dev/null +++ b/src/fs/drive.cpp @@ -0,0 +1,41 @@ +#include "drive.h" +#include "cfg.h" +#include "ui.h" + +drive::gd *fs::gDrive = NULL; + +void fs::driveInit() +{ + if(!cfg::driveClientID.empty() && !cfg::driveClientSecret.empty()) + { + fs::gDrive = new drive::gd(cfg::driveClientID, cfg::driveClientSecret, cfg::driveAuthCode, cfg::driveRefreshToken); + if(!fs::gDrive->hasToken()) + { + delete fs::gDrive; + fs::gDrive = NULL; + ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveFailed", 0)); + } + else + { + fs::gDrive->loadDriveList("name = 'JKSV'"); + + if(!fs::gDrive->dirExists("JKSV")) + fs::gDrive->createDir("JKSV"); + + std::string jksvID = fs::gDrive->getFileID("JKSV"); + fs::gDrive->setRootDir(jksvID); + + ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveStarted", 0)); + } + } +} + +void fs::driveExit() +{ + if(fs::gDrive) + { + //Need to save for config if first run + cfg::driveRefreshToken = fs::gDrive->getRefreshToken(); + delete gDrive; + } +} \ No newline at end of file diff --git a/src/fs/file.cpp b/src/fs/file.cpp index 85ce28f..f17e712 100644 --- a/src/fs/file.cpp +++ b/src/fs/file.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include "fs.h" @@ -16,6 +18,69 @@ static std::string wd = "sdmc:/JKSV/"; +typedef struct +{ + std::mutex bufferLock; + std::condition_variable cond; + std::vector sharedBuffer; + std::string dst, dev; + bool bufferIsFull = false; + unsigned int filesize = 0, writeLimit = 0; +} fileCpyThreadArgs; + +static void writeFile_t(void *a) +{ + fileCpyThreadArgs *in = (fileCpyThreadArgs *)a; + size_t written = 0; + std::vector localBuffer; + FILE *out = fopen(in->dst.c_str(), "wb"); + while(written < in->filesize) + { + std::unique_lock buffLock(in->bufferLock); + in->cond.wait(buffLock, [in]{ return in->bufferIsFull;}); + localBuffer.clear(); + localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end()); + in->sharedBuffer.clear(); + in->bufferIsFull = false; + buffLock.unlock(); + in->cond.notify_one(); + + written += fwrite(localBuffer.data(), 1, localBuffer.size(), out); + } + fclose(out); +} + +static void writeFileCommit_t(void *a) +{ + fileCpyThreadArgs *in = (fileCpyThreadArgs *)a; + size_t written = 0, journalCount = 0; + std::vector localBuffer; + FILE *out = fopen(in->dst.c_str(), "wb"); + + while(written < in->filesize) + { + std::unique_lock buffLock(in->bufferLock); + in->cond.wait(buffLock, [in]{ return in->bufferIsFull; }); + localBuffer.clear(); + localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end()); + in->sharedBuffer.clear(); + in->bufferIsFull = false; + buffLock.unlock(); + in->cond.notify_one(); + + written += fwrite(localBuffer.data(), 1, localBuffer.size(), out); + journalCount += written; + if(journalCount >= in->writeLimit) + { + journalCount = 0; + fclose(out); + fs::commitToDevice(in->dev.c_str()); + out = fopen(in->dst.c_str(), "ab"); + } + } + fclose(out); +} + fs::copyArgs *fs::copyArgsCreate(const std::string& src, const std::string& dst, const std::string& dev, zipFile z, unzFile unz, bool _cleanup, bool _trimZipPath, uint8_t _trimPlaces) { copyArgs *ret = new copyArgs; @@ -122,62 +187,53 @@ int fs::dataFile::getNextValueInt() void fs::copyFile(const std::string& src, const std::string& dst, threadInfo *t) { fs::copyArgs *c = NULL; + size_t filesize = fs::fsize(src); if(t) { c = (fs::copyArgs *)t->argPtr; c->offset = 0; - c->prog->setMax(fs::fsize(src)); + c->prog->setMax(filesize); c->prog->update(0); } - if(cfg::config["directFsCmd"]) + FILE *fsrc = fopen(src.c_str(), "rb"); + if(!fsrc) { - FSFILE *fsrc = fsfopen(src.c_str(), FsOpenMode_Read); - FSFILE *fdst = fsfopen(dst.c_str(), FsOpenMode_Write); - - if(!fsrc || !fdst) - { - fsfclose(fsrc); - fsfclose(fdst); - return; - } - - uint8_t *buff = new uint8_t[BUFF_SIZE]; - size_t readIn = 0; - while((readIn = fsfread(buff, 1, BUFF_SIZE, fsrc)) > 0) - { - fsfwrite(buff, 1, readIn, fdst); - if(c) - c->offset += readIn; - } - fsfclose(fsrc); - fsfclose(fdst); - delete[] buff; - } - else - { - FILE *fsrc = fopen(src.c_str(), "rb"); - FILE *fdst = fopen(dst.c_str(), "wb"); - - if(!fsrc || !fdst) - { - fclose(fsrc); - fclose(fdst); - return; - } - - uint8_t *buff = new uint8_t[BUFF_SIZE]; - size_t readIn = 0; - while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0) - { - fwrite(buff, 1, readIn, fdst); - if(c) - c->offset += readIn; - } fclose(fsrc); - fclose(fdst); - delete[] buff; + return; } + + fileCpyThreadArgs thrdArgs; + thrdArgs.dst = dst; + thrdArgs.filesize = filesize; + + uint8_t *buff = new uint8_t[BUFF_SIZE]; + std::vector transferBuffer; + Thread writeThread; + threadCreate(&writeThread, writeFile_t, &thrdArgs, NULL, 0x8000, 0x2B, 2); + threadStart(&writeThread); + size_t readIn = 0; + while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0) + { + transferBuffer.insert(transferBuffer.end(), buff, buff + readIn); + if(c) + c->offset += readIn; + + if(transferBuffer.size() > TRANSFER_BUFFER_LIMIT || readIn < BUFF_SIZE) + { + std::unique_lock buffLock(thrdArgs.bufferLock); + thrdArgs.cond.wait(buffLock, [&thrdArgs]{ return thrdArgs.bufferIsFull == false; }); + thrdArgs.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end()); + transferBuffer.clear(); + thrdArgs.bufferIsFull = true; + buffLock.unlock(); + thrdArgs.cond.notify_one(); + } + } + threadWaitForExit(&writeThread); + threadClose(&writeThread); + fclose(fsrc); + delete[] buff; } static void copyFileThreaded_t(void *a) @@ -202,88 +258,59 @@ void fs::copyFileThreaded(const std::string& src, const std::string& dst) void fs::copyFileCommit(const std::string& src, const std::string& dst, const std::string& dev, threadInfo *t) { fs::copyArgs *c = NULL; + size_t filesize = fs::fsize(src); if(t) { c = (fs::copyArgs *)t->argPtr; c->offset = 0; - c->prog->setMax(fs::fsize(src)); + c->prog->setMax(filesize); c->prog->update(0); } - data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); - uint64_t journ = fs::getJournalSize(utinfo); - if(cfg::config["directFsCmd"]) + FILE *fsrc = fopen(src.c_str(), "rb"); + if(!fsrc) { - FSFILE *fsrc = fsfopen(src.c_str(), FsOpenMode_Read); - FSFILE *fdst = fsfopen(dst.c_str(), FsOpenMode_Write); - - if(!fsrc || !fdst) - { - fsfclose(fsrc); - fsfclose(fdst); - return; - } - - uint8_t *buff = new uint8_t[BUFF_SIZE]; - size_t readIn = 0, writeCount = 0; - while((readIn = fsfread(buff, 1, BUFF_SIZE, fsrc)) > 0) - { - fsfwrite(buff, 1, readIn, fdst); - writeCount += readIn; - if(c) - c->offset += readIn; - - if(writeCount >= (journ - 0x100000)) - { - writeCount = 0; - fsfclose(fdst); - if(!fs::commitToDevice(dev.c_str())) - break; - - fdst = fsfopen(dst.c_str(), FsOpenMode_Write | FsOpenMode_Append); - } - } - fsfclose(fsrc); - fsfclose(fdst); - fs::commitToDevice(dev); - delete[] buff; - } - else - { - FILE *fsrc = fopen(src.c_str(), "rb"); - FILE *fdst = fopen(dst.c_str(), "wb"); - - if(!fsrc || !fdst) - { - fclose(fsrc); - fclose(fdst); - return; - } - - uint8_t *buff = new uint8_t[BUFF_SIZE]; - size_t readIn = 0, writeCount = 0; - while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0) - { - fwrite(buff, 1, readIn, fdst); - writeCount += readIn; - if(c) - c->offset += readIn; - - if(writeCount >= (journ - 0x100000)) - { - writeCount = 0; - fclose(fdst); - if(!fs::commitToDevice(dev)) - break; - - fdst = fopen(dst.c_str(), "ab"); - } - } fclose(fsrc); - fclose(fdst); - fs::commitToDevice(dev); - delete[] buff; + return; } + + fileCpyThreadArgs thrdArgs; + thrdArgs.dst = dst; + thrdArgs.dev = dev; + thrdArgs.filesize = filesize; + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + uint64_t journalSpace = fs::getJournalSize(utinfo); + thrdArgs.writeLimit = (journalSpace - 0x100000) < TRANSFER_BUFFER_LIMIT ? journalSpace - 0x100000 : TRANSFER_BUFFER_LIMIT; + + Thread writeThread; + threadCreate(&writeThread, writeFileCommit_t, &thrdArgs, NULL, 0x8000, 0x2B, 2); + + uint8_t *buff = new uint8_t[BUFF_SIZE]; + size_t readIn = 0; + std::vector transferBuffer; + threadStart(&writeThread); + while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0) + { + transferBuffer.insert(transferBuffer.end(), buff, buff + readIn); + if(c) + c->offset += readIn; + if(transferBuffer.size() >= thrdArgs.writeLimit || readIn < BUFF_SIZE) + { + std::unique_lock buffLock(thrdArgs.bufferLock); + thrdArgs.cond.wait(buffLock, [&thrdArgs]{ return thrdArgs.bufferIsFull == false; }); + thrdArgs.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end()); + transferBuffer.clear(); + thrdArgs.bufferIsFull = true; + buffLock.unlock(); + thrdArgs.cond.notify_one(); + } + } + threadWaitForExit(&writeThread); + threadClose(&writeThread); + + fclose(fsrc); + fs::commitToDevice(dev); + delete[] buff; } static void copyFileCommit_t(void *a) diff --git a/src/fs/zip.cpp b/src/fs/zip.cpp index 2aa9c06..0d00311 100644 --- a/src/fs/zip.cpp +++ b/src/fs/zip.cpp @@ -1,8 +1,57 @@ #include #include +#include +#include +#include #include "fs.h" #include "util.h" +#include "cfg.h" + +typedef struct +{ + std::mutex buffLock; + std::condition_variable cond; + std::vector sharedBuffer; + std::string dst, dev; + bool bufferIsFull = false; + unzFile unz; + unsigned int fileSize, writeLimit = 0; +} unzThrdArgs; + +static void writeFileFromZip_t(void *a) +{ + unzThrdArgs *in = (unzThrdArgs *)a; + std::vector localBuffer; + unsigned int written = 0, journalCount = 0; + + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + uint64_t journalSpace = fs::getJournalSize(utinfo); + + FILE *out = fopen(in->dst.c_str(), "wb"); + while(written < in->fileSize) + { + std::unique_lock buffLock(in->buffLock); + in->cond.wait(buffLock, [in]{ return in->bufferIsFull; }); + localBuffer.clear(); + localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end()); + in->sharedBuffer.clear(); + in->bufferIsFull = false; + buffLock.unlock(); + in->cond.notify_one(); + + written += fwrite(localBuffer.data(), 1, localBuffer.size(), out); + journalCount += written; + if(journalCount >= in->writeLimit) + { + journalCount = 0; + fclose(out); + fs::commitToDevice(in->dev); + out = fopen(in->dst.c_str(), "ab"); + } + } + fclose(out); +} void fs::copyDirToZip(const std::string& src, zipFile dst, bool trimPath, int trimPlaces, threadInfo *t) { @@ -56,8 +105,8 @@ void fs::copyDirToZip(const std::string& src, zipFile dst, bool trimPath, int tr FILE *fsrc = fopen(fullSrc.c_str(), "rb"); size_t readIn = 0; - uint8_t *buff = new uint8_t[BUFF_SIZE]; - while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0) + uint8_t *buff = new uint8_t[ZIP_BUFF_SIZE]; + while((readIn = fread(buff, 1, ZIP_BUFF_SIZE, fsrc)) > 0) { zipWriteInFileInZip(dst, buff, readIn); if(c) @@ -74,7 +123,17 @@ void copyDirToZip_t(void *a) { threadInfo *t = (threadInfo *)a; fs::copyArgs *c = (fs::copyArgs *)t->argPtr; + if(cfg::config["ovrClk"]) + { + util::sysBoost(); + ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popCPUBoostEnabled", 0)); + } + fs::copyDirToZip(c->src, c->z, c->trimZipPath, c->trimZipPlaces, t); + + if(cfg::config["ovrClk"]) + util::sysNormal(); + if(c->cleanup) { zipClose(c->z, NULL); @@ -119,23 +178,36 @@ void fs::copyZipToDir(unzFile src, const std::string& dst, const std::string& de std::string fullDst = dst + filename; fs::mkDirRec(fullDst.substr(0, fullDst.find_last_of('/') + 1)); - FILE *fdst = fopen(fullDst.c_str(), "wb"); + unzThrdArgs unzThrd; + unzThrd.dst = fullDst; + unzThrd.fileSize = info.uncompressed_size; + unzThrd.dev = dev; + unzThrd.writeLimit = (journalSize - 0x100000) < TRANSFER_BUFFER_LIMIT ? (journalSize - 0x100000) : TRANSFER_BUFFER_LIMIT; + + Thread writeThread; + threadCreate(&writeThread, writeFileFromZip_t, &unzThrd, NULL, 0x8000, 0x2B, 2); + threadStart(&writeThread); + + std::vector transferBuffer; while((readIn = unzReadCurrentFile(src, buff, BUFF_SIZE)) > 0) { - c->offset += readIn; - writeCount += readIn; - fwrite(buff, 1, readIn, fdst); - if(writeCount >= journalSize - 0x100000) - { - writeCount = 0; - fclose(fdst); - if(!fs::commitToDevice(dev)) - break; + transferBuffer.insert(transferBuffer.end(), buff, buff + readIn); - fdst = fopen(fullDst.c_str(), "ab"); + if(c) + c->offset += readIn; + + if(transferBuffer.size() >= unzThrd.writeLimit || readIn < BUFF_SIZE) + { + std::unique_lock buffLock(unzThrd.buffLock); + unzThrd.cond.wait(buffLock, [&unzThrd]{ return unzThrd.bufferIsFull == false; }); + unzThrd.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end()); + transferBuffer.clear(); + unzThrd.bufferIsFull = true; + unzThrd.cond.notify_one(); } } - fclose(fdst); + threadWaitForExit(&writeThread); + threadClose(&writeThread); fs::commitToDevice(dev); } } diff --git a/src/gd.cpp b/src/gd.cpp new file mode 100644 index 0000000..c52734e --- /dev/null +++ b/src/gd.cpp @@ -0,0 +1,654 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "gd.h" +#include "fs.h" +#include "curlfuncs.h" +#include "util.h" + +/* +Google Drive code for JKSV. +Still major WIP +*/ + +#define DRIVE_UPLOAD_BUFFER_SIZE 0x8000 +#define DRIVE_DOWNLOAD_BUFFER_SIZE 0xA00000 + +#define tokenURL "https://oauth2.googleapis.com/token" +#define tokenCheckURL "https://oauth2.googleapis.com/tokeninfo" +#define driveURL "https://www.googleapis.com/drive/v3/files" +#define driveUploadURL "https://www.googleapis.com/upload/drive/v3/files" +#define userAgent "JKSV" + +std::vector downloadBuffer; + +typedef struct +{ + curlFuncs::curlDlArgs *cfa; + std::mutex dataLock; + std::condition_variable cond; + std::vector sharedBuffer; + bool bufferFull = false; + unsigned int downloaded = 0; +} dlWriteThreadStruct; + +static inline void writeDriveError(const std::string& _function, const std::string& _message) +{ + fs::logWrite("Drive/%s: %s\n", _function.c_str(), _message.c_str()); +} + +static inline void writeCurlError(const std::string& _function, int _cerror) +{ + fs::logWrite("Drive/%s: CURL returned error %i\n", _function.c_str(), _cerror); +} + +static void writeThread_t(void *a) +{ + dlWriteThreadStruct *in = (dlWriteThreadStruct *)a; + std::vector localBuff; + int written = 0; + + FILE *out = fopen(in->cfa->path.c_str(), "wb"); + + while(written < in->cfa->size) + { + std::unique_lock dataLock(in->dataLock); + in->cond.wait(dataLock, [in]{ return in->bufferFull; }); + localBuff.clear(); + localBuff.assign(in->sharedBuffer.begin(), in->sharedBuffer.end()); + in->sharedBuffer.clear(); + in->bufferFull = false; + dataLock.unlock(); + in->cond.notify_one(); + + written += fwrite(localBuff.data(), 1, localBuff.size(), out); + } + fclose(out); + downloadBuffer.clear(); +} + +static size_t writeDataBufferThreaded(uint8_t *buff, size_t sz, size_t cnt, void *u) +{ + dlWriteThreadStruct *in = (dlWriteThreadStruct *)u; + downloadBuffer.insert(downloadBuffer.end(), buff, buff + (sz * cnt)); + in->downloaded += sz * cnt; + + if(in->downloaded == in->cfa->size || (downloadBuffer.size() >= DRIVE_DOWNLOAD_BUFFER_SIZE)) + { + std::unique_lock dataLock(in->dataLock); + in->cond.wait(dataLock, [in]{ return in->bufferFull == false; }); + in->sharedBuffer.assign(downloadBuffer.begin(), downloadBuffer.end()); + downloadBuffer.clear(); + in->bufferFull = true; + dataLock.unlock(); + in->cond.notify_one(); + } + + if(in->cfa->o) + *in->cfa->o = in->downloaded; + + return sz * cnt; +} + +drive::gd::gd(const std::string &_clientID, const std::string& _secretID, const std::string& _authCode, const std::string& _rToken) +{ + clientID = _clientID; + secretID = _secretID; + rToken = _rToken; + + if(!_authCode.empty()) + exhangeAuthCode(_authCode); + else if(!rToken.empty()) + refreshToken(); + else + writeDriveError("gd::gd", "Missing data needed to init Google Drive.\n"); +} + +void drive::gd::exhangeAuthCode(const std::string& _authCode) +{ + // Header + curl_slist *postHeader = NULL; + postHeader = curl_slist_append(postHeader, HEADER_CONTENT_TYPE_APP_JSON); + + // Post json + std::string *postJson = new std::string; + json_object *post = json_object_new_object(); + json_object *clientIDString = json_object_new_string(clientID.c_str()); + json_object *secretIDString = json_object_new_string(secretID.c_str()); + json_object *authCodeString = json_object_new_string(_authCode.c_str()); + json_object *redirectUriString = json_object_new_string("urn:ietf:wg:oauth:2.0:oob"); + json_object *grantTypeString = json_object_new_string("authorization_code"); + json_object_object_add(post, "client_id", clientIDString); + json_object_object_add(post, "client_secret", secretIDString); + json_object_object_add(post, "code", authCodeString); + json_object_object_add(post, "redirect_uri", redirectUriString); + json_object_object_add(post, "grant_type", grantTypeString); + postJson->assign(json_object_to_json_string_ext(post, JSON_C_TO_STRING_NOSLASHESCAPE)); + + // Curl Request + std::string *jsonResp = new std::string; + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeader); + curl_easy_setopt(curl, CURLOPT_URL, tokenURL); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postJson->c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postJson->length()); + + int error = curl_easy_perform(curl); + + json_object *respParse = json_tokener_parse(jsonResp->c_str()); + if (error == CURLE_OK) + { + json_object *accessToken = json_object_object_get(respParse, "access_token"); + json_object *refreshToken = json_object_object_get(respParse, "refresh_token"); + + if(accessToken && refreshToken) + { + token = json_object_get_string(accessToken); + rToken = json_object_get_string(refreshToken); + } + else + writeDriveError("exchangeAuthCode", "Error exchanging code for token."); + } + else + writeCurlError("exchangeAuthCode", error); + + delete postJson; + delete jsonResp; + json_object_put(post); + json_object_put(respParse); + curl_slist_free_all(postHeader); + curl_easy_cleanup(curl); +} + +void drive::gd::refreshToken() +{ + // Header + curl_slist *header = NULL; + header = curl_slist_append(header, HEADER_CONTENT_TYPE_APP_JSON); + + // Post Json + std::string *jsonPost = new std::string; + json_object *post = json_object_new_object(); + json_object *clientIDString = json_object_new_string(clientID.c_str()); + json_object *secretIDString = json_object_new_string(secretID.c_str()); + json_object *refreshTokenString = json_object_new_string(rToken.c_str()); + json_object *grantTypeString = json_object_new_string("refresh_token"); + json_object_object_add(post, "client_id", clientIDString); + json_object_object_add(post, "client_secret", secretIDString); + json_object_object_add(post, "refresh_token", refreshTokenString); + json_object_object_add(post, "grant_type", grantTypeString); + jsonPost->assign(json_object_to_json_string(post)); + + // Curl + std::string *jsonResp = new std::string; + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header); + curl_easy_setopt(curl, CURLOPT_URL, tokenURL); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonPost->c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, jsonPost->length()); + int error = curl_easy_perform(curl); + + json_object *parse = json_tokener_parse(jsonResp->c_str()); + if (error == CURLE_OK) + { + json_object *accessToken; + json_object_object_get_ex(parse, "access_token", &accessToken); + if(accessToken) + token = json_object_get_string(accessToken); + else + writeDriveError("refreshToken", "Error refreshing token."); + } + + delete jsonPost; + delete jsonResp; + json_object_put(post); + json_object_put(parse); + curl_slist_free_all(header); + curl_easy_cleanup(curl); +} + +bool drive::gd::tokenIsValid() +{ + bool ret = false; + + std::string url = tokenCheckURL; + url.append("?access_token=" + token); + + CURL *curl = curl_easy_init(); + std::string *jsonResp = new std::string; + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp); + + int error = curl_easy_perform(curl); + json_object *parse = json_tokener_parse(jsonResp->c_str()); + if (error == CURLE_OK) + { + json_object *checkError; + json_object_object_get_ex(parse, "error", &checkError); + if(!checkError) + ret = true; + } + + delete jsonResp; + json_object_put(parse); + curl_easy_cleanup(curl); + return ret; +} + +void drive::gd::loadDriveList(const std::string& _qParams) +{ + if(!tokenIsValid()) + refreshToken(); + + // Request url with specific fields needed. + std::string url = driveURL, queryParams = "trashed=false and 'me' in owners "; + //These are always used + url.append("?fields=files(name,id,mimeType,size,parents)&q="); + if(!parentDir.empty()) + queryParams.append("and '" + parentDir + "' in parents "); + if(!_qParams.empty()) + queryParams.append("and " + _qParams); + + // Headers needed + curl_slist *postHeaders = NULL; + postHeaders = curl_slist_append(postHeaders, std::string(HEADER_AUTHORIZATION + token).c_str()); + + // Curl request + std::string *jsonResp = new std::string; + CURL *curl = curl_easy_init(); + + //I don't understand why this needs a CURL handle... + char *urlAppend = curl_easy_escape(curl, queryParams.c_str(), queryParams.length()); + url.append(urlAppend); + curl_free(urlAppend); + + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeaders); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp); + + int error = curl_easy_perform(curl); + + json_object *parse = json_tokener_parse(jsonResp->c_str()); + if (error == CURLE_OK) + { + driveList.clear(); + json_object *fileArray; + json_object_object_get_ex(parse, "files", &fileArray); + if(fileArray) + { + size_t count = json_object_array_length(fileArray); + driveList.reserve(count); + for (unsigned i = 0; i < count; i++) + { + json_object *idString, *nameString, *mimeTypeString, *size, *parentArray; + json_object *curFile = json_object_array_get_idx(fileArray, i); + json_object_object_get_ex(curFile, "id", &idString); + json_object_object_get_ex(curFile, "name", &nameString); + json_object_object_get_ex(curFile, "mimeType", &mimeTypeString); + json_object_object_get_ex(curFile, "size", &size); + json_object_object_get_ex(curFile, "parents", &parentArray); + + drive::gdDirItem newDirItem; + newDirItem.name = json_object_get_string(nameString); + newDirItem.id = json_object_get_string(idString); + newDirItem.mimeType = json_object_get_string(mimeTypeString); + newDirItem.size = json_object_get_int(size); + + if (parentArray) + { + size_t parentCount = json_object_array_length(parentArray); + for (unsigned j = 0; j < parentCount; j++) + { + json_object *parent = json_object_array_get_idx(parentArray, j); + newDirItem.parents.push_back(json_object_get_string(parent)); + } + } + driveList.push_back(newDirItem); + } + } + else + writeDriveError("updateList", "Error obtaining drive listing."); + } + else + writeCurlError("updateList", error); + + delete jsonResp; + json_object_put(parse); + curl_slist_free_all(postHeaders); + curl_easy_cleanup(curl); +} + +void drive::gd::debugWriteList() +{ + fs::logWrite("Parent: %s\n", parentDir.c_str()); + for(auto& di : driveList) + { + fs::logWrite("%s\n\t%s\n", di.name.c_str(), di.id.c_str()); + if(!di.parents.empty()) + fs::logWrite("\t%s\n", di.parents[0].c_str()); + } +} + +bool drive::gd::createDir(const std::string& _dirName) +{ + if(!tokenIsValid()) + refreshToken(); + + bool ret = true; + + // Headers to use + curl_slist *postHeaders = NULL; + postHeaders = curl_slist_append(postHeaders, std::string(HEADER_AUTHORIZATION + token).c_str()); + postHeaders = curl_slist_append(postHeaders, HEADER_CONTENT_TYPE_APP_JSON); + + // JSON To Post + std::string *jsonPost = new std::string; + json_object *post = json_object_new_object(); + json_object *nameString = json_object_new_string(_dirName.c_str()); + json_object *mimeTypeString = json_object_new_string(MIMETYPE_FOLDER); + json_object_object_add(post, "name", nameString); + json_object_object_add(post, "mimeType", mimeTypeString); + if (!parentDir.empty()) + { + json_object *parentsArray = json_object_new_array(); + json_object *parentString = json_object_new_string(parentDir.c_str()); + json_object_array_add(parentsArray, parentString); + json_object_object_add(post, "parents", parentsArray); + } + jsonPost->assign(json_object_to_json_string_ext(post, JSON_C_TO_STRING_NOSLASHESCAPE)); + + // Curl Request + std::string *jsonResp = new std::string; + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeaders); + curl_easy_setopt(curl, CURLOPT_URL, driveURL); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonPost->c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, jsonPost->length()); + int error = curl_easy_perform(curl); + + json_object *respParse = json_tokener_parse(jsonResp->c_str()), *checkError; + json_object_object_get_ex(respParse, "error", &checkError); + if (error == CURLE_OK && !checkError) + { + //Append it to list + json_object *id; + json_object_object_get_ex(respParse, "id", &id); + + drive::gdDirItem newDir; + newDir.name = _dirName; + newDir.id = json_object_get_string(id); + newDir.mimeType = MIMETYPE_FOLDER; + newDir.size = 0; + driveList.push_back(newDir); + } + else + ret = false; + + delete jsonPost; + delete jsonResp; + json_object_put(post); + json_object_put(respParse); + curl_slist_free_all(postHeaders); + curl_easy_cleanup(curl); + return ret; +} + +bool drive::gd::dirExists(const std::string& _dirName) +{ + bool ret = false; + + for(unsigned i = 0; i < driveList.size(); i++) + { + if(driveList[i].name == _dirName && driveList[i].mimeType == MIMETYPE_FOLDER) + { + ret = true; + break; + } + } + + return ret; +} + +bool drive::gd::fileExists(const std::string& _filename) +{ + bool ret = false; + + for(unsigned i = 0; i < driveList.size(); i++) + { + if(driveList[i].name == _filename && driveList[i].mimeType != MIMETYPE_FOLDER) + { + ret = true; + break; + } + } + + return ret; +} + +void drive::gd::uploadFile(const std::string& _filename, curlFuncs::curlUpArgs *_upload) +{ + if(!tokenIsValid()) + refreshToken(); + + std::string url = driveUploadURL; + url.append("?uploadType=resumable"); + + // Headers + curl_slist *postHeaders = NULL; + postHeaders = curl_slist_append(postHeaders, std::string(HEADER_AUTHORIZATION + token).c_str()); + postHeaders = curl_slist_append(postHeaders, HEADER_CONTENT_TYPE_APP_JSON); + + // Post JSON + std::string *jsonPost = new std::string; + json_object *post = json_object_new_object(); + json_object *nameString = json_object_new_string(_filename.c_str()); + json_object_object_add(post, "name", nameString); + if (!parentDir.empty()) + { + json_object *parentArray = json_object_new_array(); + json_object *parentString = json_object_new_string(parentDir.c_str()); + json_object_array_add(parentArray, parentString); + json_object_object_add(post, "parents", parentArray); + } + jsonPost->assign(json_object_to_json_string_ext(post, JSON_C_TO_STRING_NOSLASHESCAPE)); + + // Curl upload request + std::string *jsonResp = new std::string; + std::vector *headers = new std::vector; + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeaders); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlFuncs::writeHeaders); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, headers); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonPost->c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, jsonPost->length()); + + int error = curl_easy_perform(curl); + std::string location = curlFuncs::getHeader("Location", headers); + if (error == CURLE_OK && location != HEADER_ERROR) + { + CURL *curlUp = curl_easy_init(); + curl_easy_setopt(curlUp, CURLOPT_PUT, 1); + curl_easy_setopt(curlUp, CURLOPT_URL, location.c_str()); + curl_easy_setopt(curlUp, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString); + curl_easy_setopt(curlUp, CURLOPT_WRITEDATA, jsonResp); + curl_easy_setopt(curlUp, CURLOPT_READFUNCTION, curlFuncs::readDataFile); + curl_easy_setopt(curlUp, CURLOPT_READDATA, _upload); + curl_easy_setopt(curlUp, CURLOPT_UPLOAD_BUFFERSIZE, DRIVE_UPLOAD_BUFFER_SIZE); + curl_easy_setopt(curlUp, CURLOPT_UPLOAD, 1); + curl_easy_perform(curlUp); + curl_easy_cleanup(curlUp); + + json_object *parse = json_tokener_parse(jsonResp->c_str()), *id, *name, *mimeType; + json_object_object_get_ex(parse, "id", &id); + json_object_object_get_ex(parse, "name", &name); + json_object_object_get_ex(parse, "mimeType", &mimeType); + + if(name && id && mimeType) + { + drive::gdDirItem uploadData; + uploadData.id = json_object_get_string(id); + uploadData.name = json_object_get_string(name); + uploadData.mimeType = json_object_get_string(mimeType); + uploadData.size = *_upload->o;//should be safe to use + uploadData.parents.push_back(parentDir); + driveList.push_back(uploadData); + } + } + else + writeCurlError("uploadFile", error); + + delete jsonPost; + delete jsonResp; + delete headers; + json_object_put(post); + curl_slist_free_all(postHeaders); +} + +void drive::gd::updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_upload) +{ + if(!tokenIsValid()) + refreshToken(); + + //URL + std::string url = driveUploadURL; + url.append("/" + _fileID); + url.append("?uploadType=resumable"); + + //Header + curl_slist *patchHeader = NULL; + patchHeader = curl_slist_append(patchHeader, std::string(HEADER_AUTHORIZATION + token).c_str()); + + //Curl + std::string *jsonResp = new std::string; + std::vector *headers = new std::vector; + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, patchHeader); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlFuncs::writeHeaders); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, headers); + + int error = curl_easy_perform(curl); + std::string location = curlFuncs::getHeader("Location", headers); + if(error == CURLE_OK && location != HEADER_ERROR) + { + CURL *curlPatch = curl_easy_init(); + curl_easy_setopt(curlPatch, CURLOPT_PUT, 1); + curl_easy_setopt(curlPatch, CURLOPT_URL, location.c_str()); + curl_easy_setopt(curlPatch, CURLOPT_READFUNCTION, curlFuncs::readDataFile); + curl_easy_setopt(curlPatch, CURLOPT_READDATA, _upload); + curl_easy_setopt(curlPatch, CURLOPT_UPLOAD_BUFFERSIZE, DRIVE_UPLOAD_BUFFER_SIZE); + curl_easy_setopt(curlPatch, CURLOPT_UPLOAD, 1); + curl_easy_perform(curlPatch); + curl_easy_cleanup(curlPatch); + } + + delete jsonResp; + delete headers; + curl_slist_free_all(patchHeader); + curl_easy_cleanup(curl); +} + +void drive::gd::downloadFile(const std::string& _fileID, curlFuncs::curlDlArgs *_download) +{ + if(!tokenIsValid()) + refreshToken(); + + //URL + std::string url = driveURL; + url.append("/" + _fileID); + url.append("?alt=media"); + + //Headers + curl_slist *getHeaders = NULL; + getHeaders = curl_slist_append(getHeaders, std::string(HEADER_AUTHORIZATION + token).c_str()); + + //Downloading is threaded because it's too slow otherwise + dlWriteThreadStruct dlWrite; + dlWrite.cfa = _download; + + Thread writeThread; + threadCreate(&writeThread, writeThread_t, &dlWrite, NULL, 0x8000, 0x2B, 2); + + //Curl + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, getHeaders); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeDataBufferThreaded); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &dlWrite); + threadStart(&writeThread); + int error = curl_easy_perform(curl); + + threadWaitForExit(&writeThread); + threadClose(&writeThread); + + curl_slist_free_all(getHeaders); + curl_easy_cleanup(curl); +} + +void drive::gd::deleteFile(const std::string& _fileID) +{ + if(!tokenIsValid()) + refreshToken(); + + //URL + std::string url = driveURL; + url.append("/" + _fileID); + + //Header + curl_slist *delHeaders = NULL; + delHeaders = curl_slist_append(delHeaders, std::string(HEADER_AUTHORIZATION + token).c_str()); + + //Curl + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, delHeaders); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + int error = curl_easy_perform(curl); + + curl_slist_free_all(delHeaders); + curl_easy_cleanup(curl); +} + +std::string drive::gd::getFileID(const std::string& _name) +{ + for(unsigned i = 0; i < driveList.size(); i++) + { + if(driveList[i].name == _name) + return driveList[i].id; + } + return ""; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d90c780..8044b8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include +#include #include "gfx.h" #include "file.h" @@ -49,8 +50,14 @@ int main(int argc, const char *argv[]) ui::init(); romfsExit(); + curl_global_init(CURL_GLOBAL_ALL); + //Drive needs config read + fs::driveInit(); + while(ui::runApp()){ } + fs::driveExit(); + curl_global_cleanup(); cfg::saveConfig(); ui::exit(); data::exit(); diff --git a/src/ui.cpp b/src/ui.cpp index 7761dfe..281e067 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -149,6 +149,7 @@ void ui::init() ui::settInit(); ui::extInit(); ui::fmInit(); + ui::fldInit(); popMessages = new ui::popMessageMngr; threadMngr = new ui::threadProcMngr; @@ -165,6 +166,7 @@ void ui::exit() ui::settExit(); ui::extExit(); ui::fmExit(); + ui::fldExit(); delete popMessages; delete threadMngr; @@ -312,7 +314,7 @@ void ui::toTTL(void *a) if(u->titleInfo.size() > 0) { ui::changeState(TTL_SEL); - ui::ttlSetActive(curUserIndex); + ui::ttlSetActive(curUserIndex, true, true); ui::usrMenu->setActive(false); } else diff --git a/src/ui/ext.cpp b/src/ui/ext.cpp index b899e76..3d7d445 100644 --- a/src/ui/ext.cpp +++ b/src/ui/ext.cpp @@ -142,7 +142,7 @@ static void extMenuPackJKSVZip_t(void *a) t->status->setStatus(ui::getUICString("threadStatusPackingJKSV", 0)); if(cfg::config["ovrClk"]) { - util::setCPU(util::CPU_SPEED_1785MHz); + util::sysBoost(); ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popCPUBoostEnabled", 0)); } @@ -164,7 +164,7 @@ static void extMenuPackJKSVZip_t(void *a) zipClose(zip, NULL); } if(cfg::config["ovrClk"]) - util::setCPU(util::CPU_SPEED_1224MHz); + util::sysNormal(); delete jksv; delete c; diff --git a/src/ui/fld.cpp b/src/ui/fld.cpp new file mode 100644 index 0000000..099d1d0 --- /dev/null +++ b/src/ui/fld.cpp @@ -0,0 +1,374 @@ +#include + +#include "ui.h" +#include "fs.h" +#include "util.h" +#include "cfg.h" + +static ui::menu *fldMenu = NULL; +ui::slideOutPanel *ui::fldPanel = NULL; +static fs::dirList *fldList = NULL; +static SDL_Texture *fldBuffer; +static unsigned int fldGuideWidth = 0; +static Mutex fldLock = 0; + +static void fldMenuCallback(void *a) +{ + switch(ui::padKeysDown()) + { + case HidNpadButton_B: + if(fs::gDrive) + fs::gDrive->returnToRoot(); + + fs::unmountSave(); + fs::freePathFilters(); + fldMenu->setActive(false); + ui::fldPanel->closePanel(); + unsigned cusr = data::getCurrentUserIndex(); + ui::ttlSetActive(cusr, true, true); + ui::updateInput(); + break; + } +} + +static void fldPanelDraw(void *a) +{ + mutexLock(&fldLock); + SDL_Texture *target = (SDL_Texture *)a; + gfx::clearTarget(fldBuffer, &ui::slidePanelColor); + fldMenu->draw(fldBuffer, &ui::txtCont, true); + gfx::texDraw(target, fldBuffer, 0, 0); + gfx::drawLine(target, &ui::divClr, 10, 648, fldGuideWidth + 54, 648); + gfx::drawTextf(target, 18, 32, 673, &ui::txtCont, ui::getUICString("helpFolder", 0)); + mutexUnlock(&fldLock); +} + +static void fldFuncCancel(void *a) +{ + std::string *del = (std::string *)a; + delete del; +} + +static void fldFuncOverwrite(void *a) +{ + fs::dirItem *in = (fs::dirItem *)a; + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + std::string *send = new std::string; + send->assign(util::generatePathByTID(utinfo->tid) + in->getItm()); + + ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdOver"], fs::overwriteBackup, fldFuncCancel, send, ui::getUICString("confirmOverwrite", 0), in->getItm().c_str()); + ui::confirm(conf); +} + +static void fldFuncDelete(void *a) +{ + fs::dirItem *in = (fs::dirItem *)a; + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + std::string *send = new std::string; + send->assign(util::generatePathByTID(utinfo->tid) + in->getItm()); + + ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"], fs::deleteBackup, fldFuncCancel, send, ui::getUICString("confirmDelete", 0), in->getItm().c_str()); + ui::confirm(conf); +} + +static void fldFuncRestore(void *a) +{ + fs::dirItem *in = (fs::dirItem *)a; + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + std::string *send = new std::string; + send->assign(util::generatePathByTID(utinfo->tid) + in->getItm()); + + ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdRest"], fs::restoreBackup, fldFuncCancel, send, ui::getUICString("confirmRestore", 0), in->getItm().c_str()); + ui::confirm(conf); +} + +static void fldFuncUpload_t(void *a) +{ + threadInfo *t = (threadInfo *)a; + fs::dirItem *di = (fs::dirItem *)t->argPtr; + fsSetPriority(FsPriority_Realtime); + + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid); + std::string path, tmpZip, filename;//Final path to upload from + + if(cfg::config["ovrClk"]) + util::sysBoost(); + + //Zip first then upload if folder based backup + if(di->isDir()) + { + t->status->setStatus(ui::getUICString("threadStatusCompressingSaveForUpload", 0), di->getItm().c_str()); + filename = di->getItm() + ".zip"; + tmpZip = util::generatePathByTID(utinfo->tid) + di->getItm() + ".zip"; + std::string fldPath = util::generatePathByTID(utinfo->tid) + di->getItm() + "/"; + + int zipTrim = util::getTotalPlacesInPath(fs::getWorkDir()) + 2;//Trim path down to save root + zipFile tmp = zipOpen64(tmpZip.c_str(), 0); + fs::copyDirToZip(fldPath, tmp, true, zipTrim, NULL); + zipClose(tmp, NULL); + path = tmpZip; + } + else + { + filename = di->getItm(); + path = util::generatePathByTID(utinfo->tid) + di->getItm(); + } + + //Change thread stuff so upload status can be shown + t->status->setStatus(ui::getUICString("threadStatusUploadingFile", 0), di->getItm().c_str()); + fs::copyArgs *cpyArgs = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0); + cpyArgs->prog->setMax(fs::fsize(path)); + cpyArgs->prog->update(0); + t->argPtr = cpyArgs; + t->drawFunc = fs::fileDrawFunc; + + //curlDlArgs + curlFuncs::curlUpArgs upload; + upload.f = fopen(path.c_str(), "rb"); + upload.o = &cpyArgs->offset; + + if(fs::gDrive->fileExists(filename)) + { + std::string id = fs::gDrive->getFileID(filename); + fs::gDrive->updateFile(id, &upload); + } + else + fs::gDrive->uploadFile(filename, &upload); + + fclose(upload.f); + + if(!tmpZip.empty()) + fs::delfile(tmpZip); + + fs::copyArgsDestroy(cpyArgs); + t->drawFunc = NULL; + + if(cfg::config["ovrClk"]) + util::sysNormal(); + + ui::fldRefreshMenu(false); + + t->finished = true; +} + +static void fldFuncUpload(void *a) +{ + if(fs::gDrive) + ui::newThread(fldFuncUpload_t, a, NULL); + else + ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveNotActive", 0)); +} + +static void fldFuncDownload_t(void *a) +{ + threadInfo *t = (threadInfo *)a; + drive::gdDirItem *in = (drive::gdDirItem *)t->argPtr; + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid); + std::string targetPath = util::generatePathByTID(utinfo->tid) + in->name; + t->status->setStatus(ui::getUICString("threadStatusDownloadingFile", 0), in->name.c_str()); + + if(cfg::config["ovrClk"]) + util::sysBoost(); + + if(fs::fileExists(targetPath)) + fs::delfile(targetPath); + + //Use this for progress bar + fs::copyArgs *cpy = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0); + cpy->prog->setMax(in->size); + cpy->prog->update(0); + t->argPtr = cpy; + t->drawFunc = fs::fileDrawFunc; + + //DL struct + curlFuncs::curlDlArgs dlFile; + dlFile.path = targetPath; + dlFile.size = in->size; + dlFile.o = &cpy->offset; + + fs::gDrive->downloadFile(in->id, &dlFile); + + //fclose(dlFile.f); + + fs::copyArgsDestroy(cpy); + t->drawFunc = NULL; + + if(cfg::config["ovrClk"]) + util::sysNormal(); + + ui::fldRefreshMenu(false); + + t->finished = true; +} + +static void fldFuncDownload(void *a) +{ + drive::gdDirItem *in = (drive::gdDirItem *)a; + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid); + std::string testPath = util::generatePathByTID(utinfo->tid) + in->name; + if(fs::fileExists(testPath)) + { + ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdOver"], fldFuncDownload_t, NULL, a, ui::getUICString("confirmDriveOverwrite", 0)); + ui::confirm(conf); + } + else + ui::newThread(fldFuncDownload_t, a, NULL); + +} + +static void fldFuncDriveDelete_t(void *a) +{ + threadInfo *t = (threadInfo *)a; + drive::gdDirItem *gdi = (drive::gdDirItem *)t->argPtr; + t->status->setStatus(ui::getUICString("threadStatusDeletingFile", 0)); + fs::gDrive->deleteFile(gdi->id); + ui::fldRefreshMenu(true); + t->finished = true; +} + +static void fldFuncDriveDelete(void *a) +{ + drive::gdDirItem *in = (drive::gdDirItem *)a; + ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"], fldFuncDriveDelete_t, NULL, a, ui::getUICString("confirmDelete", 0), in->name.c_str()); + ui::confirm(conf); +} + +//TODO +static void fldFuncDriveRestore_t(void *a) +{ + +} + +static void fldFuncDriveRestore(void *a) +{ + +} + +void ui::fldInit() +{ + fldGuideWidth = gfx::getTextWidth(ui::getUICString("helpFolder", 0), 18); + + fldMenu = new ui::menu(10, 8, fldGuideWidth + 44, 20, 6); + fldMenu->setCallback(fldMenuCallback, NULL); + fldMenu->setActive(false); + + ui::fldPanel = new ui::slideOutPanel(fldGuideWidth + 64, 720, 0, ui::SLD_RIGHT, fldPanelDraw); + fldBuffer = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, fldGuideWidth + 64, 647); + ui::registerPanel(fldPanel); + + fldList = new fs::dirList; +} + +void ui::fldExit() +{ + delete ui::fldPanel; + delete fldMenu; + delete fldList; + SDL_DestroyTexture(fldBuffer); +} + +void ui::fldUpdate() +{ + fldMenu->update(); +} + +void ui::fldPopulateMenu() +{ + mutexLock(&fldLock); + + fldMenu->reset(); + + data::userTitleInfo *d = data::getCurrentUserTitleInfo(); + data::titleInfo *t = data::getTitleInfoByTID(d->tid); + util::createTitleDirectoryByTID(d->tid); + std::string targetDir = util::generatePathByTID(d->tid); + + fldList->reassign(targetDir); + if(fs::gDrive) + { + if(!fs::gDrive->dirExists(t->title)) + fs::gDrive->createDir(t->title); + + std::string dirID = fs::gDrive->getFileID(t->title); + fs::gDrive->chDir(dirID); + } + + fs::loadPathFilters(d->tid); + + fldMenu->addOpt(NULL, ui::getUICString("folderMenuNew", 0)); + fldMenu->optAddButtonEvent(0, HidNpadButton_A, fs::createNewBackup, NULL); + + unsigned fldInd = 1; + if(fs::gDrive && fs::gDrive->getDriveListCount() > 0) + { + for(unsigned i = 0; i < fs::gDrive->getDriveListCount(); i++, fldInd++) + { + drive::gdDirItem *gdi = fs::gDrive->getDirItemAt(i); + fldMenu->addOpt(NULL, "[GD] " + gdi->name); + + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncDownload, gdi); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDriveDelete, gdi); + } + } + + for(unsigned i = 0; i < fldList->getCount(); i++, fldInd++) + { + fs::dirItem *di = fldList->getDirItemAt(i); + fldMenu->addOpt(NULL, di->getItm()); + + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncOverwrite, di); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDelete, di); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_Y, fldFuncRestore, di); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_ZR, fldFuncUpload, di); + } + fldMenu->setActive(true); + ui::fldPanel->openPanel(); + + mutexUnlock(&fldLock); +} + +void ui::fldRefreshMenu(bool _updateDrive) +{ + mutexLock(&fldLock); + + fldMenu->reset(); + data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); + data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid); + std::string targetDir = util::generatePathByTID(utinfo->tid); + + fldList->reassign(targetDir); + if(_updateDrive && fs::gDrive) + fs::gDrive->loadDriveList(""); + + fldMenu->addOpt(NULL, ui::getUIString("folderMenuNew", 0)); + fldMenu->optAddButtonEvent(0, HidNpadButton_A, fs::createNewBackup, NULL); + + unsigned fldInd = 1; + if(fs::gDrive && fs::gDrive->getDriveListCount() > 0) + { + for(unsigned i = 0; i < fs::gDrive->getDriveListCount(); i++, fldInd++) + { + drive::gdDirItem *gdi = fs::gDrive->getDirItemAt(i); + fldMenu->addOpt(NULL, "[GD] " + gdi->name); + + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncDownload, gdi); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDriveDelete, gdi); + } + } + + for(unsigned i = 0; i < fldList->getCount(); i++, fldInd++) + { + fs::dirItem *di = fldList->getDirItemAt(i); + fldMenu->addOpt(NULL, di->getItm()); + + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncOverwrite, di); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDelete, di); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_Y, fldFuncRestore, di); + fldMenu->optAddButtonEvent(fldInd, HidNpadButton_ZR, fldFuncUpload, di); + } + + mutexUnlock(&fldLock); +} \ No newline at end of file diff --git a/src/ui/ttl.cpp b/src/ui/ttl.cpp index 943136c..a4eb598 100644 --- a/src/ui/ttl.cpp +++ b/src/ui/ttl.cpp @@ -6,14 +6,11 @@ #include "util.h" #include "cfg.h" -static int ttlHelpX = 0, fldHelpWidth = 0; +static int ttlHelpX = 0; static std::vector ttlViews; -static ui::menu *ttlOpts, *fldMenu; +static ui::menu *ttlOpts; ui::slideOutPanel *ui::ttlOptsPanel; -static ui::slideOutPanel *infoPanel, *fldPanel;//There's no reason to have a separate folder section -static fs::dirList *fldList; -static std::string infoPanelString; -static SDL_Texture *fldBuffer;//This is so folder menu doesn't draw over guide +static ui::slideOutPanel *infoPanel; static Mutex ttlViewLock = 0; void ui::ttlRefresh() @@ -24,77 +21,6 @@ void ui::ttlRefresh() mutexUnlock(&ttlViewLock); } -static void fldFuncCancel(void *a) -{ - std::string *del = (std::string *)a; - delete del; -} - -static void fldFuncOverwrite(void *a) -{ - data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); - int sel = fldMenu->getSelected() - 1;//Skip 'New' - std::string itm = fldList->getItem(sel); - std::string *send = new std::string; - send->assign(util::generatePathByTID(utinfo->tid) + itm); - - ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdOver"], fs::overwriteBackup, fldFuncCancel, send, ui::getUICString("confirmOverwrite", 0), itm.c_str()); - ui::confirm(conf); -} - -static void fldFuncDelete(void *a) -{ - data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); - int sel = fldMenu->getSelected() - 1;//Skip 'New' - std::string itm = fldList->getItem(sel); - std::string *send = new std::string; - send->assign(util::generatePathByTID(utinfo->tid) + itm); - - ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"], fs::deleteBackup, fldFuncCancel, send, ui::getUICString("confirmDelete", 0), itm.c_str()); - ui::confirm(conf); -} - -static void fldFuncRestore(void *a) -{ - data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo(); - int sel = fldMenu->getSelected() - 1;//Skip 'New' - std::string itm = fldList->getItem(sel); - std::string *send = new std::string; - send->assign(util::generatePathByTID(utinfo->tid) + itm); - - ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdRest"], fs::restoreBackup, fldFuncCancel, send, ui::getUICString("confirmRestore", 0), itm.c_str()); - ui::confirm(conf); -} - -void ui::populateFldMenu() -{ - fldMenu->reset(); - - data::userTitleInfo *d = data::getCurrentUserTitleInfo(); - util::createTitleDirectoryByTID(d->tid); - std::string targetDir = util::generatePathByTID(d->tid); - - fldList->reassign(targetDir); - - char filterPath[128]; - sprintf(filterPath, "sdmc:/config/JKSV/0x%016lX_filter.txt", d->tid); - fs::loadPathFilters(d->tid); - - fldMenu->addOpt(NULL, ui::getUICString("folderMenuNew", 0)); - fldMenu->optAddButtonEvent(0, HidNpadButton_A, fs::createNewBackup, NULL); - - for(unsigned i = 0; i < fldList->getCount(); i++) - { - fldMenu->addOpt(NULL, fldList->getItem(i)); - - fldMenu->optAddButtonEvent(i + 1, HidNpadButton_A, fldFuncOverwrite, NULL); - fldMenu->optAddButtonEvent(i + 1, HidNpadButton_X, fldFuncDelete, NULL); - fldMenu->optAddButtonEvent(i + 1, HidNpadButton_Y, fldFuncRestore, NULL); - } - fldMenu->setActive(true); - fldPanel->openPanel(); -} - static void ttlViewCallback(void *a) { unsigned curUserIndex = data::getCurrentUserIndex(); @@ -107,7 +33,10 @@ static void ttlViewCallback(void *a) { case HidNpadButton_A: if(fs::mountSave(d->saveInfo)) - ui::populateFldMenu(); + { + ttlViews[curUserIndex]->setActive(false, true); + ui::fldPopulateMenu(); + } break; case HidNpadButton_B: @@ -332,6 +261,7 @@ static void ttlOptsExportSVI(void *a) fwrite(ctrlData->icon, 1, jpegSize, svi); fclose(svi); delete ctrlData; + ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popSVIExported", 0)); } } @@ -403,34 +333,9 @@ static void infoPanelCallback(void *a) } } -static void fldMenuCallback(void *a) -{ - switch(ui::padKeysDown()) - { - case HidNpadButton_B: - fs::unmountSave(); - fs::freePathFilters(); - fldMenu->setActive(false); - fldPanel->closePanel(); - break; - } - ui::updateInput(); -} - -static void fldPanelDraw(void *a) -{ - SDL_Texture *target = (SDL_Texture *)a; - gfx::clearTarget(fldBuffer, &ui::slidePanelColor); - fldMenu->draw(fldBuffer, &ui::txtCont, true); - gfx::texDraw(target, fldBuffer, 0, 0); - gfx::drawLine(target, &ui::divClr, 10, 648, fldHelpWidth + 54, 648); - gfx::drawTextf(target, 18, 32, 673, &ui::txtCont, ui::getUICString("helpFolder", 0)); -} - void ui::ttlInit() { ttlHelpX = 1220 - gfx::getTextWidth(ui::getUICString("helpTitle", 0), 18); - fldHelpWidth = gfx::getTextWidth(ui::getUICString("helpFolder", 0), 18); for(data::user& u : data::users) ttlViews.emplace_back(new ui::titleview(u, 128, 128, 16, 16, 7, ttlViewCallback)); @@ -439,10 +344,6 @@ void ui::ttlInit() ttlOpts->setCallback(ttlOptsCallback, NULL); ttlOpts->setActive(false); - fldMenu = new ui::menu(10, 8, fldHelpWidth + 44, 20, 6); - fldMenu->setCallback(fldMenuCallback, NULL); - fldMenu->setActive(false); - ttlOptsPanel = new ui::slideOutPanel(410, 720, 0, ui::SLD_RIGHT, ttlOptsPanelDraw); ui::registerPanel(ttlOptsPanel); @@ -450,12 +351,6 @@ void ui::ttlInit() ui::registerPanel(infoPanel); infoPanel->setCallback(infoPanelCallback, NULL); - fldPanel = new ui::slideOutPanel(fldHelpWidth + 64, 720, 0, ui::SLD_RIGHT, fldPanelDraw); - fldBuffer = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, fldHelpWidth + 64, 647); - ui::registerPanel(fldPanel); - - fldList = new fs::dirList; - ttlOpts->setActive(false); for(int i = 0; i < 9; i++) ttlOpts->addOpt(NULL, ui::getUIString("titleOptions", i)); @@ -484,19 +379,15 @@ void ui::ttlExit() { for(ui::titleview *t : ttlViews) delete t; - - SDL_DestroyTexture(fldBuffer); + delete ttlOptsPanel; delete ttlOpts; delete infoPanel; - delete fldPanel; - delete fldMenu; - delete fldList; } -void ui::ttlSetActive(int usr) +void ui::ttlSetActive(int usr, bool _set, bool _showSel) { - ttlViews[usr]->setActive(true, true); + ttlViews[usr]->setActive(_set, _showSel); } void ui::ttlUpdate() @@ -505,7 +396,7 @@ void ui::ttlUpdate() ttlOpts->update(); infoPanel->update(); - fldMenu->update(); + ui::fldUpdate(); ttlViews[curUserIndex]->update(); } diff --git a/src/ui/uistr.cpp b/src/ui/uistr.cpp index 0104330..ad26b2e 100644 --- a/src/ui/uistr.cpp +++ b/src/ui/uistr.cpp @@ -108,8 +108,9 @@ void ui::initStrings() addUIString("author", 0, "NULL"); addUIString("helpUser", 0, "[A] Select [Y] Dump All Saves [X] User Options"); addUIString("helpTitle", 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"); - addUIString("helpFolder", 0, "[A] Select [Y] Restore [X] Delete [B] Close"); + addUIString("helpFolder", 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"); addUIString("helpSettings", 0, "[A] Toggle [X] Defaults [B] Back"); + addUIString("helpDrive", 0, "[A] Download [Y] Restore [X] Delete [B] Back"); //Y/N On/Off addUIString("dialogYes", 0, "Yes [A]"); @@ -132,6 +133,7 @@ void ui::initStrings() addUIString("confirmCreateAllSaveData", 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."); addUIString("confirmDeleteBackupsTitle", 0, "Are you sure you would like to delete all save backups for #%s#?"); addUIString("confirmDeleteBackupsAll", 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"); + addUIString("confirmDriveOverwrite", 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"); //Save Data related strings addUIString("saveDataNoneFound", 0, "No saves found for #%s#!"); @@ -266,6 +268,9 @@ void ui::initStrings() addUIString("threadStatusPackingJKSV", 0, "Writing JKSV folder contents to ZIP..."); addUIString("threadStatusSavingTranslations", 0, "Saving the file master..."); addUIString("threadStatusCalculatingSaveSize", 0, "Calculating save data size..."); + addUIString("threadStatusUploadingFile", 0, "Uploading #%s#..."); + addUIString("threadStatusDownloadingFile", 0, "Downloading #%s#..."); + addUIString("threadStatusCompressingSaveForUpload", 0, "Compressing #%s# for upload..."); //Random leftover pop-ups addUIString("popCPUBoostEnabled", 0, "CPU Boost Enabled for ZIP."); @@ -278,6 +283,10 @@ void ui::initStrings() addUIString("popChangeOutputFolder", 0, "#%s# changed to #%s#"); addUIString("popChangeOutputError", 0, "#%s# contains illegal or non-ASCII characters."); addUIString("popTrashEmptied", 0, "Trash emptied"); + addUIString("popSVIExported", 0, "SVI Exported."); + addUIString("popDriveStarted", 0, "Google Drive started successfully."); + addUIString("popDriveFailed", 0, "Failed to start Google Drive."); + addUIString("popDriveNotActive", 0, "Google Drive is not available"); //Keyboard hints addUIString("swkbdEnterName", 0, "Enter a new name"); @@ -312,7 +321,7 @@ void ui::loadTrans() std::string transTestFile = fs::getWorkDir() + "trans.txt"; std::string translationFile = "romfs:/lang/" + getFilename(data::sysLang); bool transFile = fs::fileExists(transTestFile); - if(!transFile && (data::sysLang == SetLanguage_ENUS || cfg::config["langOverride"])) + if(!transFile && (data::sysLang == SetLanguage_ENUS || data::sysLang == SetLanguage_ENGB || cfg::config["langOverride"])) ui::initStrings(); else if(transFile) loadTranslationFile(transTestFile); diff --git a/src/util.cpp b/src/util.cpp index 0e2bdfd..7a2be31 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -11,6 +11,7 @@ #include "ui.h" #include "curlfuncs.h" #include "type.h" +#include "cfg.h" static const uint32_t verboten[] = { L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'}; @@ -370,23 +371,55 @@ SDL_Texture *util::createIconGeneric(const char *txt, int fontSize, bool clearBa return ret; } -void util::setCPU(uint32_t hz) +void util::sysBoost() { if(R_FAILED(clkrstInitialize())) return; - ClkrstSession cpu; + ClkrstSession cpu, gpu, ram; clkrstOpenSession(&cpu, PcvModuleId_CpuBus, 3); - clkrstSetClockRate(&cpu, hz); + clkrstOpenSession(&gpu, PcvModuleId_GPU, 3); + clkrstOpenSession(&ram, PcvModuleId_EMC, 3); + + clkrstSetClockRate(&cpu, util::CPU_SPEED_1785MHz); + clkrstSetClockRate(&gpu, util::GPU_SPEED_76MHz); + clkrstSetClockRate(&ram, util::RAM_SPEED_1600MHz); + clkrstCloseSession(&cpu); + clkrstCloseSession(&gpu); + clkrstCloseSession(&ram); clkrstExit(); } +void util::sysNormal() +{ + if(R_FAILED(clkrstInitialize())) + return; + + ClkrstSession cpu, gpu, ram; + clkrstOpenSession(&cpu, PcvModuleId_CpuBus, 3); + clkrstOpenSession(&gpu, PcvModuleId_GPU, 3); + clkrstOpenSession(&ram, PcvModuleId_EMC, 3); + + if(cfg::config["ovrClk"]) + clkrstSetClockRate(&cpu, util::CPU_SPEED_1224MHz); + else + clkrstSetClockRate(&cpu, util::CPU_SPEED_1020MHz); + clkrstSetClockRate(&gpu, util::GPU_SPEED_76MHz); + clkrstSetClockRate(&ram, util::RAM_SPEED_1331MHz); + + clkrstCloseSession(&cpu); + clkrstCloseSession(&gpu); + clkrstCloseSession(&ram); + clkrstExit(); + +} + void util::checkForUpdate(void *a) { threadInfo *t = (threadInfo *)a; t->status->setStatus(ui::getUICString("threadStatusCheckingForUpdate", 0)); - std::string gitJson = getJSONURL(NULL, "https://api.github.com/repos/J-D-K/JKSV/releases/latest"); + std::string gitJson = curlFuncs::getJSONURL(NULL, "https://api.github.com/repos/J-D-K/JKSV/releases/latest"); if(gitJson.empty()) { ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("onlineErrorConnecting", 0)); @@ -412,7 +445,7 @@ void util::checkForUpdate(void *a) std::vector jksvBuff; std::string url = json_object_get_string(dlUrl); - getBinURL(&jksvBuff, url); + curlFuncs::getBinURL(&jksvBuff, url); FILE *jksvOut = fopen("sdmc:/switch/JKSV.nro", "wb"); fwrite(jksvBuff.data(), 1, jksvBuff.size(), jksvOut); fclose(jksvOut);