mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-04-27 18:47:16 -05:00
Add drawTextf, drawTextfWrap, Add ZIP export (import later)
This commit is contained in:
parent
749c8fd654
commit
633a97a568
4
Makefile
4
Makefile
|
|
@ -38,7 +38,7 @@ INCLUDES := inc
|
||||||
EXEFS_SRC := exefs_src
|
EXEFS_SRC := exefs_src
|
||||||
APP_TITLE := JKSV
|
APP_TITLE := JKSV
|
||||||
APP_AUTHOR := JK
|
APP_AUTHOR := JK
|
||||||
APP_VERSION := 06.04.2020
|
APP_VERSION := 06.06.2020
|
||||||
ROMFS := romfs
|
ROMFS := romfs
|
||||||
ICON := romfs/icon.jpg
|
ICON := romfs/icon.jpg
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := `freetype-config --libs` -lpng -ljpeg -lnx
|
LIBS := `freetype-config --libs` -lpng -ljpeg -lz -lminizip -lnx
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
|
|
||||||
15
README.MD
15
README.MD
|
|
@ -9,12 +9,12 @@ JKSV on Switch started as a small project/port to test some things and get famil
|
||||||
1. Dump and restore save data.
|
1. Dump and restore save data.
|
||||||
* This includes the ability to dump and restore to/from any location on SD by pressing minus and using the Advanced Mode.
|
* This includes the ability to dump and restore to/from any location on SD by pressing minus and using the Advanced Mode.
|
||||||
2. Dump system save data
|
2. Dump system save data
|
||||||
* Dumping this data is allowed, but writing back is not.
|
* Dumping this data is always enabled, but writing back needs to be enabled from the options menu. Writing to this can be very dangerous.
|
||||||
3. Open and explore bis storage partitions via the Extras menu
|
3. Open and explore bis storage partitions via the Extras menu
|
||||||
* BIS Storage is opened inside a basic filebrowser. The partition's listing is on the left. Your SD is on the right.
|
* BIS Storage is opened inside a basic filebrowser. The partition's listing is on the left. Your SD is on the right.
|
||||||
* Only copying to SD and file properties work on BIS partitions. Writing to and deleting are disabled for now.
|
* Only copying to SD and file properties work on BIS partitions. Writing to and deleting are disabled unless enabled like system save data.
|
||||||
4. Misc Extras:
|
4. Misc Extras:
|
||||||
* Ability to remove downloaded firmware updates from NAND.
|
* Ability to remove downloaded firmware updates from NAND. This is located in the extras menu (ZR on User selection)
|
||||||
* Terminating processes by [ID](https://switchbrew.org/wiki/Title_list#System_Modules). Allowing you to dump normally unopenable system archives.
|
* Terminating processes by [ID](https://switchbrew.org/wiki/Title_list#System_Modules). Allowing you to dump normally unopenable system archives.
|
||||||
* Mount by System Save [ID](https://switchbrew.org/wiki/Flash_Filesystem#System_Savegames). Normally used when the terminated process makes JKSV unable to rescan titles without the Switch crashing.
|
* Mount by System Save [ID](https://switchbrew.org/wiki/Flash_Filesystem#System_Savegames). Normally used when the terminated process makes JKSV unable to rescan titles without the Switch crashing.
|
||||||
* Mount and open RomFS of process the homebrew menu takes over (if launched as NRO).
|
* Mount and open RomFS of process the homebrew menu takes over (if launched as NRO).
|
||||||
|
|
@ -59,6 +59,15 @@ JKSV on Switch started as a small project/port to test some things and get famil
|
||||||
* Make Dir creates a folder.
|
* Make Dir creates a folder.
|
||||||
* Properties gets file size and directory size.
|
* Properties gets file size and directory size.
|
||||||
* ZL or ZR Change the controlled menu.
|
* ZL or ZR Change the controlled menu.
|
||||||
|
|
||||||
|
5. Extras
|
||||||
|
* SD To SD Browser opens the filebrowser with your SD open in both panels
|
||||||
|
* BIS: [X] opens partition [X] in the filebrowser.
|
||||||
|
* Remove Update deletes system updates downloaded from Nintendo and asks to reboot the system to get rid of the update nag.
|
||||||
|
* Terminate Process asks for a title ID to terminate.
|
||||||
|
* Mount System Save asks for the save ID to mount. This is for when JKSV is unable to rescan with a process terminated.
|
||||||
|
* Rescan Titles reloads save data information. This can be used to reload after a process is terminated or when options are changed.
|
||||||
|
* Mount Process RomFS opens the title's romfs that is taken over to launch the homebrew menu. This only works as an NRO. The NSP will only open JKSV's own RomFS.
|
||||||
|
|
||||||
**NOTE**: Press Plus to Exit JKSV. JKSV saves all config, favorites, and the blacklist when exited. Pressing the home button and closing that way will not allow this to take place.
|
**NOTE**: Press Plus to Exit JKSV. JKSV saves all config, favorites, and the blacklist when exited. Pressing the home button and closing that way will not allow this to take place.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ namespace data
|
||||||
void saveFav();
|
void saveFav();
|
||||||
void loadDefs();
|
void loadDefs();
|
||||||
|
|
||||||
|
//Draws some stats to the upper left corner
|
||||||
|
void dispStats();
|
||||||
|
|
||||||
//Class to store title info
|
//Class to store title info
|
||||||
class titledata
|
class titledata
|
||||||
{
|
{
|
||||||
|
|
@ -121,7 +124,7 @@ namespace data
|
||||||
void restoreDefaultConfig();
|
void restoreDefaultConfig();
|
||||||
extern int selUser, selData;
|
extern int selUser, selData;
|
||||||
extern SetLanguage sysLang;
|
extern SetLanguage sysLang;
|
||||||
extern bool incDev, autoBack, ovrClk, holdDel, holdRest, holdOver, forceMount, accSysSave, sysSaveWrite, directFsCmd, skipUser;
|
extern bool incDev, autoBack, ovrClk, holdDel, holdRest, holdOver, forceMount, accSysSave, sysSaveWrite, directFsCmd, skipUser, zip;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DATA_H
|
#endif // DATA_H
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <minizip/zip.h>
|
||||||
|
|
||||||
#include "fsfile.h"
|
#include "fsfile.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
@ -25,6 +26,9 @@ namespace fs
|
||||||
//Recursively copies 'from' to 'to'
|
//Recursively copies 'from' to 'to'
|
||||||
void copyDirToDir(const std::string& from, const std::string& to);
|
void copyDirToDir(const std::string& from, const std::string& to);
|
||||||
|
|
||||||
|
//Copies from to zipFile to
|
||||||
|
void copyDirToZip(const std::string& from, zipFile *to);
|
||||||
|
|
||||||
//Same as above, but commits data to 'dev' after every file is closed
|
//Same as above, but commits data to 'dev' after every file is closed
|
||||||
void copyDirToDirCommit(const std::string& from, const std::string& to, const std::string& dev);
|
void copyDirToDirCommit(const std::string& from, const std::string& to, const std::string& dev);
|
||||||
|
|
||||||
|
|
|
||||||
47
inc/fsfile.h
47
inc/fsfile.h
|
|
@ -38,25 +38,60 @@ FSFILE *fsfopen(const char *_p, uint32_t mode);
|
||||||
FSFILE *fsfopenWithSystem(FsFileSystem *_s, const char *_p, uint32_t mode);
|
FSFILE *fsfopenWithSystem(FsFileSystem *_s, const char *_p, uint32_t mode);
|
||||||
|
|
||||||
//Closes _f
|
//Closes _f
|
||||||
void fsfclose(FSFILE *_f);
|
inline void fsfclose(FSFILE *_f)
|
||||||
|
{
|
||||||
|
if(_f != NULL)
|
||||||
|
{
|
||||||
|
fsFileClose(&_f->_f);
|
||||||
|
free(_f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Seeks like stdio
|
//Seeks like stdio
|
||||||
void fsfseek(FSFILE *_f, int offset, int origin);
|
inline void fsfseek(FSFILE *_f, int offset, int origin)
|
||||||
|
{
|
||||||
|
switch(origin)
|
||||||
|
{
|
||||||
|
case FS_SEEK_SET:
|
||||||
|
_f->offset = offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_SEEK_CUR:
|
||||||
|
_f->offset += offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_SEEK_END:
|
||||||
|
_f->offset = offset + _f->fsize;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Returns offset
|
//Returns offset
|
||||||
size_t fsftell(FSFILE *_f);
|
inline size_t fsftell(FSFILE *_f) { return _f->offset; }
|
||||||
|
|
||||||
//Writes buf to file. Automatically resizes _f to fit buf
|
//Writes buf to file. Automatically resizes _f to fit buf
|
||||||
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f);
|
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f);
|
||||||
|
|
||||||
//Reads to buff
|
//Reads to buff
|
||||||
size_t fsfread(void *buf, size_t sz, size_t count, FSFILE *_f);
|
inline size_t fsfread(void *buf, size_t sz, size_t count, FSFILE *_f)
|
||||||
|
{
|
||||||
|
uint64_t read = 0;
|
||||||
|
_f->error = fsFileRead(&_f->_f, _f->offset, buf, sz * count, 0, &read);
|
||||||
|
_f->offset += read;
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
//Gets byte from file
|
//Gets byte from file
|
||||||
char fsfgetc(FSFILE *_f);
|
inline char fsfgetc(FSFILE *_f)
|
||||||
|
{
|
||||||
|
char ret = 0;
|
||||||
|
uint64_t read = 0;
|
||||||
|
_f->error = fsFileRead(&_f->_f, _f->offset++, &ret, 1, 0, &read);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
//Writes byte to file
|
//Writes byte to file
|
||||||
void fsfputc(int ch, FSFILE *_f);
|
inline void fsfputc(int ch, FSFILE *_f) { fsfwrite(&ch, 1, 1, _f); }
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,11 @@ inline uint32_t clrGetColor(const clr c)
|
||||||
|
|
||||||
//Draws text using f
|
//Draws text using f
|
||||||
void drawText(const char *str, tex *target, const font *f, int x, int y, int sz, clr c);
|
void drawText(const char *str, tex *target, const font *f, int x, int y, int sz, clr c);
|
||||||
|
void drawTextf(tex *target, const font *f, int x, int y, int sz, clr c, const char *fmt, ...);
|
||||||
|
|
||||||
//Draws text wrapping lines
|
//Draws text wrapping lines
|
||||||
void drawTextWrap(const char *str, tex *target, const font *f, int x, int y, int sz, clr c, int maxWidth);
|
void drawTextWrap(const char *str, tex *target, const font *f, int x, int y, int sz, clr c, int maxWidth);
|
||||||
|
void drawTextfWrap(tex *target, const font *f, int x, int y, int sz, clr c, int maxWidth, const char *fmt, ...);
|
||||||
|
|
||||||
//Returns text width
|
//Returns text width
|
||||||
size_t textGetWidth(const char *str, const font *f, int sz);
|
size_t textGetWidth(const char *str, const font *f, int sz);
|
||||||
|
|
|
||||||
4
inc/ui.h
4
inc/ui.h
|
|
@ -51,9 +51,9 @@ namespace ui
|
||||||
//Strings for extras menu
|
//Strings for extras menu
|
||||||
extern std::string exMenuStr[10];
|
extern std::string exMenuStr[10];
|
||||||
//Strings for options menu
|
//Strings for options menu
|
||||||
extern std::string optMenuStr[12];
|
extern std::string optMenuStr[13];
|
||||||
//Strings for options explanations
|
//Strings for options explanations
|
||||||
extern std::string optMenuExp[12];
|
extern std::string optMenuExp[13];
|
||||||
//Strings for the holding thing
|
//Strings for the holding thing
|
||||||
extern std::string holdingText[3];
|
extern std::string holdingText[3];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ optMenu = 8, "Write to Sys. Saves: "
|
||||||
optMenu = 9, "Text UI Mode: "
|
optMenu = 9, "Text UI Mode: "
|
||||||
optMenu = 10, "Direct FS Cmd: "
|
optMenu = 10, "Direct FS Cmd: "
|
||||||
optMenu = 11, "Skip User Select: "
|
optMenu = 11, "Skip User Select: "
|
||||||
|
optMenu = 12, "Export to ZIP: "
|
||||||
|
|
||||||
#Explanations of what options do.
|
#Explanations of what options do.
|
||||||
optMenuExp = 0, "Includes Device Save data in user accounts."
|
optMenuExp = 0, "Includes Device Save data in user accounts."
|
||||||
|
|
@ -113,3 +114,4 @@ optMenuExp = 8, "Controls whether system save data and partitions can have files
|
||||||
optMenuExp = 9, "Changes the UI to be text menu based like the original JKSM for 3DS."
|
optMenuExp = 9, "Changes the UI to be text menu based like the original JKSM for 3DS."
|
||||||
optMenuExp = 10, "Directly uses the Switch's FS commands to copy files instead of stdio."
|
optMenuExp = 10, "Directly uses the Switch's FS commands to copy files instead of stdio."
|
||||||
optMenuExp = 11, "Skips the user selection screen and jumps directly to the first user account found."
|
optMenuExp = 11, "Skips the user selection screen and jumps directly to the first user account found."
|
||||||
|
optMenuExp = 12, "Exports saves to zip files."
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ extMenu = 1, "BIS: PRODINFOF"
|
||||||
extMenu = 2, "BIS: SAFE"
|
extMenu = 2, "BIS: SAFE"
|
||||||
extMenu = 3, "BIS: SYSTEM"
|
extMenu = 3, "BIS: SYSTEM"
|
||||||
extMenu = 4, "BIS: USER"
|
extMenu = 4, "BIS: USER"
|
||||||
extMenu = 5, "撤銷操作與變動"
|
extMenu = 5, "移除系統更新通知"
|
||||||
extMenu = 6, "終止指定程序"
|
extMenu = 6, "終止指定程序"
|
||||||
extMenu = 7, "掛載系統進度"
|
extMenu = 7, "掛載系統進度"
|
||||||
extMenu = 8, "重新掃瞄Titles"
|
extMenu = 8, "重新掃瞄Titles"
|
||||||
|
|
|
||||||
27
src/data.cpp
27
src/data.cpp
|
|
@ -21,7 +21,7 @@ SetLanguage data::sysLang;
|
||||||
|
|
||||||
//Options
|
//Options
|
||||||
bool data::incDev = false, data::autoBack = true, data::ovrClk = false, data::holdDel = true, data::holdRest = true, data::holdOver = true;
|
bool data::incDev = false, data::autoBack = true, data::ovrClk = false, data::holdDel = true, data::holdRest = true, data::holdOver = true;
|
||||||
bool data::forceMount = true, data::accSysSave = false, data::sysSaveWrite = false, data::directFsCmd = false, data::skipUser = false;
|
bool data::forceMount = true, data::accSysSave = false, data::sysSaveWrite = false, data::directFsCmd = false, data::skipUser = false, data::zip = false;
|
||||||
|
|
||||||
//For other save types
|
//For other save types
|
||||||
static bool sysBCATPushed = false, cachePushed = false, tempPushed = false;
|
static bool sysBCATPushed = false, cachePushed = false, tempPushed = false;
|
||||||
|
|
@ -189,6 +189,10 @@ bool data::loadUsersTitles(bool clearUsers)
|
||||||
NsApplicationControlData *dat = new NsApplicationControlData;
|
NsApplicationControlData *dat = new NsApplicationControlData;
|
||||||
while(R_SUCCEEDED(fsSaveDataInfoReaderRead(&it, &info, 1, &total)) && total != 0)
|
while(R_SUCCEEDED(fsSaveDataInfoReaderRead(&it, &info, 1, &total)) && total != 0)
|
||||||
{
|
{
|
||||||
|
//Don't bother with this stuff
|
||||||
|
if(blacklisted(info.application_id) || blacklisted(info.save_data_id) || !accountSystemSaveCheck(info) || !testMount(info))
|
||||||
|
continue;
|
||||||
|
|
||||||
switch(info.save_data_type)
|
switch(info.save_data_type)
|
||||||
{
|
{
|
||||||
case FsSaveDataType_Bcat:
|
case FsSaveDataType_Bcat:
|
||||||
|
|
@ -227,14 +231,10 @@ bool data::loadUsersTitles(bool clearUsers)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Don't bother with this stuff
|
|
||||||
if(blacklisted(info.application_id) || blacklisted(info.save_data_id) || !accountSystemSaveCheck(info) || !testMount(info))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int u = getUserIndex(info.uid);
|
int u = getUserIndex(info.uid);
|
||||||
if(u == -1)
|
if(u == -1)
|
||||||
{
|
{
|
||||||
users.emplace(users.end() - 3, info.uid, "");
|
users.emplace(data::users.end() - 3, info.uid, "");
|
||||||
u = getUserIndex(info.uid);
|
u = getUserIndex(info.uid);
|
||||||
}
|
}
|
||||||
users[u].titles.emplace_back(info, dat);
|
users[u].titles.emplace_back(info, dat);
|
||||||
|
|
@ -481,6 +481,7 @@ void data::loadCfg()
|
||||||
ui::textMode = cfgIn >> 54 & 1;
|
ui::textMode = cfgIn >> 54 & 1;
|
||||||
data::directFsCmd = cfgIn >> 53 & 1;
|
data::directFsCmd = cfgIn >> 53 & 1;
|
||||||
data::skipUser = cfgIn >> 52 & 1;
|
data::skipUser = cfgIn >> 52 & 1;
|
||||||
|
data::zip = cfgIn >> 51 & 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -503,6 +504,7 @@ void data::saveCfg()
|
||||||
cfgOut |= (uint64_t)ui::textMode << 54;
|
cfgOut |= (uint64_t)ui::textMode << 54;
|
||||||
cfgOut |= (uint64_t)data::directFsCmd << 53;
|
cfgOut |= (uint64_t)data::directFsCmd << 53;
|
||||||
cfgOut |= (uint64_t)data::skipUser << 52;
|
cfgOut |= (uint64_t)data::skipUser << 52;
|
||||||
|
cfgOut |= (uint64_t)data::zip << 51;
|
||||||
fwrite(&cfgOut, sizeof(uint64_t), 1, cfg);
|
fwrite(&cfgOut, sizeof(uint64_t), 1, cfg);
|
||||||
|
|
||||||
fclose(cfg);
|
fclose(cfg);
|
||||||
|
|
@ -557,3 +559,16 @@ void data::loadDefs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void data::dispStats()
|
||||||
|
{
|
||||||
|
//Easiest/laziest way to do this
|
||||||
|
std::string stats = "User Count: " + std::to_string(users.size()) + "\n";
|
||||||
|
for(data::user& u : data::users)
|
||||||
|
stats += u.getUsername() + ": " + std::to_string(u.titles.size()) + "\n";
|
||||||
|
stats += "Current User: " + data::curUser.getUsername() + "\n";
|
||||||
|
stats += "Current Title: " + data::curData.getTitle() + "\n";
|
||||||
|
stats += "Safe Title: " + data::curData.getTitleSafe() + "\n";
|
||||||
|
stats += "Icon count: " + std::to_string(icons.size()) + "\n";
|
||||||
|
drawText(stats.c_str(), frameBuffer, ui::shared, 2, 2, 16, clrCreateU32(0xFF00DD00));
|
||||||
|
}
|
||||||
|
|
|
||||||
40
src/file.cpp
40
src/file.cpp
|
|
@ -5,6 +5,7 @@
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
#include <minizip/zip.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
@ -452,6 +453,45 @@ void fs::copyDirToDir(const std::string& from, const std::string& to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void copyFileToZip(const std::string& from, zipFile *to)
|
||||||
|
{
|
||||||
|
FILE *cpy = fopen(from.c_str(), "rb");
|
||||||
|
|
||||||
|
size_t readIn = 0;
|
||||||
|
uint8_t *inBuff= new uint8_t[BUFF_SIZE];
|
||||||
|
while((readIn = fread(inBuff, 1, BUFF_SIZE, cpy)) > 0)
|
||||||
|
{
|
||||||
|
if(zipWriteInFileInZip(*to, inBuff, readIn) != 0)
|
||||||
|
ui::showMessage("fail", "here");
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] inBuff;
|
||||||
|
fclose(cpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyDirToZip(const std::string& from, zipFile *to)
|
||||||
|
{
|
||||||
|
fs::dirList list(from);
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < list.getCount(); i++)
|
||||||
|
{
|
||||||
|
if(list.isDir(i))
|
||||||
|
{
|
||||||
|
std::string newFrom = from + list.getItem(i) + "/";
|
||||||
|
fs::copyDirToZip(newFrom, to);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zip_fileinfo inf = { 0 };
|
||||||
|
std::string filename = from + list.getItem(i);
|
||||||
|
size_t devPos = filename.find_first_of('/') + 1;
|
||||||
|
if(zipOpenNewFileInZip(*to, filename.substr(devPos, filename.length()).c_str(), &inf, NULL, 0, NULL, 0, "", Z_DEFLATED, Z_DEFAULT_COMPRESSION) == ZIP_OK)
|
||||||
|
copyFileToZip(std::string(from) + list.getItem(i).c_str(), to);
|
||||||
|
zipCloseFileInZip(*to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void fs::copyDirToDirCommit(const std::string& from, const std::string& to, const std::string& dev)
|
void fs::copyDirToDirCommit(const std::string& from, const std::string& to, const std::string& dev)
|
||||||
{
|
{
|
||||||
dirList list(from);
|
dirList list(from);
|
||||||
|
|
|
||||||
54
src/fsfile.c
54
src/fsfile.c
|
|
@ -107,38 +107,6 @@ FSFILE *fsfopenWithSystem(FsFileSystem *_s, const char *_p, uint32_t mode)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsfclose(FSFILE *_f)
|
|
||||||
{
|
|
||||||
if(_f != NULL)
|
|
||||||
{
|
|
||||||
fsFileClose(&_f->_f);
|
|
||||||
free(_f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fsfseek(FSFILE *_f, int offset, int origin)
|
|
||||||
{
|
|
||||||
switch(origin)
|
|
||||||
{
|
|
||||||
case FS_SEEK_SET:
|
|
||||||
_f->offset = offset;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FS_SEEK_CUR:
|
|
||||||
_f->offset += offset;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FS_SEEK_END:
|
|
||||||
_f->offset = offset + _f->fsize;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t fsftell(FSFILE *_f)
|
|
||||||
{
|
|
||||||
return _f->offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f)
|
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f)
|
||||||
{
|
{
|
||||||
size_t fullSize = sz * count;
|
size_t fullSize = sz * count;
|
||||||
|
|
@ -153,25 +121,3 @@ size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f)
|
||||||
|
|
||||||
return fullSize;
|
return fullSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t fsfread(void *buf, size_t sz, size_t count, FSFILE *_f)
|
|
||||||
{
|
|
||||||
uint64_t read = 0;
|
|
||||||
uint64_t fullSize = sz * count;
|
|
||||||
_f->error = fsFileRead(&_f->_f, _f->offset, buf, fullSize, 0, &read);
|
|
||||||
_f->offset += read;
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
char fsfgetc(FSFILE *_f)
|
|
||||||
{
|
|
||||||
char ret = 0;
|
|
||||||
uint64_t read = 0;
|
|
||||||
_f->error = fsFileRead(&_f->_f, _f->offset++, &ret, 1, 0, &read);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fsfputc(int ch, FSFILE *_f)
|
|
||||||
{
|
|
||||||
fsfwrite(&ch, 1, 1, _f);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
32
src/gfx.c
32
src/gfx.c
|
|
@ -111,6 +111,7 @@ static void drawGlyph(const FT_Bitmap *bmp, tex *target, int _x, int _y)
|
||||||
if(bmp->pixel_mode != FT_PIXEL_MODE_GRAY)
|
if(bmp->pixel_mode != FT_PIXEL_MODE_GRAY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
clr txClr = textClr, tgtClr;
|
||||||
uint8_t *bmpPtr = bmp->buffer;
|
uint8_t *bmpPtr = bmp->buffer;
|
||||||
for(int y = _y; y < _y + bmp->rows; y++)
|
for(int y = _y; y < _y + bmp->rows; y++)
|
||||||
{
|
{
|
||||||
|
|
@ -125,9 +126,8 @@ static void drawGlyph(const FT_Bitmap *bmp, tex *target, int _x, int _y)
|
||||||
|
|
||||||
if(*bmpPtr > 0)
|
if(*bmpPtr > 0)
|
||||||
{
|
{
|
||||||
clr txClr = clrCreateRGBA(textClr.r, textClr.g, textClr.b, *bmpPtr);
|
txClr.a = *bmpPtr;
|
||||||
clr tgtClr = clrCreateU32(*rowPtr);
|
tgtClr = clrCreateU32(*rowPtr);
|
||||||
|
|
||||||
*rowPtr = blend(txClr, tgtClr);
|
*rowPtr = blend(txClr, tgtClr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -232,6 +232,17 @@ void drawText(const char *str, tex *target, const font *f, int x, int y, int sz,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawTextf(tex *target, const font *f, int x, int y, int sz, clr c, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char tmp[512];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsprintf(tmp, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
drawText(tmp, target, f, x, y, sz, c);
|
||||||
|
}
|
||||||
|
|
||||||
void drawTextWrap(const char *str, tex *target, const font *f, int x, int y, int sz, clr c, int maxWidth)
|
void drawTextWrap(const char *str, tex *target, const font *f, int x, int y, int sz, clr c, int maxWidth)
|
||||||
{
|
{
|
||||||
char wordBuf[128];
|
char wordBuf[128];
|
||||||
|
|
@ -241,10 +252,9 @@ void drawTextWrap(const char *str, tex *target, const font *f, int x, int y, int
|
||||||
resizeFont(f, sz);
|
resizeFont(f, sz);
|
||||||
textClr = c;
|
textClr = c;
|
||||||
|
|
||||||
|
|
||||||
for(unsigned i = 0; i < strLength; )
|
for(unsigned i = 0; i < strLength; )
|
||||||
{
|
{
|
||||||
nextbreak = strcspn(&str[i], " /");
|
nextbreak = strcspn(&str[i], " /_");
|
||||||
|
|
||||||
memset(wordBuf, 0, 128);
|
memset(wordBuf, 0, 128);
|
||||||
memcpy(wordBuf, &str[i], nextbreak + 1);
|
memcpy(wordBuf, &str[i], nextbreak + 1);
|
||||||
|
|
@ -316,11 +326,21 @@ void drawTextWrap(const char *str, tex *target, const font *f, int x, int y, int
|
||||||
tmpX += slot->advance.x >> 6;
|
tmpX += slot->advance.x >> 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i += strlen(wordBuf);
|
i += strlen(wordBuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawTextfWrap(tex *target, const font *f, int x, int y, int sz, clr c, int maxWidth, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char tmp[512];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsprintf(tmp, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
drawTextWrap(tmp, target, f, x, y, sz, c, maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
size_t textGetWidth(const char *str, const font *f, int sz)
|
size_t textGetWidth(const char *str, const font *f, int sz)
|
||||||
{
|
{
|
||||||
size_t width = 0;
|
size_t width = 0;
|
||||||
|
|
|
||||||
10
src/main.cpp
10
src/main.cpp
|
|
@ -37,6 +37,8 @@ extern "C"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool debDataStats = false;
|
||||||
|
|
||||||
int main(int argc, const char *argv[])
|
int main(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
fs::init();
|
fs::init();
|
||||||
|
|
@ -52,12 +54,16 @@ int main(int argc, const char *argv[])
|
||||||
|
|
||||||
uint64_t down = hidKeysDown(CONTROLLER_P1_AUTO);
|
uint64_t down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
uint64_t held = hidKeysHeld(CONTROLLER_P1_AUTO);
|
uint64_t held = hidKeysHeld(CONTROLLER_P1_AUTO);
|
||||||
|
if(held & KEY_LSTICK && held & KEY_RSTICK)
|
||||||
if(down & KEY_PLUS)
|
debDataStats = true;
|
||||||
|
else if(down & KEY_PLUS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
gfxBeginFrame();
|
gfxBeginFrame();
|
||||||
ui::runApp(down, held);
|
ui::runApp(down, held);
|
||||||
|
|
||||||
|
if(debDataStats)
|
||||||
|
data::dispStats();
|
||||||
gfxEndFrame();
|
gfxEndFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
||||||
#define VER_STRING "v. 06.04.2020"
|
#define VER_STRING "v. 06.06.2020"
|
||||||
|
|
||||||
//text mode
|
//text mode
|
||||||
bool ui::textMode = false;
|
bool ui::textMode = false;
|
||||||
|
|
@ -68,8 +68,8 @@ std::string ui::confEraseFolder = "*WARNING*: This *will* delete the current sav
|
||||||
std::string ui::noSavesFound = "No saves found for #%s#!";
|
std::string ui::noSavesFound = "No saves found for #%s#!";
|
||||||
std::string ui::advMenuStr[6] = { "Copy to ", "Delete", "Rename", "Make Dir", "Properties", "Close" };
|
std::string ui::advMenuStr[6] = { "Copy to ", "Delete", "Rename", "Make Dir", "Properties", "Close" };
|
||||||
std::string ui::exMenuStr[10] = { "SD to SD Browser", "BIS: PRODINFOF", "BIS: SAFE", "BIS: SYSTEM", "BIS: USER", "Remove Update", "Terminate Process", "Mount System Save", "Rescan Titles", "Mount Process RomFS" };
|
std::string ui::exMenuStr[10] = { "SD to SD Browser", "BIS: PRODINFOF", "BIS: SAFE", "BIS: SYSTEM", "BIS: USER", "Remove Update", "Terminate Process", "Mount System Save", "Rescan Titles", "Mount Process RomFS" };
|
||||||
std::string ui::optMenuStr[12] = { "Include Dev Sv: ", "AutoBackup: ", "Overclock: ", "Hold to Delete: ", "Hold to Restore: ", "Hold to Overwrite: ", "Force Mount: ", "Account Sys. Saves: ", "Write to Sys. Saves: ", "Text UI Mode: ", "Direct FS Cmd: ", "Skip User Select: " };
|
std::string ui::optMenuStr[13] = { "Include Dev Sv: ", "AutoBackup: ", "Overclock: ", "Hold to Delete: ", "Hold to Restore: ", "Hold to Overwrite: ", "Force Mount: ", "Account Sys. Saves: ", "Write to Sys. Saves: ", "Text UI Mode: ", "Direct FS Cmd: ", "Skip User Select: ", "Export to ZIP: " };
|
||||||
std::string ui::optMenuExp[12] =
|
std::string ui::optMenuExp[13] =
|
||||||
{
|
{
|
||||||
"Includes Device Save data in user accounts.",
|
"Includes Device Save data in user accounts.",
|
||||||
"Automatically creates a save backup before restoring a save.",
|
"Automatically creates a save backup before restoring a save.",
|
||||||
|
|
@ -82,7 +82,8 @@ std::string ui::optMenuExp[12] =
|
||||||
"Controls whether system save data and partitions can have files and data written and deleted from them. *This can be extremely dangerous if you don't know what you're doing!*",
|
"Controls whether system save data and partitions can have files and data written and deleted from them. *This can be extremely dangerous if you don't know what you're doing!*",
|
||||||
"Changes the UI to be text menu based like the original JKSM for 3DS.",
|
"Changes the UI to be text menu based like the original JKSM for 3DS.",
|
||||||
"Directly uses the Switch's FS commands to copy files instead of stdio.",
|
"Directly uses the Switch's FS commands to copy files instead of stdio.",
|
||||||
"Skips the user selection screen and jumps directly to the first user account found."
|
"Skips the user selection screen and jumps directly to the first user account found.",
|
||||||
|
"Exports saves to ZIP files."
|
||||||
};
|
};
|
||||||
std::string ui::holdingText[3] = { "(Hold) ", "(Keep Holding) ", "(Almost there!) " };
|
std::string ui::holdingText[3] = { "(Hold) ", "(Keep Holding) ", "(Almost there!) " };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,22 @@ void ui::createNewBackup(const uint64_t& held)
|
||||||
if(!folder.empty())
|
if(!folder.empty())
|
||||||
{
|
{
|
||||||
std::string path = data::curData.getPath() + "/" + folder;
|
std::string path = data::curData.getPath() + "/" + folder;
|
||||||
mkdir(path.c_str(), 777);
|
if(data::zip)
|
||||||
path += "/";
|
{
|
||||||
fs::copyDirToDir("sv:/", path);
|
path += ".zip";
|
||||||
|
zipFile cpyTo = zipOpen(path.c_str(), 0);
|
||||||
|
if(cpyTo != NULL)
|
||||||
|
{
|
||||||
|
fs::copyDirToZip("sv:/", &cpyTo);
|
||||||
|
zipClose(cpyTo, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mkdir(path.c_str(), 777);
|
||||||
|
path += "/";
|
||||||
|
fs::copyDirToDir("sv:/", path);
|
||||||
|
}
|
||||||
|
|
||||||
folderMenuPrepare(data::curUser, data::curData);
|
folderMenuPrepare(data::curUser, data::curData);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,27 @@
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
//Where to start in titles
|
||||||
|
static int start = 0;
|
||||||
|
|
||||||
|
//Color shift for rect
|
||||||
|
static uint8_t clrShft = 0;
|
||||||
|
//Whether or not we're adding or subtracting from clrShft
|
||||||
|
static bool clrAdd = true;
|
||||||
|
|
||||||
|
//Selected rectangle X and Y.
|
||||||
|
static unsigned selRectX = 66, selRectY = 94;
|
||||||
|
|
||||||
|
static inline void reset()
|
||||||
|
{
|
||||||
|
start = 0;
|
||||||
|
selRectX = 66;
|
||||||
|
selRectY = 94;
|
||||||
|
data::selData = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ui::updateTitleMenu(const uint64_t& down, const uint64_t& held)
|
void ui::updateTitleMenu(const uint64_t& down, const uint64_t& held)
|
||||||
{
|
{
|
||||||
//Static vars so they don't change on every loop
|
|
||||||
//Where to start in titles, selected title
|
|
||||||
static int start = 0;
|
|
||||||
|
|
||||||
//Color shift for rect
|
|
||||||
static uint8_t clrShft = 0;
|
|
||||||
//Whether or not we're adding or subtracting from clrShft
|
|
||||||
static bool clrAdd = true;
|
|
||||||
|
|
||||||
//Selected rectangle X and Y.
|
|
||||||
static unsigned selRectX = 66, selRectY = 94;
|
|
||||||
|
|
||||||
if(clrAdd)
|
if(clrAdd)
|
||||||
{
|
{
|
||||||
clrShft += 6;
|
clrShft += 6;
|
||||||
|
|
@ -56,8 +63,7 @@ void ui::updateTitleMenu(const uint64_t& down, const uint64_t& held)
|
||||||
selRectX = tX - 6;
|
selRectX = tX - 6;
|
||||||
selRectY = y - 6;
|
selRectY = y - 6;
|
||||||
|
|
||||||
std::string title = data::curData.getTitle();
|
unsigned titleWidth = textGetWidth(data::curData.getTitle().c_str(), ui::shared, 18);
|
||||||
unsigned titleWidth = textGetWidth(title.c_str(), ui::shared, 18);
|
|
||||||
int rectWidth = titleWidth + 32, rectX = (tX + 64) - (rectWidth / 2);
|
int rectWidth = titleWidth + 32, rectX = (tX + 64) - (rectWidth / 2);
|
||||||
if(rectX < 16)
|
if(rectX < 16)
|
||||||
rectX = 16;
|
rectX = 16;
|
||||||
|
|
@ -65,7 +71,7 @@ void ui::updateTitleMenu(const uint64_t& down, const uint64_t& held)
|
||||||
rectX = 1264 - rectWidth;
|
rectX = 1264 - rectWidth;
|
||||||
|
|
||||||
drawTextbox(frameBuffer, rectX, y - 50, rectWidth, 38);
|
drawTextbox(frameBuffer, rectX, y - 50, rectWidth, 38);
|
||||||
drawText(title.c_str(), frameBuffer, ui::shared, rectX + 16, y - 40, 18, ui::txtDiag);
|
drawText(data::curData.getTitle().c_str(), frameBuffer, ui::shared, rectX + 16, y - 40, 18, ui::txtDiag);
|
||||||
}
|
}
|
||||||
if(data::curUser.titles[i].getFav())
|
if(data::curUser.titles[i].getFav())
|
||||||
texDrawSkipNoAlpha(data::curUser.titles[i].getIconFav(), frameBuffer, tX, y);
|
texDrawSkipNoAlpha(data::curUser.titles[i].getIconFav(), frameBuffer, tX, y);
|
||||||
|
|
@ -125,10 +131,7 @@ void ui::updateTitleMenu(const uint64_t& down, const uint64_t& held)
|
||||||
}
|
}
|
||||||
else if(down & KEY_B)
|
else if(down & KEY_B)
|
||||||
{
|
{
|
||||||
start = 0;
|
reset();
|
||||||
data::selData = 0;
|
|
||||||
selRectX = 64;
|
|
||||||
selRectY = 90;
|
|
||||||
mstate = USR_SEL;
|
mstate = USR_SEL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -137,9 +140,7 @@ void ui::updateTitleMenu(const uint64_t& down, const uint64_t& held)
|
||||||
if(--data::selUser < 0)
|
if(--data::selUser < 0)
|
||||||
data::selUser = data::users.size() - 1;
|
data::selUser = data::users.size() - 1;
|
||||||
|
|
||||||
start = 0;
|
reset();
|
||||||
data::selData = 0;
|
|
||||||
selRectX = 64, selRectY = 90;
|
|
||||||
ui::showPopup(POP_FRAME_DEFAULT, data::curUser.getUsername().c_str());
|
ui::showPopup(POP_FRAME_DEFAULT, data::curUser.getUsername().c_str());
|
||||||
}
|
}
|
||||||
else if(down & KEY_R)
|
else if(down & KEY_R)
|
||||||
|
|
@ -147,9 +148,7 @@ void ui::updateTitleMenu(const uint64_t& down, const uint64_t& held)
|
||||||
if(++data::selUser > (int)data::users.size() - 1)
|
if(++data::selUser > (int)data::users.size() - 1)
|
||||||
data::selUser = 0;
|
data::selUser = 0;
|
||||||
|
|
||||||
start = 0;
|
reset();
|
||||||
data::selData = 0;
|
|
||||||
selRectX = 64, selRectY = 90;
|
|
||||||
ui::showPopup(POP_FRAME_DEFAULT, data::curUser.getUsername().c_str());
|
ui::showPopup(POP_FRAME_DEFAULT, data::curUser.getUsername().c_str());
|
||||||
}
|
}
|
||||||
else if(down & KEY_ZR)
|
else if(down & KEY_ZR)
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,7 @@ void ui::updateExMenu(const uint64_t& down, const uint64_t& held)
|
||||||
void ui::optMenuInit()
|
void ui::optMenuInit()
|
||||||
{
|
{
|
||||||
optMenu.setParams(76, 98, 310);
|
optMenu.setParams(76, 98, 310);
|
||||||
for(unsigned i = 0; i < 12; i++)
|
for(unsigned i = 0; i < 13; i++)
|
||||||
optMenu.addOpt(ui::optMenuStr[i]);
|
optMenu.addOpt(ui::optMenuStr[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,6 +360,7 @@ void ui::updateOptMenu(const uint64_t& down, const uint64_t& held)
|
||||||
optMenu.editOpt(9, optMenuStr[9] + getBoolText(ui::textMode));
|
optMenu.editOpt(9, optMenuStr[9] + getBoolText(ui::textMode));
|
||||||
optMenu.editOpt(10, optMenuStr[10] + getBoolText(data::directFsCmd));
|
optMenu.editOpt(10, optMenuStr[10] + getBoolText(data::directFsCmd));
|
||||||
optMenu.editOpt(11, optMenuStr[11] + getBoolText(data::skipUser));
|
optMenu.editOpt(11, optMenuStr[11] + getBoolText(data::skipUser));
|
||||||
|
optMenu.editOpt(12, optMenuStr[12] + getBoolText(data::zip));
|
||||||
|
|
||||||
if(down & KEY_A)
|
if(down & KEY_A)
|
||||||
{
|
{
|
||||||
|
|
@ -412,6 +413,10 @@ void ui::updateOptMenu(const uint64_t& down, const uint64_t& held)
|
||||||
case 11:
|
case 11:
|
||||||
switchBool(data::skipUser);
|
switchBool(data::skipUser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 12:
|
||||||
|
switchBool(data::zip);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(down & KEY_X)
|
else if(down & KEY_X)
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,7 @@ void ui::updateUserMenu(const uint64_t& down, const uint64_t& held)
|
||||||
selRectX = tX - 6;
|
selRectX = tX - 6;
|
||||||
selRectY = y - 6;
|
selRectY = y - 6;
|
||||||
|
|
||||||
std::string username = data::users[data::selUser].getUsername();
|
unsigned userWidth = textGetWidth(data::curUser.getUsername().c_str(), ui::shared, 18);
|
||||||
unsigned userWidth = textGetWidth(username.c_str(), ui::shared, 18);
|
|
||||||
int userRectWidth = userWidth + 32, userRectX = (tX + 64) - (userRectWidth / 2);
|
int userRectWidth = userWidth + 32, userRectX = (tX + 64) - (userRectWidth / 2);
|
||||||
if(userRectX < 16)
|
if(userRectX < 16)
|
||||||
userRectX = 16;
|
userRectX = 16;
|
||||||
|
|
@ -57,7 +56,7 @@ void ui::updateUserMenu(const uint64_t& down, const uint64_t& held)
|
||||||
userRectX = 1264 - userRectWidth;
|
userRectX = 1264 - userRectWidth;
|
||||||
|
|
||||||
drawTextbox(frameBuffer, userRectX, y - 50, userRectWidth, 38);
|
drawTextbox(frameBuffer, userRectX, y - 50, userRectWidth, 38);
|
||||||
drawText(username.c_str(), frameBuffer, ui::shared, userRectX + 16, y - 40, 18, ui::txtDiag);
|
drawText(data::curUser.getUsername().c_str(), frameBuffer, ui::shared, userRectX + 16, y - 40, 18, ui::txtDiag);
|
||||||
}
|
}
|
||||||
data::users[i].drawIconHalf(tX, y);
|
data::users[i].drawIconHalf(tX, y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user