Files
NXST/source/fs.cpp
2024-07-14 20:20:03 +03:00

520 lines
18 KiB
C++

#include <switch.h>
#include "fs.h"
#include "threads.h"
#include "util.h"
static std::string wd = "sdmc:/NXST/";
static FSFILE *debLog;
static FsFileSystem sv;
static std::vector <std::string> pathFilter;
void fs::init() {
mkDirRec(wd);
fs::logOpen();
}
bool fs::mountSave(const FsSaveDataInfo &_m) {
Result svOpen;
FsSaveDataAttribute attr = {0};
switch (_m.save_data_type) {
case FsSaveDataType_System:
case FsSaveDataType_SystemBcat: {
attr.uid = _m.uid;
attr.system_save_data_id = _m.system_save_data_id;
attr.save_data_type = _m.save_data_type;
svOpen = fsOpenSaveDataFileSystemBySystemSaveDataId(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Account: {
attr.uid = _m.uid;
attr.application_id = _m.application_id;
attr.save_data_type = _m.save_data_type;
attr.save_data_rank = _m.save_data_rank;
attr.save_data_index = _m.save_data_index;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Device: {
attr.application_id = _m.application_id;
attr.save_data_type = FsSaveDataType_Device;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Bcat: {
attr.application_id = _m.application_id;
attr.save_data_type = FsSaveDataType_Bcat;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Cache: {
attr.application_id = _m.application_id;
attr.save_data_type = FsSaveDataType_Cache;
attr.save_data_index = _m.save_data_index;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Temporary: {
attr.application_id = _m.application_id;
attr.save_data_type = _m.save_data_type;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
}
break;
default:
svOpen = 1;
break;
}
return R_SUCCEEDED(svOpen) && fsdevMountDevice("sv", sv) != -1;
}
bool fs::commitToDevice(const std::string &dev) {
bool ret = true;
Result res = fsdevCommitDevice(dev.c_str());
if (R_FAILED(res)) {
fs::logWrite("Error committing file to device -> 0x%X\n", res);
ret = false;
}
return ret;
}
std::string fs::getWorkDir() { return wd; }
void fs::setWorkDir(const std::string &_w) { wd = _w; }
void fs::loadPathFilters(const uint64_t &tid) {
char path[256];
// sprintf(path, "sdmc:/config/JKSV/0x%016lX_filter.txt", tid);
if (fs::fileExists(path)) {
fs::dataFile filter(path);
while (filter.readNextLine(false))
pathFilter.push_back(filter.getLine());
}
}
bool fs::pathIsFiltered(const std::string &_path) {
if (pathFilter.empty())
return false;
for (std::string &_p: pathFilter) {
if (_path == _p)
return true;
}
return false;
}
void fs::freePathFilters() {
pathFilter.clear();
}
void fs::createSaveData(FsSaveDataType _type, uint64_t _tid, AccountUid _uid, threadInfo *t) {
data::titleInfo *tinfo = data::getTitleInfoByTID(_tid);
if (t)
t->status->setStatus("Creating Save Data for #%s#...", tinfo->title.c_str());
uint16_t cacheIndex = 0;
std::string indexStr;
if (_type == FsSaveDataType_Cache &&
!(indexStr = util::getStringInput(SwkbdType_NumPad, "0", "Enter cache index", 2, 0, NULL)).empty())
cacheIndex = strtoul(indexStr.c_str(), NULL, 10);
else if (_type == FsSaveDataType_Cache && indexStr.empty()) {
if (t)
t->finished = true;
return;
}
FsSaveDataAttribute attr;
memset(&attr, 0, sizeof(FsSaveDataAttribute));
attr.application_id = _tid;
attr.uid = _uid;
attr.system_save_data_id = 0;
attr.save_data_type = _type;
attr.save_data_rank = 0;
attr.save_data_index = cacheIndex;
FsSaveDataCreationInfo crt;
memset(&crt, 0, sizeof(FsSaveDataCreationInfo));
int64_t saveSize = 0, journalSize = 0;
switch (_type) {
case FsSaveDataType_Account:
saveSize = tinfo->nacp.user_account_save_data_size;
journalSize = tinfo->nacp.user_account_save_data_journal_size;
break;
case FsSaveDataType_Device:
saveSize = tinfo->nacp.device_save_data_size;
journalSize = tinfo->nacp.device_save_data_journal_size;
break;
case FsSaveDataType_Bcat:
saveSize = tinfo->nacp.bcat_delivery_cache_storage_size;
journalSize = tinfo->nacp.bcat_delivery_cache_storage_size;
break;
case FsSaveDataType_Cache:
saveSize = 32 * 1024 * 1024;
if (tinfo->nacp.cache_storage_journal_size > tinfo->nacp.cache_storage_data_and_journal_size_max)
journalSize = tinfo->nacp.cache_storage_journal_size;
else
journalSize = tinfo->nacp.cache_storage_data_and_journal_size_max;
break;
default:
if (t)
t->finished = true;
return;
break;
}
crt.save_data_size = saveSize;
crt.journal_size = journalSize;
crt.available_size = 0x4000;
crt.owner_id = _type == FsSaveDataType_Bcat ? 0x010000000000000C : tinfo->nacp.save_data_owner_id;
crt.flags = 0;
crt.save_data_space_id = FsSaveDataSpaceId_User;
FsSaveDataMetaInfo meta;
memset(&meta, 0, sizeof(FsSaveDataMetaInfo));
if (_type != FsSaveDataType_Bcat) {
meta.size = 0x40060;
meta.type = FsSaveDataMetaType_Thumbnail;
}
Result res = 0;
if (R_SUCCEEDED(res = fsCreateSaveDataFileSystem(&attr, &crt, &meta))) {
util::createTitleDirectoryByTID(_tid);
data::loadUsersTitles(false);
} else {
fs::logWrite("SaveCreate Failed -> %X\n", res);
}
}
static void createSaveData_t(void *a) {
threadInfo *t = (threadInfo *) a;
fs::svCreateArgs *crt = (fs::svCreateArgs *) t->argPtr;
fs::createSaveData(crt->type, crt->tid, crt->account, t);
delete crt;
t->finished = true;
}
void fs::createSaveDataThreaded(FsSaveDataType _type, uint64_t _tid, AccountUid _uid) {
fs::svCreateArgs *send = new fs::svCreateArgs;
send->type = _type;
send->tid = _tid;
send->account = _uid;
threads::newThread(createSaveData_t, send, NULL);
}
bool fs::extendSaveData(const data::userTitleInfo *tinfo, uint64_t extSize, threadInfo *t) {
if (t)
t->status->setStatus("Extending Save Data for #%s#...", data::getTitleNameByTID(tinfo->tid).c_str());
uint64_t journal = fs::getJournalSizeMax(tinfo);
uint64_t saveID = tinfo->saveInfo.save_data_id;
FsSaveDataSpaceId space = (FsSaveDataSpaceId) tinfo->saveInfo.save_data_space_id;
Result res = 0;
if (R_FAILED((res = fsExtendSaveDataFileSystem(space, saveID, extSize, journal)))) {
int64_t totalSize = 0;
fs::mountSave(tinfo->saveInfo);
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &totalSize);
fs::unmountSave();
fs::logWrite("Extend Failed: %uMB to %uMB -> %X\n", totalSize / 1024 / 1024, extSize / 1024 / 1024, res);
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataExtendFailed", 0));
return false;
}
return true;
}
static void extendSaveData_t(void *a) {
threadInfo *t = (threadInfo *) a;
fs::svExtendArgs *e = (fs::svExtendArgs *) t->argPtr;
fs::extendSaveData(e->tinfo, e->extSize, t);
delete e;
t->finished = true;
}
void fs::extendSaveDataThreaded(const data::userTitleInfo *tinfo, uint64_t extSize) {
fs::svExtendArgs *send = new fs::svExtendArgs;
send->tinfo = tinfo;
send->extSize = extSize;
threads::newThread(extendSaveData_t, send, NULL);
}
uint64_t fs::getJournalSize(const data::userTitleInfo *tinfo) {
uint64_t ret = 0;
data::titleInfo *t = data::getTitleInfoByTID(tinfo->tid);
switch (tinfo->saveInfo.save_data_type) {
case FsSaveDataType_Account:
ret = t->nacp.user_account_save_data_journal_size;
break;
case FsSaveDataType_Device:
ret = t->nacp.device_save_data_journal_size;
break;
case FsSaveDataType_Bcat:
ret = t->nacp.bcat_delivery_cache_storage_size;
break;
case FsSaveDataType_Cache:
if (t->nacp.cache_storage_journal_size > 0)
ret = t->nacp.cache_storage_journal_size;
else
ret = t->nacp.cache_storage_data_and_journal_size_max;
break;
default:
ret = BUFF_SIZE;
break;
}
return ret;
}
uint64_t fs::getJournalSizeMax(const data::userTitleInfo *tinfo) {
uint64_t ret = 0;
data::titleInfo *extend = data::getTitleInfoByTID(tinfo->tid);
switch (tinfo->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)
ret = extend->nacp.user_account_save_data_journal_size_max;
else
ret = extend->nacp.user_account_save_data_journal_size;
break;
case FsSaveDataType_Bcat:
ret = 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)
ret = extend->nacp.cache_storage_data_and_journal_size_max;
else
ret = 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)
ret = extend->nacp.device_save_data_journal_size_max;
else
ret = extend->nacp.device_save_data_journal_size;
break;
default:
//will just fail
ret = 0;
break;
}
return ret;
}
static void wipeSave_t(void *a) {
threadInfo *t = (threadInfo *) a;
t->status->setStatus("Resetting save data...");
fs::delDir("sv:/");
fs::commitToDevice("sv");
t->finished = true;
}
void fs::wipeSave() {
threads::newThread(wipeSave_t, NULL, NULL);
}
void fs::createNewBackup(void *a) {
if (!fs::dirNotEmpty("sv:/")) {
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popSaveIsEmpty", 0));
return;
}
uint64_t held = threads::padKeysHeld();
data::user *u = data::getCurrentUser();
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
data::titleInfo *t = data::getTitleInfoByTID(d->tid);
std::string out;
const std::string dict[] =
{
util::getDateTime(util::DATE_FMT_YMD),
util::getDateTime(util::DATE_FMT_YDM),
util::getDateTime(util::DATE_FMT_HOYSTE),
util::getDateTime(util::DATE_FMT_JHK),
util::getDateTime(util::DATE_FMT_ASC),
u->getUsernameSafe(),
t->safeTitle,
util::generateAbbrev(d->tid),
".zip"
};
std::string defaultText = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD);
out = util::getStringInput(SwkbdType_QWERTY, defaultText, "Enter a new name", 64, 9, dict);
out = util::safeString(out);
if (!out.empty()) {
std::string ext = util::getExtensionFromString(out);
std::string path = util::generatePathByTID(d->tid) + out;
fs::mkDir(path);
path += "/";
fs::copyDirToDirThreaded("sv:/", path);
// ui::fldRefreshMenu();
}
}
void fs::overwriteBackup(void *a) {
threadInfo *t = (threadInfo *) a;
std::string *dst = (std::string *) t->argPtr;
bool saveHasFiles = fs::dirNotEmpty("sv:/");
if (fs::isDir(*dst) && saveHasFiles) {
fs::delDir(*dst);
fs::mkDir(*dst);
dst->append("/");
fs::copyDirToDirThreaded("sv:/", *dst);
}
delete dst;
t->finished = true;
}
void fs::restoreBackup(void *a) {
threadInfo *t = (threadInfo *) a;
std::string *restore = (std::string *) t->argPtr;
data::user *u = data::getCurrentUser();
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
if ((utinfo->saveInfo.save_data_type != FsSaveDataType_System)) {
bool saveHasFiles = fs::dirNotEmpty("sv:/");
if (saveHasFiles) {
std::string autoFolder = util::generatePathByTID(utinfo->tid) + "/AUTO - " + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + "/";
fs::mkDir(autoFolder.substr(0, autoFolder.length() - 1));
fs::copyDirToDirThreaded("sv:/", autoFolder);
}
if (fs::isDir(*restore)) {
restore->append("/");
if (fs::dirNotEmpty(*restore)) {
t->status->setStatus("Calculating save data size...");
unsigned dirCount = 0, fileCount = 0;
uint64_t saveSize = 0;
int64_t availSize = 0;
fs::getDirProps(*restore, dirCount, fileCount, saveSize);
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &availSize);
if ((int) saveSize > availSize) {
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
fs::unmountSave();
fs::extendSaveData(utinfo, saveSize + 0x500000, t);
fs::mountSave(utinfo->saveInfo);
}
fs::wipeSave();
fs::copyDirToDirCommitThreaded(*restore, "sv:/", "sv");
}
// else
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popFolderIsEmpty", 0));
} else {
std::string dstPath = "sv:/" + util::getFilenameFromPath(*restore);
fs::copyFileCommitThreaded(*restore, dstPath, "sv");
}
}
delete restore;
t->finished = true;
}
void fs::deleteBackup(void *a) {
threadInfo *t = (threadInfo *) a;
std::string *deletePath = (std::string *) t->argPtr;
std::string backupName = util::getFilenameFromPath(*deletePath);
t->status->setStatus("Deleting...");
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
if (fs::isDir(*deletePath)) {
*deletePath += "/";
fs::delDir(*deletePath);
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupDeleted", 0), backupName.c_str());
} else {
fs::delfile(*deletePath);
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupDeleted", 0), backupName.c_str());
}
// ui::fldRefreshMenu();
delete deletePath;
t->finished = true;
}
void fs::dumpAllUserSaves(void *a) {
threadInfo *t = (threadInfo *) a;
fs::copyArgs *c = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
t->argPtr = c;
data::user *u = data::getCurrentUser();
for (unsigned i = 0; i < u->titleInfo.size(); i++) {
bool saveMounted = fs::mountSave(u->titleInfo[i].saveInfo);
util::createTitleDirectoryByTID(u->titleInfo[i].tid);
if (saveMounted && fs::dirNotEmpty("sv:/")) {
fs::loadPathFilters(u->titleInfo[i].tid);
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + "/";
fs::mkDir(dst.substr(0, dst.length() - 1));
fs::copyDirToDir("sv:/", dst, t);
fs::freePathFilters();
}
fs::unmountSave();
}
fs::copyArgsDestroy(c);
t->finished = true;
}
void fs::dumpAllUsersAllSaves(void *a) {
threadInfo *t = (threadInfo *) a;
fs::copyArgs *c = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
t->argPtr = c;
unsigned curUser = 0;
while (data::users[curUser].getUID128() != 2) {
data::user *u = &data::users[curUser++];
for (unsigned i = 0; i < u->titleInfo.size(); i++) {
bool saveMounted = fs::mountSave(u->titleInfo[i].saveInfo);
util::createTitleDirectoryByTID(u->titleInfo[i].tid);
if (saveMounted && fs::dirNotEmpty("sv:/")) {
fs::loadPathFilters(u->titleInfo[i].tid);
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + "/";
fs::mkDir(dst.substr(0, dst.length() - 1));
fs::copyDirToDir("sv:/", dst, t);
fs::freePathFilters();
}
fs::unmountSave();
}
}
fs::copyArgsDestroy(c);
t->finished = true;
}
void fs::logOpen() {
std::string logPath = wd + "log.txt";
debLog = fsfopen(logPath.c_str(), FsOpenMode_Write);
fsfclose(debLog);
}
void fs::logWrite(const char *fmt, ...) {
std::string logPath = wd + "log.txt";
debLog = fsfopen(logPath.c_str(), FsOpenMode_Append | FsOpenMode_Write);
char tmp[256];
va_list args;
va_start(args, fmt);
vsprintf(tmp, fmt, args);
va_end(args);
fsfwrite(tmp, 1, strlen(tmp), debLog);
fsfclose(debLog);
}