Progress bars, split some ui classes, threaded confirm, better thread safety(I think)

This commit is contained in:
J-D-K 2021-07-22 18:44:47 -04:00
parent d18204733d
commit 9227c7a3f1
23 changed files with 818 additions and 669 deletions

View File

@ -34,11 +34,11 @@ TARGET := JKSV
BUILD := build
SOURCES := src src/ui
DATA := data
INCLUDES := inc
INCLUDES := inc inc/ui
EXEFS_SRC := exefs_src
APP_TITLE := JKSV
APP_AUTHOR := JK
APP_VERSION := 07.18.2021
APP_VERSION := 07.22.2021
ROMFS := romfs
ICON := icon.jpg

View File

@ -11,7 +11,7 @@
#define curData users[data::selUser].titleInfo[data::selData]
#define BLD_MON 7
#define BLD_DAY 18
#define BLD_DAY 22
#define BLD_YEAR 2021
namespace data

View File

@ -1,19 +1,36 @@
#pragma once
#include <string>
#include <switch.h>
//Misc stuff for new menu code
typedef void (*funcPtr)(void *);
typedef struct
{
Mutex statusLock = 0;
std::string status;
void setStatus(const std::string& newStatus)
{
mutexLock(&statusLock);
status = newStatus;
mutexUnlock(&statusLock);
}
void getStatus(std::string& statusOut)
{
mutexLock(&statusLock);
statusOut = status;
mutexUnlock(&statusLock);
}
} threadStatus;
typedef struct
{
bool running = false, finished = false;
Thread *thrdPtr = NULL;
Thread thrd;
void *argPtr = NULL;
Mutex statusLock = 0;
std::string *status;
void updateStatus(const std::string& newStatus)
{
mutexLock(&statusLock);
*status = newStatus;
mutexUnlock(&statusLock);
}
funcPtr drawFunc = NULL;//Draw func is passed threadInfo pointer too
threadStatus *status;
} threadInfo;

View File

@ -8,12 +8,15 @@
#include "gfx.h"
//ui headers - split up to keep a bit more organized
#include "miscui.h"
#include "uistr.h"
#include "usr.h"
#include "ttl.h"
#include "sett.h"
#include "fm.h"
#include "ui/miscui.h"
#include "ui/uistr.h"
#include "ui/usr.h"
#include "ui/ttl.h"
#include "ui/sett.h"
#include "ui/fm.h"
#include "ui/ttlview.h"
#include "ui/thrdProc.h"
#include "ui/sldpanel.h"
enum menuState
{
@ -84,7 +87,7 @@ namespace ui
//Adds a panel pointer to a vector since they need to be drawn over everything else
int registerMenu(ui::menu *m);
int registerPanel(ui::slideOutPanel *sop);
int newThread(ThreadFunc func, void *args);
threadInfo *newThread(ThreadFunc func, void *args, funcPtr _drawFunc);
//Just draws a screen and flips JIC boot takes long.
void showLoadScreen();

View File

@ -23,6 +23,19 @@ typedef enum
//For smaller classes that aren't easy to get lost in and general functions
namespace ui
{
typedef struct
{
std::string text;
bool hold;
bool cleanup;
funcPtr func;
void *args;
//Stuff needed to keep track
bool sel = 1;//1 = YES
} confirmArgs;
typedef struct
{
funcPtr func = NULL;
@ -103,86 +116,20 @@ namespace ui
{
public:
//Constructor. _max is the maximum value
progBar() = default;
progBar(const uint64_t& _max) { max = _max; }
void setMax(const uint64_t& _max) { max = _max; };
//Updates progress
void update(const uint64_t& _prog);
//Draws with text at top
void draw(const std::string& text, const std::string& head);
void draw(const std::string& text);
private:
uint64_t max, prog;
float width;
};
//_draw is called and passed the panel texture/target when this.draw() is called.
class slideOutPanel
{
public:
slideOutPanel(int _w, int _h, int _y, funcPtr _draw);
~slideOutPanel();
void update();
void setCallback(funcPtr _cb, void *_args) { callback = _cb; cbArgs = _args; }
void openPanel() { open = true; }
void closePanel() { open = false; }
bool isOpen() { return open; }
void draw(const SDL_Color *backCol);
private:
int w, h, x = 1280, y;
bool open = false;
SDL_Texture *panel;
funcPtr drawFunc, callback = NULL;
void *cbArgs = NULL;
};
class titleTile
{
public:
titleTile(unsigned _w, unsigned _h, bool _fav, SDL_Texture *_icon)
{
w = _w;
h = _h;
wS = _w;
hS = _h;
fav = _fav;
icon = _icon;
}
void draw(SDL_Texture *target, int x, int y, bool sel);
private:
unsigned w, h, wS, hS;
bool fav = false;
SDL_Texture *icon;
};
//Todo less hardcode etc
class titleview
{
public:
titleview(const data::user& _u, int _iconW, int _iconH, int _horGap, int _vertGap, int _rowCount, funcPtr _callback);
~titleview();
void update();
void refresh();
void setActive(bool _set, bool _showSel) { active = _set; showSel = _showSel; }
bool getActive(){ return active; }
void setSelected(int _set){ selected = _set; }
int getSelected(){ return selected; }
void draw(SDL_Texture *target);
private:
const data::user *u;//Might not be safe. Users *shouldn't* be touched after initial load
bool active = false, showSel = false, clrAdd = true;
uint8_t clrShft = 0;
funcPtr callback = NULL;
int x = 34, y = 69, selected = 0, selRectX = 10, selRectY = 45;
int iconW, iconH, horGap, vertGap, rowCount;
std::vector<ui::titleTile *> tiles;
uint64_t max = 0, prog = 0;
float width = 0;
};
typedef struct
@ -206,26 +153,10 @@ namespace ui
std::vector<popMessage> message;
};
class threadProcMngr
{
public:
~threadProcMngr();
void newThread(ThreadFunc func, void *args);
void addThread(threadInfo *t);
void update();
void draw();
bool empty(){ return threads.empty(); }
private:
std::vector<threadInfo *> threads;
uint8_t lgFrame = 0;
unsigned frameCount = 0;
Mutex threadLock = 0;
};
//General use
ui::confirmArgs *confirmArgsCreate(bool _hold, funcPtr _func, void *_funcArgs, bool _cleanup, const char *fmt, ...);
void showMessage(const char *head, const char *fmt, ...);
bool confirm(bool hold, const char *fmt, ...);
void confirm(void *a);
bool confirmTransfer(const std::string& f, const std::string& t);
bool confirmDelete(const std::string& p);
void drawBoundBox(SDL_Texture *target, int x, int y, int w, int h, uint8_t clrSh);

26
inc/ui/sldpanel.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
namespace ui
{
//_draw is called and passed the panel texture/target when this.draw() is called.
class slideOutPanel
{
public:
slideOutPanel(int _w, int _h, int _y, funcPtr _draw);
~slideOutPanel();
void update();
void setCallback(funcPtr _cb, void *_args) { callback = _cb; cbArgs = _args; }
void openPanel() { open = true; }
void closePanel() { open = false; }
bool isOpen() { return open; }
void draw(const SDL_Color *backCol);
private:
int w, h, x = 1280, y;
bool open = false;
SDL_Texture *panel;
funcPtr drawFunc, callback = NULL;
void *cbArgs = NULL;
};
}

23
inc/ui/thrdProc.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "type.h"
namespace ui
{
class threadProcMngr
{
public:
~threadProcMngr();
//Draw function is used and called to draw on overlay
threadInfo *newThread(ThreadFunc func, void *args, funcPtr _drawFunc);
void update();
void draw();
bool empty() { return threads.empty(); }
private:
std::vector<threadInfo *> threads;
uint8_t lgFrame = 0;
unsigned frameCount = 0;
Mutex threadLock = 0;
};
}

56
inc/ui/ttlview.h Normal file
View File

@ -0,0 +1,56 @@
#pragma once
#include <SDL.h>
#include "type.h"
#include "data.h"
namespace ui
{
class titleTile
{
public:
titleTile(unsigned _w, unsigned _h, bool _fav, SDL_Texture *_icon)
{
w = _w;
h = _h;
wS = _w;
hS = _h;
fav = _fav;
icon = _icon;
}
void draw(SDL_Texture *target, int x, int y, bool sel);
private:
unsigned w, h, wS, hS;
bool fav = false;
SDL_Texture *icon;
};
//Todo less hardcode etc
class titleview
{
public:
titleview(const data::user& _u, int _iconW, int _iconH, int _horGap, int _vertGap, int _rowCount, funcPtr _callback);
~titleview();
void update();
void refresh();
void setActive(bool _set, bool _showSel) { active = _set; showSel = _showSel; }
bool getActive() { return active; }
void setSelected(int _set) { selected = _set; }
int getSelected() { return selected; }
void draw(SDL_Texture *target);
private:
const data::user *u;//Might not be safe. Users *shouldn't* be touched after initial load
bool active = false, showSel = false, clrAdd = true;
uint8_t clrShft = 0;
funcPtr callback = NULL;
int x = 34, y = 69, selected = 0, selRectX = 10, selRectY = 45;
int iconW, iconH, horGap, vertGap, rowCount;
std::vector<ui::titleTile *> tiles;
};
}

View File

@ -30,7 +30,7 @@ namespace util
std::string getInfoString(data::user& u, const uint64_t& tid);
std::string getStringInput(const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[]);
std::string getStringInput(SwkbdType _type, const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[]);
inline std::string getExtensionFromString(const std::string& get)
{

View File

@ -31,10 +31,16 @@ typedef struct
std::string to, from, dev;
zipFile z;
unzFile unz;
bool closeZip = false;
bool cleanup = false;
uint64_t offset = 0, fileSize = 0;
ui::progBar *prog;
threadStatus *thrdStatus;
Mutex arglck = 0;
void argLock() { mutexLock(&arglck); }
void argUnlock() { mutexUnlock(&arglck); }
} copyArgs;
static copyArgs *copyArgsCreate(const std::string& from, const std::string& to, const std::string& dev, zipFile z, unzFile unz, bool _closeZip)
static copyArgs *copyArgsCreate(const std::string& from, const std::string& to, const std::string& dev, zipFile z, unzFile unz, bool _cleanup)
{
copyArgs *ret = new copyArgs;
ret->to = to;
@ -42,10 +48,20 @@ static copyArgs *copyArgsCreate(const std::string& from, const std::string& to,
ret->dev = dev;
ret->z = z;
ret->unz = unz;
ret->closeZip = _closeZip;
ret->cleanup = _cleanup;
ret->prog = new ui::progBar;
ret->fileSize = 0.0f;
ret->offset = 0.0f;
return ret;
}
static void copyArgsDestroy(copyArgs *c)
{
delete c->prog;
delete c;
c = NULL;
}
static struct
{
bool operator()(const fs::dirItem& a, const fs::dirItem& b)
@ -349,19 +365,26 @@ int fs::dataFile::getNextValueInt()
return ret;
}
static inline std::string fileStatusString(const std::string& itm, float complete, float total)
//Todo: Weird flickering?
static void _fileDrawFunc(void *a)
{
char tmp[512];
sprintf(tmp, "Copying \"%s\" : %.2f MB / %.2f MB", itm.c_str(), complete / 1024.0f / 1024.0f, total / 1024.0f / 1024.0f);
return std::string(tmp);
threadInfo *t = (threadInfo *)a;
if(!t->finished)
{
copyArgs *c = (copyArgs *)t->argPtr;
std::string tmp;
t->status->getStatus(tmp);
c->argLock();
c->prog->draw(tmp);
c->argUnlock();
}
}
static void copyfile_t(void *a)
{
threadInfo *t = (threadInfo *)a;
copyArgs *args = (copyArgs *)t->argPtr;
float srcSize = (float)fs::fsize(args->from);
t->status->setStatus("Copying '" + args->from + "'...");
uint8_t *buff = new uint8_t[BUFF_SIZE];
if(data::config["directFsCmd"])
@ -380,7 +403,10 @@ static void copyfile_t(void *a)
while((readIn = fsfread(buff, 1, BUFF_SIZE, in)) > 0)
{
fsfwrite(buff, 1, readIn, out);
*t->status = fileStatusString(args->from, (float)in->offset, srcSize);
args->argLock();
args->offset = in->offset;
args->prog->update(args->offset);
args->argUnlock();
}
fsfclose(in);
fsfclose(out);
@ -401,20 +427,25 @@ static void copyfile_t(void *a)
while((readIn = fread(buff, 1, BUFF_SIZE, in)) > 0)
{
fwrite(buff, 1, readIn, out);
*t->status = fileStatusString(args->from, (float)ftell(in), srcSize);
args->argLock();
args->offset = ftell(in);
args->prog->update(args->offset);
args->argUnlock();
}
fclose(in);
fclose(out);
}
delete[] buff;
delete args;
copyArgsDestroy(args);
t->finished = true;
}
void fs::copyFile(const std::string& from, const std::string& to)
{
copyArgs *send = copyArgsCreate(from, to, "", NULL, NULL, false);
ui::newThread(copyfile_t, send);
send->fileSize = fs::fsize(from);
send->prog->setMax(send->fileSize);
ui::newThread(copyfile_t, send, _fileDrawFunc);
}
void copyFileCommit_t(void *a)
@ -422,8 +453,8 @@ void copyFileCommit_t(void *a)
threadInfo *t = (threadInfo *)a;
copyArgs *args = (copyArgs *)t->argPtr;
data::titleInfo *info = data::getTitleInfoByTID(data::curData.saveID);
t->status->setStatus("Copying '" + args->from + "'...");
float srcSize = (float)fs::fsize(args->from);
uint64_t journalSize = getJournalSize(info), writeCount = 0;
uint8_t *buff = new uint8_t[BUFF_SIZE];
@ -454,7 +485,10 @@ void copyFileCommit_t(void *a)
out = fsfopen(args->to.c_str(), FsOpenMode_Write | FsOpenMode_Append);
}
*t->status = fileStatusString(args->from, (float)out->offset, srcSize);
args->argLock();
args->offset = out->offset;
args->prog->update(args->offset);
args->argUnlock();
}
fsfclose(in);
fsfclose(out);
@ -486,7 +520,10 @@ void copyFileCommit_t(void *a)
out = fopen(args->to.c_str(), "ab");
}
*t->status = fileStatusString(args->from, (float)ftell(out), srcSize);
args->argLock();
args->offset = ftell(out);
args->prog->update(args->offset);
args->argUnlock();
}
fclose(in);
fclose(out);
@ -494,15 +531,16 @@ void copyFileCommit_t(void *a)
delete[] buff;
commitToDevice(args->dev.c_str());
delete args;
copyArgsDestroy(args);
t->finished = true;
}
void fs::copyFileCommit(const std::string& from, const std::string& to, const std::string& dev)
{
ui::progBar prog(fsize(from));
copyArgs *send = copyArgsCreate(from, to, dev, NULL, NULL, false);
ui::newThread(copyFileCommit_t, send);
send->fileSize = fs::fsize(from);
send->prog->setMax(send->fileSize);
ui::newThread(copyFileCommit_t, send, _fileDrawFunc);
}
void fs::copyDirToDir(const std::string& from, const std::string& to)
@ -539,30 +577,12 @@ void closeZip_t(void *a)
t->finished = true;
}
void copyFileToZip(const std::string& from, zipFile z, std::string *_status)
{
float srcSize = fs::fsize(from);
FILE *cpy = fopen(from.c_str(), "rb");
size_t readIn = 0;
uint8_t *buff = new uint8_t[BUFF_SIZE];
while((readIn = fread(buff, 1, BUFF_SIZE, cpy)) > 0)
{
zipWriteInFileInZip(z, buff, readIn);
*_status = (fileStatusString(from, (float)ftell(cpy), srcSize));
}
delete[] buff;
fclose(cpy);
}
void copyDirToZip_t(void *a)
{
threadInfo *t = (threadInfo *)a;
copyArgs *args = (copyArgs *)t->argPtr;
t->updateStatus("Opening " + args->from + "...");
//fs::logWrite(t->status->c_str());
t->status->setStatus("Opening " + args->from + "...");
fs::dirList *list = new fs::dirList(args->from);
unsigned listTotal = list->getCount();
@ -575,11 +595,18 @@ void copyDirToZip_t(void *a)
if(list->isDir(i))
{
std::string newFrom = args->from + itm + "/";
//Fake thread and new args to point to src thread stuff
//This wouldn't work spawning a new thread.
threadInfo *tmpThread = new threadInfo;
tmpThread->status = t->status;
copyArgs *tmpArgs = copyArgsCreate(newFrom, "", "", args->z, NULL, false);
copyArgs *tmpArgs = new copyArgs;
tmpArgs->from = newFrom;
tmpArgs->prog = args->prog;
tmpArgs->z = args->z;
tmpArgs->cleanup = false;
tmpThread->argPtr = tmpArgs;
copyDirToZip_t(tmpThread);
delete tmpArgs;
delete tmpThread;
}
else
@ -587,67 +614,92 @@ void copyDirToZip_t(void *a)
zip_fileinfo inf = {0};
std::string filename = args->from + itm;
size_t devPos = filename.find_first_of('/') + 1;
t->updateStatus("Adding \"" + itm + "\" to ZIP.");
if(zipOpenNewFileInZip(args->z, filename.substr(devPos, filename.length()).c_str(), &inf, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION) == ZIP_OK)
t->status->setStatus("Adding '" + itm + "' to ZIP.");
int zOpenFile = zipOpenNewFileInZip64(args->z, filename.substr(devPos, filename.length()).c_str(), &inf, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0);
if(zOpenFile == ZIP_OK)
{
copyFileToZip(args->from +itm, args->z, t->status);
std::string fullFrom = args->from + itm;
args->offset = 0;
args->fileSize = fs::fsize(fullFrom);
args->prog->setMax(args->fileSize);
args->prog->update(0);
FILE *cpy = fopen(fullFrom.c_str(), "rb");
size_t readIn = 0;
uint8_t *buff = new uint8_t[BUFF_SIZE];
while((readIn = fread(buff, 1, BUFF_SIZE, cpy)) > 0)
{
zipWriteInFileInZip(args->z, buff, readIn);
args->offset += readIn;
args->prog->update(args->offset);
}
delete[] buff;
fclose(cpy);
zipCloseFileInZip(args->z);
}
}
}
delete list;
if(args->closeZip)
ui::newThread(closeZip_t, args->z);
delete args;
if(args->cleanup)
{
ui::newThread(closeZip_t, args->z, NULL);
delete args->prog;
delete args;
}
t->finished = true;
}
void fs::copyDirToZip(const std::string& from, zipFile to)
{
copyArgs *send = copyArgsCreate(from, "", "", to, NULL, true);
ui::newThread(copyDirToZip_t, send);
ui::newThread(copyDirToZip_t, send, _fileDrawFunc);
}
void copyZipToDir_t(void *a)
{
threadInfo *t = (threadInfo *)a;
copyArgs *cpyArgs = (copyArgs *)t->argPtr;
copyArgs *args = (copyArgs *)t->argPtr;
data::titleInfo *tinfo = data::getTitleInfoByTID(data::curData.saveID);
uint64_t journalSize = getJournalSize(tinfo), writeCount = 0;
char filename[FS_MAX_PATH];
uint8_t *buff = new uint8_t[BUFF_SIZE];
int readIn = 0;
unz_file_info info;
if(unzGoToFirstFile(cpyArgs->unz) == UNZ_OK)
unz_file_info64 info;
if(unzGoToFirstFile(args->unz) == UNZ_OK)
{
do
{
unzGetCurrentFileInfo(cpyArgs->unz, &info, filename, FS_MAX_PATH, NULL, 0, NULL, 0);
if(unzOpenCurrentFile(cpyArgs->unz) == UNZ_OK)
unzGetCurrentFileInfo64(args->unz, &info, filename, FS_MAX_PATH, NULL, 0, NULL, 0);
if(unzOpenCurrentFile(args->unz) == UNZ_OK)
{
std::string path = cpyArgs->to + filename;
t->status->setStatus("Copying '" + std::string(filename) + "'...");
std::string path = args->to + filename;
mkdirRec(path.substr(0, path.find_last_of('/') + 1));
float srcSize = (float)info.uncompressed_size;
args->fileSize = info.uncompressed_size;
args->offset = 0.0f;
args->prog->setMax(args->fileSize);
size_t done = 0;
if(data::config["directFsCmd"])
{
FSFILE *out = fsfopen(path.c_str(), FsOpenMode_Write);
while((readIn = unzReadCurrentFile(cpyArgs->unz, buff, BUFF_SIZE)) > 0)
while((readIn = unzReadCurrentFile(args->unz, buff, BUFF_SIZE)) > 0)
{
done += readIn;
writeCount += readIn;
args->offset += readIn;
args->prog->update(args->offset);
fsfwrite(buff, 1, readIn, out);
if(writeCount >= (journalSize - 0x100000))
{
writeCount = 0;
fsfclose(out);
if(!commitToDevice(cpyArgs->dev.c_str()))
if(!commitToDevice(args->dev.c_str()))
break;
out = fsfopen(path.c_str(), FsOpenMode_Write | FsOpenMode_Append);
}
t->updateStatus(fileStatusString(filename, (float)done, srcSize));
}
fsfclose(out);
}
@ -655,37 +707,39 @@ void copyZipToDir_t(void *a)
{
FILE *out = fopen(path.c_str(), "wb");
while((readIn = unzReadCurrentFile(cpyArgs->unz, buff, BUFF_SIZE)) > 0)
while((readIn = unzReadCurrentFile(args->unz, buff, BUFF_SIZE)) > 0)
{
done += readIn;
writeCount += readIn;
args->offset += readIn;
args->prog->update(args->offset);
fwrite(buff, 1, readIn, out);
if(writeCount >= (journalSize - 0x100000))
{
writeCount = 0;
fclose(out);
if(!commitToDevice(cpyArgs->dev.c_str()))
if(!commitToDevice(args->dev.c_str()))
break;
out = fopen(path.c_str(), "ab");
}
t->updateStatus(fileStatusString(filename, (float)done, srcSize));
}
fclose(out);
}
unzCloseCurrentFile(cpyArgs->unz);
commitToDevice(cpyArgs->dev.c_str());
unzCloseCurrentFile(args->unz);
commitToDevice(args->dev.c_str());
}
}
while(unzGoToNextFile(cpyArgs->unz) != UNZ_END_OF_LIST_OF_FILE);
while(unzGoToNextFile(args->unz) != UNZ_END_OF_LIST_OF_FILE);
}
else
ui::showPopMessage(POP_FRAME_DEFAULT, "ZIP file is empty!");
if(cpyArgs->closeZip)
unzClose(cpyArgs->unz);
delete cpyArgs;
if(args->cleanup)
{
unzClose(args->unz);
copyArgsDestroy(args);
}
delete[] buff;
t->finished = true;
}
@ -693,7 +747,7 @@ void copyZipToDir_t(void *a)
void fs::copyZipToDir(unzFile unz, const std::string& to, const std::string& dev)
{
copyArgs *send = copyArgsCreate("", to, dev, NULL, unz, true);
ui::newThread(copyZipToDir_t, send);
ui::newThread(copyZipToDir_t, send, _fileDrawFunc);
}
void fs::copyDirToDirCommit(const std::string& from, const std::string& to, const std::string& dev)
@ -939,7 +993,7 @@ void fs::createNewBackup(void *a)
util::generateAbbrev(data::curData.saveID),
".zip"
};
out = util::getStringInput("", "Enter a name", 64, 9, dict);
out = util::getStringInput(SwkbdType_QWERTY, "", "Enter a name", 64, 9, dict);
}
if(!out.empty())
@ -951,7 +1005,7 @@ void fs::createNewBackup(void *a)
if(ext != "zip")//data::zip is on but extension is not zip
path += ".zip";
zipFile zip = zipOpen(path.c_str(), 0);
zipFile zip = zipOpen64(path.c_str(), 0);
fs::copyDirToZip("sv:/", zip);
}
@ -967,49 +1021,49 @@ void fs::createNewBackup(void *a)
void fs::overwriteBackup(void *a)
{
fs::backupArgs *in = (fs::backupArgs *)a;
threadInfo *t = (threadInfo *)a;
fs::backupArgs *in = (fs::backupArgs *)t->argPtr;
ui::menu *m = in->m;
fs::dirList *d = in->d;
unsigned ind = m->getSelected() - 1;;//Skip new
std::string itemName = d->getItem(ind);
if(ui::confirm(data::config["holdOver"], ui::confOverwrite.c_str(), itemName.c_str()))
if(d->isDir(ind))
{
if(d->isDir(ind))
{
std::string toPath = util::generatePathByTID(data::curData.saveID) + itemName + "/";
//Delete and recreate
fs::delDir(toPath);
mkdir(toPath.c_str(), 777);
fs::copyDirToDir("sv:/", toPath);
}
else if(!d->isDir(ind) && d->getItemExt(ind) == "zip")
{
std::string toPath = util::generatePathByTID(data::curData.saveID) + itemName;
fs::delfile(toPath);
zipFile zip = zipOpen(toPath.c_str(), 0);
if(zip)
fs::copyDirToZip("sv:/", zip);
}
std::string toPath = util::generatePathByTID(data::curData.saveID) + itemName + "/";
//Delete and recreate
fs::delDir(toPath);
mkdir(toPath.c_str(), 777);
fs::copyDirToDir("sv:/", toPath);
}
else if(!d->isDir(ind) && d->getItemExt(ind) == "zip")
{
std::string toPath = util::generatePathByTID(data::curData.saveID) + itemName;
fs::delfile(toPath);
zipFile zip = zipOpen64(toPath.c_str(), 0);
if(zip)
fs::copyDirToZip("sv:/", zip);
}
t->finished = true;
}
void fs::restoreBackup(void *a)
{
fs::backupArgs *in = (fs::backupArgs *)a;
threadInfo *t = (threadInfo *)a;
fs::backupArgs *in = (fs::backupArgs *)t->argPtr;
ui::menu *m = in->m;
fs::dirList *d = in->d;
unsigned ind = m->getSelected() - 1;
std::string itemName = d->getItem(ind);
if((data::curData.saveInfo.save_data_type != FsSaveDataType_System || data::config["sysSaveWrite"]) && m->getSelected() > 0 && ui::confirm(data::config["holdRest"], ui::confRestore.c_str(), itemName.c_str()))
if((data::curData.saveInfo.save_data_type != FsSaveDataType_System || data::config["sysSaveWrite"]) && m->getSelected() > 0)
{
if(data::config["autoBack"] && data::config["zip"])
{
std::string autoZip = util::generatePathByTID(data::curData.saveID) + "/AUTO " + data::curUser.getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD) + ".zip";
zipFile zip = zipOpen(autoZip.c_str(), 0);
zipFile zip = zipOpen64(autoZip.c_str(), 0);
fs::copyDirToZip("sv:/", zip);
}
else if(data::config["autoBack"])
@ -1029,7 +1083,7 @@ void fs::restoreBackup(void *a)
{
fs::wipeSave();
std::string path = util::generatePathByTID(data::curData.saveID) + itemName;
unzFile unz = unzOpen(path.c_str());
unzFile unz = unzOpen64(path.c_str());
fs::copyZipToDir(unz, "sv:/", "sv");
}
else
@ -1043,40 +1097,43 @@ void fs::restoreBackup(void *a)
if(data::config["autoBack"])
ui::populateFldMenu();
t->finished = true;
}
void fs::deleteBackup(void *a)
{
fs::backupArgs *in = (fs::backupArgs *)a;
threadInfo *t = (threadInfo *)a;
fs::backupArgs *in = (fs::backupArgs *)t->argPtr;
ui::menu *m = in->m;
fs::dirList *d = in->d;
unsigned ind = m->getSelected() - 1;
std::string itemName = d->getItem(ind);
if(ui::confirmDelete(itemName))
t->status->setStatus("Deleting '" + itemName + "'...");
if(data::config["trashBin"])
{
if(data::config["trashBin"])
{
std::string oldPath = util::generatePathByTID(data::curData.saveID) + itemName;
std::string trashPath = wd + "_TRASH_/" + itemName;
rename(oldPath.c_str(), trashPath.c_str());
ui::showPopMessage(POP_FRAME_DEFAULT, "%s moved to trash.", itemName.c_str());
}
else if(d->isDir(ind))
{
std::string delPath = util::generatePathByTID(data::curData.saveID) + itemName + "/";
fs::delDir(delPath);
ui::showPopMessage(POP_FRAME_DEFAULT, "%s has been deleted.", itemName.c_str());
}
else
{
std::string delPath = util::generatePathByTID(data::curData.saveID) + itemName;
fs::delfile(delPath);
ui::showPopMessage(POP_FRAME_DEFAULT, "%s has been deleted.", itemName.c_str());
}
ui::populateFldMenu();
std::string oldPath = util::generatePathByTID(data::curData.saveID) + itemName;
std::string trashPath = wd + "_TRASH_/" + itemName;
rename(oldPath.c_str(), trashPath.c_str());
ui::showPopMessage(POP_FRAME_DEFAULT, "%s moved to trash.", itemName.c_str());
}
else if(d->isDir(ind))
{
std::string delPath = util::generatePathByTID(data::curData.saveID) + itemName + "/";
fs::delDir(delPath);
ui::showPopMessage(POP_FRAME_DEFAULT, "%s has been deleted.", itemName.c_str());
}
else
{
std::string delPath = util::generatePathByTID(data::curData.saveID) + itemName;
fs::delfile(delPath);
ui::showPopMessage(POP_FRAME_DEFAULT, "%s has been deleted.", itemName.c_str());
}
ui::populateFldMenu();
t->finished = true;
}
void fs::logOpen()

View File

@ -75,7 +75,7 @@ void ui::initTheme()
txtDiag = {0xFF, 0xFF, 0xFF, 0xFF};
rectLt = {0xDF, 0xDF, 0xDF, 0xFF};
rectSh = {0xCA, 0xCA, 0xCA, 0xFF};
tboxClr = {0x50, 0x50, 0x50, 0xFF};
tboxClr = {0xEB, 0xEB, 0xEB, 0xFF};
divClr = {0x00, 0x00, 0x00, 0xFF};
slidePanelColor = {0xEE, 0xEE, 0xEE, 0xDD};
break;
@ -84,13 +84,12 @@ void ui::initTheme()
case ColorSetId_Dark:
//jic
thmID = ColorSetId_Dark;
clearClr = {0x2D, 0x2D, 0x2D, 0xFF};
txtCont = {0xFF, 0xFF, 0xFF, 0xFF};
txtDiag = {0x00, 0x00, 0x00, 0xFF};
rectLt = {0x50, 0x50, 0x50, 0xFF};
rectSh = {0x20, 0x20, 0x20, 0xFF};
tboxClr = {0xEB, 0xEB, 0xEB, 0xFF};
tboxClr = {0x50, 0x50, 0x50, 0xFF};
divClr = {0xFF, 0xFF, 0xFF, 0xFF};
slidePanelColor = {0x00, 0x00, 0x00, 0xDD};
break;
@ -113,10 +112,10 @@ void ui::init()
{
case ColorSetId_Light:
//Dark corners
cornerTopLeft = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerTopLeft.png");
cornerTopRight = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerTopRight.png");
cornerBottomLeft = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerBotLeft.png");
cornerBottomRight = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerBotRight.png");
cornerTopLeft = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerTopLeft.png");
cornerTopRight = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerTopRight.png");
cornerBottomLeft = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerBotLeft.png");
cornerBottomRight = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerBotRight.png");
progCovLeft = gfx::loadImageFile("romfs:/img/tboxDrk/progBarCoverLeftDrk.png");
progCovRight = gfx::loadImageFile("romfs:/img/tboxDrk/progBarCoverRightDrk.png");
icn = gfx::loadImageFile("romfs:/img/icn/icnDrk.png");
@ -125,10 +124,10 @@ void ui::init()
default:
//Light corners
cornerTopLeft = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerTopLeft.png");
cornerTopRight = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerTopRight.png");
cornerBottomLeft = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerBotLeft.png");
cornerBottomRight = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerBotRight.png");
cornerTopLeft = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerTopLeft.png");
cornerTopRight = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerTopRight.png");
cornerBottomLeft = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerBotLeft.png");
cornerBottomRight = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerBotRight.png");
progCovLeft = gfx::loadImageFile("romfs:/img/tboxLght/progBarCoverLeftLight.png");
progCovRight = gfx::loadImageFile("romfs:/img/tboxLght/progBarCoverRightLight.png");
icn = gfx::loadImageFile("romfs:/img/icn/icnLght.png");
@ -205,10 +204,9 @@ int ui::registerPanel(slideOutPanel *sop)
return panels.size() - 1;
}
int ui::newThread(ThreadFunc func, void *args)
threadInfo *ui::newThread(ThreadFunc func, void *args, funcPtr _drawFunc)
{
threadMngr->newThread(func, args);
return 0;
return threadMngr->newThread(func, args, _drawFunc);
}
void ui::showLoadScreen()
@ -243,7 +241,7 @@ void ui::drawUI()
if(ui::usrMenu->getSelected() == settPos || ui::mstate == OPT_MNU)
ui::settDraw(rightPanel);
else if(usrMenu->getSelected() == extPos || ui::mstate == EX_MNU)
gfx::drawTextf(rightPanel, 24, 32, 32, &ui::txtCont, "PLACE HOLDER SO COMPILER DOESN'T COMPLAIN");
gfx::drawTextf(rightPanel, 24, 32, 32, &ui::txtCont, "PLACE HOLDER");
else
ui::ttlDraw(rightPanel);
@ -252,21 +250,21 @@ void ui::drawUI()
for(slideOutPanel *s : panels)
s->draw(&ui::slidePanelColor);
popMessages->draw();
if(!threadMngr->empty())
threadMngr->draw();
popMessages->draw();
}
static bool debugDisp = false;
bool ui::runApp()
{
ui::updateInput();
uint64_t down = ui::padKeysDown();
if(threadMngr->empty())
{
ui::updateInput();
uint64_t down = ui::padKeysDown();
if(down & HidNpadButton_StickL && down & HidNpadButton_StickR)
debugDisp = true;
else if(down & HidNpadButton_Plus)

View File

@ -7,6 +7,7 @@
#include "ui.h"
#include "miscui.h"
#include "util.h"
#include "type.h"
static const char *okt = "OK \ue0e0";
@ -15,12 +16,10 @@ static const SDL_Color divDark = {0xCC, 0xCC, 0xCC, 0xFF};
static const SDL_Color shadow = {0x66, 0x66, 0x66, 0xFF};
static const SDL_Color fillBack = {0x66, 0x66, 0x66, 0xFF};
static const SDL_Color fillLight = {0x00, 0xFF, 0xC5, 0xFF};
static const SDL_Color fillDark = {0x32, 0x50, 0xF0, 0xFF};
static const SDL_Color fillBar = {0x00, 0xDD, 0x00, 0xFF};
static const SDL_Color menuColorLight = {0x32, 0x50, 0xF0, 0xFF};
static const SDL_Color menuColorDark = {0x00, 0xFF, 0xC5, 0xFF};
static const SDL_Color darkenBack = {0x00, 0x00, 0x00, 0xBB};
void ui::menu::setParams(const unsigned& _x, const unsigned& _y, const unsigned& _rW, const unsigned& _fS, const unsigned& _mL)
{
@ -258,239 +257,21 @@ void ui::progBar::update(const uint64_t& _prog)
{
prog = _prog;
width = (float)(((float)prog / (float)max) * 576);
if(max > 0)
width = (float)((float)prog / (float)max) * 576.0f;
}
void ui::progBar::draw(const std::string& text, const std::string& head)
void ui::progBar::draw(const std::string& text)
{
size_t headWidth = gfx::getTextWidth(head.c_str(), 20);
unsigned headX = (1280 / 2) - (headWidth / 2);
char progStr[64];
sprintf(progStr, "%.2fMB / %.2fMB", (float)prog / 1024.0f / 1024.0f, (float)max / 1024.0f / 1024.0f);
int progX = 640 - (gfx::getTextWidth(progStr, 18) / 2);
ui::drawTextbox(NULL, 320, 150, 640, 420);
gfx::drawLine(NULL, ui::thmID == ColorSetId_Light ? &divLight : &divDark, 320, 206, 959, 206);
gfx::drawRect(NULL, &fillBack, 352, 530, 576, 12);
gfx::drawRect(NULL, ui::thmID == ColorSetId_Light ? &fillLight : &fillDark, 352, 530, (int)width, 12);
gfx::texDraw(NULL, ui::progCovLeft, 352, 530);
gfx::texDraw(NULL, ui::progCovRight, 920, 530);
gfx::drawTextf(NULL, 20, headX, 168, &ui::txtDiag, head.c_str());
gfx::drawTextfWrap(NULL, 16, 352, 230, 576, &ui::txtDiag, text.c_str());
}
ui::slideOutPanel::slideOutPanel(int _w, int _h, int _y, funcPtr _draw)
{
w = _w;
h = _h;
y = _y;
drawFunc = _draw;
panel = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, w, h);
SDL_SetTextureBlendMode(panel, SDL_BLENDMODE_BLEND);
}
ui::slideOutPanel::~slideOutPanel()
{
SDL_DestroyTexture(panel);
}
void ui::slideOutPanel::update()
{
if(open && callback)
(*callback)(cbArgs);
}
void ui::slideOutPanel::draw(const SDL_Color *backCol)
{
gfx::clearTarget(panel, backCol);
if(open && x > 1280 - w)
{
float add = ((1280 - (float)w) - (float)x) / ui::animScale;
x += ceil(add);
}
else if(!open && x < 1280)
{
float add = (1280 - (float)x) / ui::animScale;
x += ceil(add);
}
//don't waste time drawing if you can't even see it.
if(x < 1280)
{
(*drawFunc)(panel);
gfx::texDraw(NULL, panel, x, y);
}
}
//Todo make less hardcoded
void ui::titleTile::draw(SDL_Texture *target, int x, int y, bool sel)
{
unsigned xScale = w * 1.28, yScale = h * 1.28;
if(sel)
{
if(wS < xScale)
wS += 18;
if(hS < yScale)
hS += 18;
}
else
{
if(wS > w)
wS -= 9;
if(hS > h)
hS -= 9;
}
int dX = x - ((wS - w) / 2);
int dY = y - ((hS - h) / 2);
gfx::texDrawStretch(target, icon, dX, dY, wS, hS);
if(fav)
gfx::drawTextf(target, 20, dX + 8, dY + 8, &ui::heartColor, "");
}
ui::titleview::titleview(const data::user& _u, int _iconW, int _iconH, int _horGap, int _vertGap, int _rowCount, funcPtr _callback)
{
iconW = _iconW;
iconH = _iconH;
horGap = _horGap;
vertGap = _vertGap;
rowCount = _rowCount;
callback = _callback;
u = &_u;
for(const data::userTitleInfo& t : u->titleInfo)
tiles.emplace_back(new ui::titleTile(_iconW, _iconH, data::isFavorite(t.saveID), data::getTitleIconByTID(t.saveID)));
}
ui::titleview::~titleview()
{
for(ui::titleTile *t : tiles)
delete t;
}
void ui::titleview::refresh()
{
for(ui::titleTile *t : tiles)
delete t;
tiles.clear();
for(const data::userTitleInfo& t : u->titleInfo)
tiles.emplace_back(new ui::titleTile(iconW, iconH, data::isFavorite(t.saveID), data::getTitleIconByTID(t.saveID)));
}
void ui::titleview::update()
{
if(selected > (int)tiles.size() - 1 && selected > 0)
selected = tiles.size() - 1;
if(!active)
return;
switch(ui::padKeysDown())
{
case HidNpadButton_StickLUp:
case HidNpadButton_StickRUp:
case HidNpadButton_Up:
if((selected -= rowCount) < 0)
selected = 0;
break;
case HidNpadButton_StickLDown:
case HidNpadButton_StickRDown:
case HidNpadButton_Down:
if((selected += rowCount) > (int)(tiles.size() - 1))
selected = tiles.size() - 1;
break;
case HidNpadButton_StickLLeft:
case HidNpadButton_StickRLeft:
case HidNpadButton_Left:
if(selected > 0)
--selected;
break;
case HidNpadButton_StickLRight:
case HidNpadButton_StickRRight:
case HidNpadButton_Right:
if(selected < (int)(tiles.size() - 1))
++selected;
break;
case HidNpadButton_L:
if((selected -= rowCount * 3) < 0)
selected = 0;
break;
case HidNpadButton_R:
if((selected += rowCount * 3) > (int)(tiles.size() - 1))
selected = tiles.size() - 1;
break;
}
if(callback)
(*callback)(this);
}
void ui::titleview::draw(SDL_Texture *target)
{
if(tiles.size() <= 0)
return;
int tH = 0, tY = 0;
SDL_QueryTexture(target, NULL, NULL, NULL, &tH);
tY = tH - 214;
if(selRectY > tY)
{
float add = ((float)tY - (float)selRectY) / ui::animScale;
y += ceil(add);
}
else if(selRectY < 38)
{
float add = (38.0f - (float)selRectY) / ui::animScale;
y += ceil(add);
}
if(clrAdd)
{
clrShft += 6;
if(clrShft >= 0x72)
clrAdd = false;
}
else
{
clrShft -= 3;
if(clrShft <= 0)
clrAdd = true;
}
int totalTitles = tiles.size(), selX = 32, selY = 64;
for(int tY = y, i = 0; i < totalTitles; tY += iconH + vertGap)
{
int endRow = i + rowCount;
for(int tX = x; i < endRow; tX += iconW + horGap, i++)
{
if(i >= totalTitles)
break;
if(i == selected)
{
//save x and y for later so it's draw over top
selX = tX;
selY = tY;
selRectX = tX - 24;
selRectY = tY - 24;
}
else
tiles[i]->draw(target, tX, tY, false);
}
}
if(showSel)
{
ui::drawBoundBox(target, selRectX, selRectY, 176, 176, clrShft);
tiles[selected]->draw(target, selX, selY, true);
}
else
tiles[selected]->draw(target, selX, selY, false);
ui::drawTextbox(NULL, 320, 262, 640, 256);
gfx::drawRect(NULL, &fillBack, 352, 464, 576, 32);
gfx::drawRect(NULL, &fillBar, 352, 464, (int)width, 32);
gfx::drawTextf(NULL, 18, progX, 471, &ui::txtCont, progStr);
gfx::drawTextfWrap(NULL, 16, 352, 288, 576, &ui::txtCont, text.c_str());
}
ui::popMessageMngr::~popMessageMngr()
@ -532,81 +313,10 @@ void ui::popMessageMngr::draw()
p.y += (y - p.y) / ui::animScale;
gfx::drawRect(NULL, &ui::tboxClr, 64, p.y, p.rectWidth, 40);
gfx::drawTextf(NULL, 24, 80, p.y + 8, &ui::txtDiag, p.message.c_str());
gfx::drawTextf(NULL, 24, 80, p.y + 8, &ui::txtCont, p.message.c_str());
}
}
ui::threadProcMngr::~threadProcMngr()
{
for(threadInfo *t : threads)
{
threadWaitForExit(t->thrdPtr);
threadClose(t->thrdPtr);
delete t->thrdPtr;
delete t->status;
delete t;
}
}
void ui::threadProcMngr::newThread(ThreadFunc func, void *args)
{
threadInfo *t = new threadInfo;
t->thrdPtr = new Thread;
t->status = new std::string;
t->finished = false;
t->argPtr = args;
if(R_SUCCEEDED(threadCreate(t->thrdPtr, func, t, NULL, 0x8000, 0x2B, 1)))
{
mutexLock(&threadLock);
threads.push_back(t);
mutexUnlock(&threadLock);
}
else
{
delete t->thrdPtr;
delete t->status;
delete t;
}
}
void ui::threadProcMngr::update()
{
if(!threads.empty())
{
threadInfo *t = threads[0];
if(t->running == false && t->finished == false)
{
t->running = true;
threadStart(t->thrdPtr);
}
else if(t->running == true && t->finished == true)
{
threadWaitForExit(t->thrdPtr);
threadClose(t->thrdPtr);
delete t->thrdPtr;
delete t->status;
delete t;
mutexLock(&threadLock);
threads.erase(threads.begin(), threads.begin() + 1);
mutexUnlock(&threadLock);
}
}
}
void ui::threadProcMngr::draw()
{
if(++frameCount % 4 == 0 && ++lgFrame > 7)
lgFrame = 0;
gfx::drawRect(NULL, &darkenBack, 0, 0, 1280, 720);
gfx::drawTextf(NULL, 32, 56, 673, &ui::loadGlyphClr, loadGlyphArray[lgFrame].c_str());
mutexLock(&threads[0]->statusLock);
int statX = 640 - (gfx::getTextWidth(threads[0]->status->c_str(), 18) / 2);
gfx::drawTextf(NULL, 18, statX, 387, &ui::txtCont, threads[0]->status->c_str());
mutexUnlock(&threads[0]->statusLock);
}
void ui::showMessage(const char *head, const char *fmt, ...)
{
char tmp[1024];
@ -634,7 +344,7 @@ void ui::showMessage(const char *head, const char *fmt, ...)
ui::updateInput();
}
bool ui::confirm(bool hold, const char *fmt, ...)
ui::confirmArgs *ui::confirmArgsCreate(bool _hold, funcPtr _func, void *_funcArgs, bool _cleanup, const char *fmt, ...)
{
char tmp[1024];
va_list args;
@ -642,100 +352,57 @@ bool ui::confirm(bool hold, const char *fmt, ...)
vsprintf(tmp, fmt, args);
va_end(args);
bool ret = false, heldDown = false;
unsigned loadFrame = 0, holdCount = 0;
uint8_t holdClrDiff = 0;
SDL_Color holdClr = ui::txtDiag;
unsigned headX = (640 / 2) - (gfx::getTextWidth(ui::confirmHead.c_str(), 20) / 2);
unsigned yesX = 160 - (gfx::getTextWidth(ui::yt.c_str(), 20) / 2);
unsigned noX = 160 - (gfx::getTextWidth(ui::nt.c_str(), 20) / 2);
std::string yesText = yt;
while(true)
{
ui::updateInput();
uint64_t down = ui::padKeysDown();
uint64_t held = ui::padKeysHeld();
if(hold && held & HidNpadButton_A)
{
heldDown = true;
holdCount++, holdClrDiff++;
if(holdCount % 4 == 0)
{
loadFrame++;
if(loadFrame > 7)
loadFrame = 0;
}
if(holdCount >= 120)
{
ret = true;
break;
}
if(holdCount <= 40)
yesText = ui::holdingText[0];
else if(holdCount <= 80)
yesText = ui::holdingText[1];
else if(holdCount < 120)
yesText = ui::holdingText[2];
yesText += loadGlyphArray[loadFrame];
yesX = 160 - (gfx::getTextWidth(yesText.c_str(), 20) / 2);
}
else if(hold && heldDown)
{
//Reset everything
heldDown= false;
holdCount = 0, loadFrame = 0, holdClrDiff = 0;
yesX = 160 - (gfx::getTextWidth(ui::yt.c_str(), 20) / 2);
yesText = ui::yt;
holdClr = ui::txtDiag;
}
else if(down & HidNpadButton_A)
{
ret = true;
break;
}
else if(down & HidNpadButton_B)
{
ret = false;
break;
}
if(hold && heldDown)
{
if(ui::thmID == ColorSetId_Light)
holdClr = {0xFF, (uint8_t)(0xFF - holdClrDiff), (uint8_t)(0xFF - holdClrDiff), 0xFF};
else
holdClr = {(uint8_t)(0x25 + holdClrDiff), 0x00, 0x00, 0xFF};
}
ui::drawTextbox(NULL, 320, 150, 640, 420);
gfx::drawTextf(NULL, 20, 320 + headX, 168, &ui::txtDiag, ui::confirmHead.c_str());
gfx::drawTextf(NULL, 20, 320 + yesX, 522, &holdClr, yesText.c_str());
gfx::drawTextf(NULL, 20, 640 + noX, 522, &ui::txtDiag, ui::nt.c_str());
gfx::drawTextfWrap(NULL, 16, 352, 230, 576, &ui::txtDiag, tmp);
gfx::present();
}
ui::updateInput();
ui::confirmArgs *ret = new confirmArgs;
ret->hold = _hold;
ret->func = _func;
ret->args = _funcArgs;
ret->cleanup = _cleanup;
ret->text = tmp;
return ret;
}
bool ui::confirmTransfer(const std::string& f, const std::string& t)
//todo: more inline with original
void confirm_t(void *a)
{
return confirm(false, ui::confCopy.c_str(), f.c_str(), t.c_str());
threadInfo *t = (threadInfo *)a;
ui::confirmArgs *c = (ui::confirmArgs *)t->argPtr;
while(true)
{
if(ui::padKeysDown() & HidNpadButton_A)
{
ui::newThread(c->func, c->args, NULL);
break;
}
else if(ui::padKeysDown() & HidNpadButton_B)
break;
svcSleepThread(1000000);
}
if(c->cleanup)
delete c;
t->finished = true;
}
bool ui::confirmDelete(const std::string& p)
//For now this will work
void confirmDrawFunc(void *a)
{
return confirm(data::config["holdDel"], ui::confDel.c_str(), p.c_str());
threadInfo *t = (threadInfo *)a;
ui::confirmArgs *c = (ui::confirmArgs *)t->argPtr;
if(!t->finished)
{
ui::drawTextbox(NULL, 320, 262, 640, 256);
gfx::drawTextfWrap(NULL, 16, 352, 288, 576, &ui::txtCont, c->text.c_str());
gfx::drawTextf(NULL, 18, 320 + 128, 476, &ui::txtCont, ui::yt.c_str());
gfx::drawTextf(NULL, 18, 960 - 208, 476, &ui::txtCont, ui::nt.c_str());
}
}
void ui::confirm(void *a)
{
ui::newThread(confirm_t, a, confirmDrawFunc);
}
void ui::drawTextbox(SDL_Texture *target, int x, int y, int w, int h)

48
src/ui/sldpanel.cpp Normal file
View File

@ -0,0 +1,48 @@
#include <SDL.h>
#include "ui.h"
#include "gfx.h"
ui::slideOutPanel::slideOutPanel(int _w, int _h, int _y, funcPtr _draw)
{
w = _w;
h = _h;
y = _y;
drawFunc = _draw;
panel = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, w, h);
SDL_SetTextureBlendMode(panel, SDL_BLENDMODE_BLEND);
}
ui::slideOutPanel::~slideOutPanel()
{
SDL_DestroyTexture(panel);
}
void ui::slideOutPanel::update()
{
if(open && callback)
(*callback)(cbArgs);
}
void ui::slideOutPanel::draw(const SDL_Color *backCol)
{
gfx::clearTarget(panel, backCol);
if(open && x > 1280 - w)
{
float add = ((1280 - (float)w) - (float)x) / ui::animScale;
x += ceil(add);
}
else if(!open && x < 1280)
{
float add = (1280 - (float)x) / ui::animScale;
x += ceil(add);
}
//don't waste time drawing if you can't even see it.
if(x < 1280)
{
(*drawFunc)(panel);
gfx::texDraw(NULL, panel, x, y);
}
}

83
src/ui/thrdProc.cpp Normal file
View File

@ -0,0 +1,83 @@
#include <switch.h>
#include <vector>
#include "ui.h"
#include "gfx.h"
static const SDL_Color darkenBack = {0x00, 0x00, 0x00, 0xBB};
ui::threadProcMngr::~threadProcMngr()
{
for(threadInfo *t : threads)
{
threadWaitForExit(&t->thrd);
threadClose(&t->thrd);
delete t->status;
delete t;
}
}
threadInfo *ui::threadProcMngr::newThread(ThreadFunc func, void *args, funcPtr _drawfunc)
{
threadInfo *t = new threadInfo;
t->status = new threadStatus;
t->finished = false;
t->drawFunc = _drawfunc;
t->argPtr = args;
if(R_SUCCEEDED(threadCreate(&t->thrd, func, t, NULL, 0x8000, 0x2B, 1)))
{
mutexLock(&threadLock);
threads.push_back(t);
mutexUnlock(&threadLock);
return threads[threads.size() - 1];
}
else
{
delete t->status;
delete t;
}
return NULL;
}
void ui::threadProcMngr::update()
{
if(!threads.empty())
{
threadInfo *t = threads[0];
if(t->running == false && t->finished == false)
{
t->running = true;
threadStart(&t->thrd);
}
else if(t->running == true && t->finished == true)
{
threadWaitForExit(&t->thrd);
threadClose(&t->thrd);
delete t->status;
delete t;
mutexLock(&threadLock);
threads.erase(threads.begin());
mutexUnlock(&threadLock);
}
}
}
void ui::threadProcMngr::draw()
{
if(++frameCount % 4 == 0 && ++lgFrame > 7)
lgFrame = 0;
gfx::drawRect(NULL, &darkenBack, 0, 0, 1280, 720);
gfx::drawTextf(NULL, 32, 56, 673, &ui::loadGlyphClr, loadGlyphArray[lgFrame].c_str());
if(threads[0]->drawFunc)
(*(threads[0]->drawFunc))(threads[0]);
else
{
std::string gStatus;
threads[0]->status->getStatus(gStatus);
int statX = 640 - (gfx::getTextWidth(gStatus.c_str(), 18) / 2);
gfx::drawTextf(NULL, 18, statX, 387, &ui::txtCont, gStatus.c_str());
}
}

View File

@ -11,6 +11,7 @@ static ui::menu *ttlOpts, *fldMenu;
static ui::slideOutPanel *ttlOptsPanel, *infoPanel, *fldPanel;//There's no reason to have a separate folder section
static fs::dirList *fldList;
static fs::backupArgs *backargs;
static ui::confirmArgs *overwriteArgs, *deleteArgs, *restArgs;
static std::string infoPanelString;
void ui::refreshAllViews()
@ -30,6 +31,10 @@ void ui::populateFldMenu()
fs::loadPathFilters(targetDir + "pathFilters.txt");
*backargs = {fldMenu, fldList};
//todo: text stuff
*overwriteArgs = {"Overwrite?", data::config["holdOver"], false, fs::overwriteBackup, backargs};
*deleteArgs = {"Delete?", data::config["holdDel"], false, fs::deleteBackup, backargs};
*restArgs = {"Restore?", data::config["holdRest"], false, fs::restoreBackup, backargs};
fldMenu->addOpt(NULL, "New");
fldMenu->optAddButtonEvent(0, HidNpadButton_A, fs::createNewBackup, backargs);
@ -38,9 +43,9 @@ void ui::populateFldMenu()
{
fldMenu->addOpt(NULL, fldList->getItem(i));
fldMenu->optAddButtonEvent(i + 1, HidNpadButton_A, fs::overwriteBackup, backargs);
fldMenu->optAddButtonEvent(i + 1, HidNpadButton_X, fs::deleteBackup, backargs);
fldMenu->optAddButtonEvent(i + 1, HidNpadButton_Y, fs::restoreBackup, backargs);
fldMenu->optAddButtonEvent(i + 1, HidNpadButton_A, confirm, overwriteArgs);
fldMenu->optAddButtonEvent(i + 1, HidNpadButton_X, confirm, deleteArgs);
fldMenu->optAddButtonEvent(i + 1, HidNpadButton_Y, confirm, restArgs);
}
fldMenu->setActive(true);
@ -115,38 +120,46 @@ static void ttlOptsShowInfoPanel(void *a)
static void ttlOptsBlacklistTitle(void *a)
{
std::string title = data::getTitleNameByTID(data::curData.saveID);
if(ui::confirm(false, ui::confBlacklist.c_str(), title.c_str()))
{
data::blacklistAdd(data::curData.saveID);
ui::refreshAllViews();
}
ui::confirmArgs *conf = ui::confirmArgsCreate(false, NULL, NULL, true, ui::confBlacklist.c_str(), title.c_str());
ui::confirm(conf);
}
static void ttlOptsDefinePath(void *a)
{
uint64_t tid = data::curData.saveID;
std::string safeTitle = data::getTitleInfoByTID(tid)->safeTitle;
std::string newSafeTitle = util::getStringInput(safeTitle, "Input New Output Folder", 0x200, 0, NULL);
std::string newSafeTitle = util::getStringInput(SwkbdType_QWERTY, safeTitle, "Input New Output Folder", 0x200, 0, NULL);
if(!newSafeTitle.empty())
data::pathDefAdd(tid, newSafeTitle);
}
static void ttlOptsResetSaveData_t(void *a)
{
threadInfo *t = (threadInfo *)a;
std::string title = data::getTitleNameByTID(data::curData.saveID);
t->status->setStatus("Resetting save data for " + title + "...");
fs::mountSave(data::curData.saveInfo);
fs::delDir("sv:/");
fsdevCommitDevice("sv:/");
fs::unmountSave();
ui::showPopMessage(POP_FRAME_DEFAULT, ui::saveDataResetSuccess.c_str(), title.c_str());
t->finished = true;
}
static void ttlOptsResetSaveData(void *a)
{
std::string title = data::getTitleNameByTID(data::curData.saveID);
if(ui::confirm(data::config["holdDel"], ui::saveDataReset.c_str(), title.c_str()) && fs::mountSave(data::curData.saveInfo))
{
fs::delDir("sv:/");
fsdevCommitDevice("sv:/");
fs::unmountSave();
ui::showPopMessage(POP_FRAME_DEFAULT, ui::saveDataResetSuccess.c_str(), title.c_str());
}
ui::confirmArgs *conf = ui::confirmArgsCreate(data::config["holdDel"], ttlOptsResetSaveData_t, NULL, true, ui::saveDataReset.c_str(), title.c_str());
ui::confirm(conf);
}
static void ttlOptsDeleteSaveData(void *a)
static void ttlOptsDeleteSaveData_t(void *a)
{
threadInfo *t = (threadInfo *)a;
std::string title = data::getTitleNameByTID(data::curData.saveID);
if(ui::confirm(data::config["holdDel"], ui::confEraseNand.c_str(), title.c_str()) && R_SUCCEEDED(fsDeleteSaveDataFileSystemBySaveDataSpaceId((FsSaveDataSpaceId)data::curData.saveInfo.save_data_space_id, data::curData.saveInfo.save_data_id)))
t->status->setStatus("Deleting save data for " + title + "...");
if(R_SUCCEEDED(fsDeleteSaveDataFileSystemBySaveDataSpaceId((FsSaveDataSpaceId)data::curData.saveInfo.save_data_space_id, data::curData.saveInfo.save_data_id)))
{
data::loadUsersTitles(false);
if(data::curUser.titleInfo.size() == 0)
@ -161,25 +174,61 @@ static void ttlOptsDeleteSaveData(void *a)
ui::showPopMessage(POP_FRAME_DEFAULT, ui::saveDataDeleteSuccess.c_str(), title.c_str());
ttlViews[data::selUser]->refresh();
}
t->finished = true;
}
static void ttlOptsDeleteSaveData(void *a)
{
std::string title = data::getTitleNameByTID(data::curData.saveID);
ui::confirmArgs *conf = ui::confirmArgsCreate(data::config["holdDel"], ttlOptsDeleteSaveData_t, NULL, true, ui::confEraseNand.c_str(), title.c_str());
ui::confirm(conf);
}
static void ttlOptsExtendSaveData_t(void *a)
{
threadInfo *w = (threadInfo *)a;
std::string expSizeStr = util::getStringInput("", "Enter New Size in MB", 4, 0, NULL);
std::string expSizeStr = util::getStringInput(SwkbdType_NumPad, "", "Enter New Size in MB", 4, 0, NULL);
if(!expSizeStr.empty())
{
data::titleInfo *extend = data::getTitleInfoByTID(data::curData.saveID);
w->updateStatus("Expanding save filesystem for " + extend->title);
w->status->setStatus("Expanding save filesystem for " + extend->title);
uint64_t expMB = strtoul(expSizeStr.c_str(), NULL, 10);
FsSaveDataSpaceId space = (FsSaveDataSpaceId)data::curData.saveInfo.save_data_space_id;
uint64_t sid = data::curData.saveInfo.save_data_id;
int64_t expSize = expMB * 1024 * 1024;
int64_t journ = 0;
if(extend->nacp.user_account_save_data_journal_size_max > extend->nacp.user_account_save_data_journal_size)
journ = extend->nacp.user_account_save_data_journal_size_max;
else
journ = extend->nacp.user_account_save_data_journal_size;
switch(data::curData.saveInfo.save_data_type)
{
case FsSaveDataType_Account:
if(extend->nacp.user_account_save_data_journal_size_max > extend->nacp.user_account_save_data_journal_size)
journ = extend->nacp.user_account_save_data_journal_size_max;
else
journ = extend->nacp.user_account_save_data_journal_size;
break;
case FsSaveDataType_Bcat:
journ = extend->nacp.bcat_delivery_cache_storage_size;
break;
case FsSaveDataType_Cache:
if(extend->nacp.cache_storage_data_and_journal_size_max > extend->nacp.cache_storage_journal_size)
journ = extend->nacp.cache_storage_data_and_journal_size_max;
else
journ = extend->nacp.cache_storage_journal_size;
break;
case FsSaveDataType_Device:
if(extend->nacp.device_save_data_journal_size_max > extend->nacp.device_save_data_journal_size)
journ = extend->nacp.device_save_data_journal_size_max;
else
journ = extend->nacp.device_save_data_journal_size;
break;
default:
//will just fail
journ = 0;
break;
}
Result res = 0;
if(R_FAILED(res = fsExtendSaveDataFileSystem(space, sid, expSize, journ)))
@ -198,7 +247,7 @@ static void ttlOptsExtendSaveData_t(void *a)
static void ttlOptsExtendSaveData(void *a)
{
ui::newThread(ttlOptsExtendSaveData_t, NULL);
ui::newThread(ttlOptsExtendSaveData_t, NULL, NULL);
}
static void infoPanelDraw(void *a)
@ -272,6 +321,9 @@ void ui::ttlInit()
fldList = new fs::dirList;
backargs = new fs::backupArgs;
overwriteArgs = new ui::confirmArgs;
deleteArgs = new ui::confirmArgs;
restArgs = new ui::confirmArgs;
ttlOpts->setActive(false);
ttlOpts->addOpt(NULL, ui::titleOptString[0]);
@ -301,6 +353,9 @@ void ui::ttlExit()
delete fldMenu;
delete fldList;
delete backargs;
delete overwriteArgs;
delete deleteArgs;
delete restArgs;
}
void ui::ttlSetActive(int usr)

177
src/ui/ttlview.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "ui.h"
#include "ui/ttlview.h"
//Todo make less hardcoded
void ui::titleTile::draw(SDL_Texture *target, int x, int y, bool sel)
{
unsigned xScale = w * 1.28, yScale = h * 1.28;
if(sel)
{
if(wS < xScale)
wS += 18;
if(hS < yScale)
hS += 18;
}
else
{
if(wS > w)
wS -= 9;
if(hS > h)
hS -= 9;
}
int dX = x - ((wS - w) / 2);
int dY = y - ((hS - h) / 2);
gfx::texDrawStretch(target, icon, dX, dY, wS, hS);
if(fav)
gfx::drawTextf(target, 20, dX + 8, dY + 8, &ui::heartColor, "");
}
ui::titleview::titleview(const data::user& _u, int _iconW, int _iconH, int _horGap, int _vertGap, int _rowCount, funcPtr _callback)
{
iconW = _iconW;
iconH = _iconH;
horGap = _horGap;
vertGap = _vertGap;
rowCount = _rowCount;
callback = _callback;
u = &_u;
for(const data::userTitleInfo& t : u->titleInfo)
tiles.emplace_back(new ui::titleTile(_iconW, _iconH, data::isFavorite(t.saveID), data::getTitleIconByTID(t.saveID)));
}
ui::titleview::~titleview()
{
for(ui::titleTile *t : tiles)
delete t;
}
void ui::titleview::refresh()
{
for(ui::titleTile *t : tiles)
delete t;
tiles.clear();
for(const data::userTitleInfo& t : u->titleInfo)
tiles.emplace_back(new ui::titleTile(iconW, iconH, data::isFavorite(t.saveID), data::getTitleIconByTID(t.saveID)));
if(selected > (int)tiles.size() - 1 && selected > 0)
selected = tiles.size() - 1;
}
void ui::titleview::update()
{
if(selected > (int)tiles.size() - 1 && selected > 0)
selected = tiles.size() - 1;
if(!active)
return;
switch(ui::padKeysDown())
{
case HidNpadButton_StickLUp:
case HidNpadButton_StickRUp:
case HidNpadButton_Up:
if((selected -= rowCount) < 0)
selected = 0;
break;
case HidNpadButton_StickLDown:
case HidNpadButton_StickRDown:
case HidNpadButton_Down:
if((selected += rowCount) > (int)(tiles.size() - 1))
selected = tiles.size() - 1;
break;
case HidNpadButton_StickLLeft:
case HidNpadButton_StickRLeft:
case HidNpadButton_Left:
if(selected > 0)
--selected;
break;
case HidNpadButton_StickLRight:
case HidNpadButton_StickRRight:
case HidNpadButton_Right:
if(selected < (int)(tiles.size() - 1))
++selected;
break;
case HidNpadButton_L:
if((selected -= rowCount * 3) < 0)
selected = 0;
break;
case HidNpadButton_R:
if((selected += rowCount * 3) > (int)(tiles.size() - 1))
selected = tiles.size() - 1;
break;
}
if(callback)
(*callback)(this);
}
void ui::titleview::draw(SDL_Texture *target)
{
if(tiles.size() <= 0)
return;
int tH = 0, tY = 0;
SDL_QueryTexture(target, NULL, NULL, NULL, &tH);
tY = tH - 214;
if(selRectY > tY)
{
float add = ((float)tY - (float)selRectY) / ui::animScale;
y += ceil(add);
}
else if(selRectY < 38)
{
float add = (38.0f - (float)selRectY) / ui::animScale;
y += ceil(add);
}
if(clrAdd)
{
clrShft += 6;
if(clrShft >= 0x72)
clrAdd = false;
}
else
{
clrShft -= 3;
if(clrShft <= 0)
clrAdd = true;
}
int totalTitles = tiles.size(), selX = 32, selY = 64;
for(int tY = y, i = 0; i < totalTitles; tY += iconH + vertGap)
{
int endRow = i + rowCount;
for(int tX = x; i < endRow; tX += iconW + horGap, i++)
{
if(i >= totalTitles)
break;
if(i == selected)
{
//save x and y for later so it's draw over top
selX = tX;
selY = tY;
selRectX = tX - 24;
selRectY = tY - 24;
}
else
tiles[i]->draw(target, tX, tY, false);
}
}
if(showSel)
{
ui::drawBoundBox(target, selRectX, selRectY, 176, 176, clrShft);
tiles[selected]->draw(target, selX, selY, true);
}
else
tiles[selected]->draw(target, selX, selY, false);
}

View File

@ -135,7 +135,7 @@ static void usrOptDeleteAllUserSaves_t(void *a)
data::user *u = &data::users[data::selUser];
for(data::userTitleInfo& tinf : u->titleInfo)
{
*t->status = "Deleting " + data::getTitleNameByTID(tinf.saveID);
t->status->setStatus("Deleting " + data::getTitleNameByTID(tinf.saveID));
fsDeleteSaveDataFileSystemBySaveDataSpaceId(FsSaveDataSpaceId_User, tinf.saveInfo.save_data_id);;
}
data::loadUsersTitles(false);
@ -146,8 +146,8 @@ static void usrOptDeleteAllUserSaves_t(void *a)
static void usrOptDeleteAllUserSaves(void *a)
{
data::user *u = &data::users[data::selUser];
if(ui::confirm(true, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*", u->getUsername().c_str()))
ui::newThread(usrOptDeleteAllUserSaves_t, NULL);
ui::confirmArgs *conf = ui::confirmArgsCreate(true, usrOptDeleteAllUserSaves_t, NULL, true, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*", u->getUsername().c_str());
ui::confirm(conf);
}
static void usrOptPanelDraw(void *a)
@ -211,7 +211,7 @@ static void createSaveData_t(void *a)
break;
}
data::titleInfo *create = data::getTitleInfoByTID(sid);
t->updateStatus("Creating save data for " + create->title);
t->status->setStatus("Creating save data for " + create->title);
FsSaveDataAttribute attr;
memset(&attr, 0, sizeof(FsSaveDataAttribute));
@ -220,7 +220,14 @@ static void createSaveData_t(void *a)
attr.system_save_data_id = 0;
attr.save_data_type = type;
attr.save_data_rank = 0;
attr.save_data_index = 0;//Todo: Let user input this
uint16_t index = 0;
if(type == FsSaveDataType_Cache)
{
std::string getIndex = util::getStringInput(SwkbdType_NumPad, "", "Input Cache save index", 2, 0, NULL);
index = strtoul(getIndex.c_str(), 0, 10);
}
attr.save_data_index = index;
FsSaveDataCreationInfo svCreate;
memset(&svCreate, 0, sizeof(FsSaveDataCreationInfo));
@ -239,7 +246,7 @@ static void createSaveData_t(void *a)
case FsSaveDataType_Bcat:
saveSize = create->nacp.bcat_delivery_cache_storage_size;
journalSize = create->nacp.bcat_delivery_cache_storage_size;//This needs to be fixed
journalSize = create->nacp.bcat_delivery_cache_storage_size;
break;
case FsSaveDataType_Cache:
@ -280,7 +287,7 @@ static void createSaveData_t(void *a)
static void createSaveData(void *a)
{
ui::newThread(createSaveData_t, a);
ui::newThread(createSaveData_t, a, NULL);
}
void ui::usrInit()

View File

@ -226,6 +226,7 @@ std::string util::getInfoString(data::user& u, const uint64_t& tid)
case FsSaveDataType_Cache:
ret += "Cache Storage\n";
ret += "Save Index: " + std::to_string(data::curData.saveInfo.save_data_index) + "\n";
break;
case FsSaveDataType_SystemBcat:
@ -238,7 +239,7 @@ std::string util::getInfoString(data::user& u, const uint64_t& tid)
return ret;
}
std::string util::getStringInput(const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[])
std::string util::getStringInput(SwkbdType _type, const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[])
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, dictCnt);
@ -247,7 +248,7 @@ std::string util::getStringInput(const std::string& def, const std::string& head
swkbdConfigSetHeaderText(&swkbd, head.c_str());
swkbdConfigSetGuideText(&swkbd, head.c_str());
swkbdConfigSetInitialCursorPos(&swkbd, SwkbdPosEnd);
swkbdConfigSetType(&swkbd, SwkbdType_QWERTY);
swkbdConfigSetType(&swkbd, _type);
swkbdConfigSetStringLenMax(&swkbd, maxLength);
swkbdConfigSetKeySetDisableBitmask(&swkbd, SwkbdKeyDisableBitmask_Backslash | SwkbdKeyDisableBitmask_ForwardSlash | SwkbdKeyDisableBitmask_Percent);
swkbdConfigSetDicFlag(&swkbd, 1);