Fix config loading, early drive code, threaded file read/write

This commit is contained in:
J-D-K 2021-11-30 15:58:05 -05:00
parent 2fbfe5b62e
commit c29688ea97
28 changed files with 1704 additions and 379 deletions

View File

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

View File

@ -34,4 +34,5 @@ namespace cfg
extern std::vector<uint64_t> blacklist;
extern std::vector<uint64_t> favorites;
extern uint8_t sortType;
extern std::string driveClientID, driveClientSecret, driveRefreshToken, driveAuthCode;
}

View File

@ -1,10 +1,35 @@
#ifndef CURLFUNCS_H
#define CURLFUNCS_H
#pragma once
#include <string>
#include <vector>
std::string getJSONURL(std::vector<std::string> *headers, const std::string& _url);
bool getBinURL(std::vector<uint8_t> *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<std::string> *h);
//Shortcuts/legacy
std::string getJSONURL(std::vector<std::string> *headers, const std::string& url);
bool getBinURL(std::vector<uint8_t> *out, const std::string& url);
}

View File

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

View File

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

View File

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

10
inc/fs/drive.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "../gd.h"
namespace fs
{
extern drive::gd *gDrive;
void driveInit();
void driveExit();
}

View File

@ -1,7 +1,7 @@
#pragma once
#include "data.h"
#include "ui.h"
#include "miscui.h"
#include "type.h"
namespace fs

70
inc/gd.h Normal file
View File

@ -0,0 +1,70 @@
#pragma once
#include <curl/curl.h>
#include <json-c/json.h>
#include <string>
#include <vector>
#include <unordered_map>
#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<std::string> 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<gdDirItem> driveList;
std::string clientID, secretID, token, rToken;
std::string rootDir = "", parentDir = "";
};
}

View File

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

19
inc/ui/fld.h Normal file
View File

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

View File

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

View File

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

View File

@ -14,13 +14,16 @@ std::vector<uint64_t> cfg::blacklist;
std::vector<uint64_t> cfg::favorites;
static std::unordered_map<uint64_t, std::string> 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<std::string, unsigned> 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");

View File

@ -2,21 +2,54 @@
#include <vector>
#include <curl/curl.h>
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<std::string> *headers = (std::vector<std::string> *)u;
headers->push_back(buff);
return sz * cnt;
}
std::string getJSONURL(std::vector<std::string> *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<std::string> *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<std::string> *headers, const std::string& _url)
{
std::string ret;
CURL *handle = curl_easy_init();
@ -29,7 +62,7 @@ std::string getJSONURL(std::vector<std::string> *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<uint8_t> *out, const std::string& _url)
bool curlFuncs::getBinURL(std::vector<uint8_t> *out, const std::string& _url)
{
bool ret = false;
CURL *handle = curl_easy_init();

View File

@ -27,7 +27,6 @@ SetLanguage data::sysLang;
//For other save types
static bool sysBCATPushed = false, tempPushed = false;
std::unordered_map<uint64_t, data::titleInfo> 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());
}
}

View File

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

41
src/fs/drive.cpp Normal file
View File

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

View File

@ -5,6 +5,8 @@
#include <switch.h>
#include <unistd.h>
#include <cstdarg>
#include <mutex>
#include <condition_variable>
#include <sys/stat.h>
#include "fs.h"
@ -16,6 +18,69 @@
static std::string wd = "sdmc:/JKSV/";
typedef struct
{
std::mutex bufferLock;
std::condition_variable cond;
std::vector<uint8_t> 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<uint8_t> localBuffer;
FILE *out = fopen(in->dst.c_str(), "wb");
while(written < in->filesize)
{
std::unique_lock<std::mutex> 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<uint8_t> localBuffer;
FILE *out = fopen(in->dst.c_str(), "wb");
while(written < in->filesize)
{
std::unique_lock<std::mutex> 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<uint8_t> 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<std::mutex> 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<uint8_t> 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<std::mutex> 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)

View File

@ -1,8 +1,57 @@
#include <switch.h>
#include <time.h>
#include <mutex>
#include <vector>
#include <condition_variable>
#include "fs.h"
#include "util.h"
#include "cfg.h"
typedef struct
{
std::mutex buffLock;
std::condition_variable cond;
std::vector<uint8_t> 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<uint8_t> 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<std::mutex> 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<uint8_t> 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<std::mutex> 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);
}
}

654
src/gd.cpp Normal file
View File

@ -0,0 +1,654 @@
#include <stdio.h>
#include <curl/curl.h>
#include <json-c/json.h>
#include <string>
#include <vector>
#include <mutex>
#include <condition_variable>
#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<uint8_t> downloadBuffer;
typedef struct
{
curlFuncs::curlDlArgs *cfa;
std::mutex dataLock;
std::condition_variable cond;
std::vector<uint8_t> 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<uint8_t> localBuff;
int written = 0;
FILE *out = fopen(in->cfa->path.c_str(), "wb");
while(written < in->cfa->size)
{
std::unique_lock<std::mutex> 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<std::mutex> 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<std::string> *headers = new std::vector<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, 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<std::string> *headers = new std::vector<std::string>;
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 "";
}

View File

@ -1,4 +1,5 @@
#include <switch.h>
#include <curl/curl.h>
#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();

View File

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

View File

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

374
src/ui/fld.cpp Normal file
View File

@ -0,0 +1,374 @@
#include <switch.h>
#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);
}

View File

@ -6,14 +6,11 @@
#include "util.h"
#include "cfg.h"
static int ttlHelpX = 0, fldHelpWidth = 0;
static int ttlHelpX = 0;
static std::vector<ui::titleview *> 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();
}

View File

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

View File

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