From 94b858842300f06b50c43ad3291c497f8f7e4367 Mon Sep 17 00:00:00 2001 From: d0k3 Date: Tue, 6 Jun 2017 21:14:19 +0200 Subject: [PATCH] Improve vff.c / vff.h functionality --- source/common/common.h | 2 +- source/fs/fsdir.c | 28 ----- source/fs/fsdir.h | 1 - source/fs/fsdrive.c | 19 ++- source/fs/fsutil.c | 251 ++++----------------------------------- source/fs/fsutil.h | 3 - source/game/gameutil.c | 20 +--- source/game/keydbutil.c | 7 +- source/virtual/vff.c | 218 ++++++++++++++++++++++++++++++++-- source/virtual/vff.h | 17 +++ source/virtual/virtual.c | 32 ----- source/virtual/virtual.h | 3 - 12 files changed, 264 insertions(+), 337 deletions(-) diff --git a/source/common/common.h b/source/common/common.h index 3e40b13..e713d2a 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -49,7 +49,7 @@ #endif // GodMode9 version -#define VERSION "1.2.1" +#define VERSION "1.2.3" // input / output paths #define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9" diff --git a/source/fs/fsdir.c b/source/fs/fsdir.c index 81f7b54..aaa208f 100644 --- a/source/fs/fsdir.c +++ b/source/fs/fsdir.c @@ -28,31 +28,3 @@ void SortDirStruct(DirStruct* contents) { } } } - -// inspired by http://www.geeksforgeeks.org/wildcard-character-matching/ -bool MatchName(const char *pattern, const char *path) { - // handling non asterisk chars - for (; *pattern != '*'; pattern++, path++) { - if ((*pattern == '\0') && (*path == '\0')) { - return true; // end reached simultaneously, match found - } else if ((*pattern == '\0') || (*path == '\0')) { - return false; // end reached on only one, failure - } else if ((*pattern != '?') && (tolower(*pattern) != tolower(*path))) { - return false; // chars don't match, failure - } - } - // handling the asterisk (matches one or more chars in path) - if ((*(pattern+1) == '?') || (*(pattern+1) == '*')) { - return false; // stupid user shenanigans, failure - } else if (*path == '\0') { - return false; // asterisk, but end reached on path, failure - } else if (*(pattern+1) == '\0') { - return true; // nothing after the asterisk, match found - } else { // we couldn't really go without recursion here - for (path++; *path != '\0'; path++) { - if (MatchName(pattern + 1, path)) return true; - } - } - - return false; -} diff --git a/source/fs/fsdir.h b/source/fs/fsdir.h index 0461673..41fa372 100644 --- a/source/fs/fsdir.h +++ b/source/fs/fsdir.h @@ -26,4 +26,3 @@ typedef struct { void DirEntryCpy(DirEntry* dest, const DirEntry* orig); void SortDirStruct(DirStruct* contents); -bool MatchName(const char *pattern, const char *path); diff --git a/source/fs/fsdrive.c b/source/fs/fsdrive.c index 8393759..d2b0330 100644 --- a/source/fs/fsdrive.c +++ b/source/fs/fsdrive.c @@ -5,7 +5,7 @@ #include "sddata.h" #include "image.h" #include "ui.h" -#include "ff.h" +#include "vff.h" // last search pattern, path & mode static char search_pattern[256] = { 0 }; @@ -111,18 +111,18 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const ch char* fname = fpath + strnlen(fpath, fnsize - 1); bool ret = false; - if (fa_opendir(&pdir, fpath) != FR_OK) + if (fvx_opendir(&pdir, fpath) != FR_OK) return false; (fname++)[0] = '/'; - while (f_readdir(&pdir, &fno) == FR_OK) { + while (fvx_readdir(&pdir, &fno) == FR_OK) { if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) continue; // filter out virtual entries strncpy(fname, fno.fname, (fnsize - 1) - (fname - fpath)); if (fno.fname[0] == 0) { ret = true; break; - } else if (!pattern || MatchName(pattern, fname)) { + } else if (!pattern || (fvx_match_name(fname, pattern) == FR_OK)) { DirEntry* entry = &(contents->entry[contents->n_entries]); strncpy(entry->path, fpath, 256); entry->name = entry->path + (fname - fpath); @@ -146,7 +146,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const ch break; } } - f_closedir(&pdir); + fvx_closedir(&pdir); return ret; } @@ -167,13 +167,8 @@ void SearchDirContents(DirStruct* contents, const char* path, const char* patter // search the path char fpath[256]; // 256 is the maximum length of a full path strncpy(fpath, path, 256); - if (DriveType(path) & DRV_VIRTUAL) { - if (!GetVirtualDirContents(contents, fpath, 256, pattern, recursive)) - contents->n_entries = 0; - } else { - if (!GetDirContentsWorker(contents, fpath, 256, pattern, recursive)) - contents->n_entries = 0; - } + if (!GetDirContentsWorker(contents, fpath, 256, pattern, recursive)) + contents->n_entries = 0; } } diff --git a/source/fs/fsutil.c b/source/fs/fsutil.c index 91f7491..880e83c 100644 --- a/source/fs/fsutil.c +++ b/source/fs/fsutil.c @@ -128,19 +128,10 @@ size_t FileGetData(const char* path, u8* data, size_t size, size_t foffset) { } size_t FileGetSize(const char* path) { - int drvtype = DriveType(path); - if (drvtype & DRV_FAT) { - FILINFO fno; - if (fa_stat(path, &fno) != FR_OK) - return 0; - return fno.fsize; - } else if (drvtype & DRV_VIRTUAL) { - VirtualFile vfile; - if (!GetVirtualFile(&vfile, path)) - return 0; - return vfile.size; - } - return 0; + FILINFO fno; + if (fvx_stat(path, &fno) != FR_OK) + return 0; + return fno.fsize; } bool FileGetSha256(const char* path, u8* sha256) { @@ -265,27 +256,6 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) { return ret; } -bool DirBuilderWorker(char* dest) { - DIR tmp_dir; - if (fa_opendir(&tmp_dir, dest) != FR_OK) { - char* slash = strrchr(dest, '/'); - if (!slash) return false; - *slash = '\0'; - if (!DirBuilderWorker(dest)) return false; - *slash = '/'; - return (fa_mkdir(dest) == FR_OK); - } else { - f_closedir(&tmp_dir); - return true; - } -} - -bool DirBuilder(const char* destdir) { - char fdpath[256]; // 256 is the maximum length of a full path - strncpy(fdpath, destdir, 255); - return DirBuilderWorker(fdpath); -} - bool DirCreate(const char* cpath, const char* dirname) { char npath[256]; // 256 is the maximum length of a full path if (!CheckWritePermissions(cpath)) return false; @@ -474,8 +444,7 @@ bool PathCopyFatToVrt(const char* destdir, const char* orig, u32* flags) { return ret; } -bool PathCopyVrtToFat(char* dest, char* orig, u32* flags) { - VirtualFile vfile; +bool PathCopyToFat(char* dest, char* orig, u32* flags, bool move) { FILINFO fno; bool ret = false; @@ -484,147 +453,8 @@ bool PathCopyVrtToFat(char* dest, char* orig, u32* flags) { if (fa_opendir(&tmp_dir, dest) != FR_OK) return false; f_closedir(&tmp_dir); } else if (!(fno.fattrib & AM_DIR)) return false; // destination is not a directory (must be at this point) - - // build full destination path (on top of destination directory) - char* oname = strrchr(orig, '/'); - char* dname = dest + strnlen(dest, 255); - if (oname == NULL) return false; // not a proper origin path - oname++; - *(dname++) = '/'; - strncpy(dname, oname, 256 - (dname - dest)); - - // path string (for output) - char deststr[36 + 1]; - TruncateString(deststr, dest, 36, 8); - - // open / check virtual file - if (!GetVirtualFile(&vfile, orig)) - return false; - - // check if destination exists - if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL)) && (fa_stat(dest, NULL) == FR_OK)) { - if (*flags & SKIP_ALL) { - *flags |= SKIP_CUR; - return true; - } - const char* optionstr[5] = - {"Choose new name", "Overwrite file(s)", "Skip file(s)", "Overwrite all", "Skip all"}; - u32 user_select = ShowSelectPrompt((*flags & ASK_ALL) ? 5 : 3, optionstr, - "Destination already exists:\n%s", deststr); - if (user_select == 1) { - do { - if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name")) - return false; - } while (fa_stat(dest, NULL) == FR_OK); - } else if (user_select == 2) { - *flags |= OVERWRITE_CUR; - } else if (user_select == 3) { - *flags |= SKIP_CUR; - return true; - } else if (user_select == 4) { - *flags |= OVERWRITE_ALL; - } else if (user_select == 5) { - *flags |= (SKIP_CUR|SKIP_ALL); - return true; - } else { - return false; - } - } - - // check destination write permission (special paths only) - if (((*dest == '1') || (strncmp(dest, "0:/Nintendo 3DS", 16) == 0)) && - (!flags || (*flags & (OVERWRITE_CUR|OVERWRITE_ALL))) && - (!flags || !(*flags & OVERRIDE_PERM)) && - !CheckWritePermissions(dest)) return false; - - // the copy process takes place here - if (!ShowProgress(0, 0, orig)) return false; - if (vfile.flags & VFLAG_DIR) { // processing folders - DIR pdir; - VirtualDir vdir; - char* fname = orig + strnlen(orig, 256); - - // create the destination folder if it does not already exist - if (fa_opendir(&pdir, dest) != FR_OK) { - if (f_mkdir(dest) != FR_OK) { - ShowPrompt(false, "%s\nError: Overwriting file with dir", deststr); - return false; - } - } else f_closedir(&pdir); - - if (!OpenVirtualDir(&vdir, &vfile)) - return false; - *(fname++) = '/'; - while (true) { - if (!ReadVirtualDir(&vfile, &vdir)) { - ret = true; - break; - } - char name[256]; - if (!GetVirtualFilename(name, &vfile, 256)) break; - strncpy(fname, name, 256 - (fname - orig)); - if (!PathCopyVrtToFat(dest, orig, flags)) - break; - } - } else { // copying files - FIL dfile; - u64 osize = vfile.size; - - if (GetFreeSpace(dest) < osize) { - ShowPrompt(false, "%s\nError: File is too big for destination", deststr); - return false; - } - - if (fx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { - ShowPrompt(false, "%s\nError: Cannot create destination file", deststr); - return false; - } - f_lseek(&dfile, 0); - f_sync(&dfile); - - ret = true; - if (flags && (*flags & CALC_SHA)) sha_init(SHA256_MODE); - for (u64 pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) { - UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos); - UINT bytes_written = 0; - if (ReadVirtualFile(&vfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0) - ret = false; - if (!ShowProgress(pos + (read_bytes / 2), osize, orig)) - ret = false; - if (fx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) - ret = false; - if (read_bytes != bytes_written) - ret = false; - if (flags && (*flags & CALC_SHA)) - sha_update(MAIN_BUFFER, read_bytes); - } - ShowProgress(1, 1, orig); - - fx_close(&dfile); - if (!ret) f_unlink(dest); - else if (flags && (*flags & CALC_SHA)) { - u8 sha256[0x20]; - char* ext_sha = dest + strnlen(dest, 256); - strncpy(ext_sha, ".sha", 256 - (ext_sha - dest)); - sha_get(sha256); - FileSetData(dest, sha256, 0x20, 0, true); - } - } - - *(--dname) = '\0'; - return ret; -} - -bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { - FILINFO fno; - bool ret = false; - - if (fa_stat(dest, &fno) != FR_OK) { // is root or destination does not exist - DIR tmp_dir; // check if root - if (fa_opendir(&tmp_dir, dest) != FR_OK) return false; - f_closedir(&tmp_dir); - } else if (!(fno.fattrib & AM_DIR)) return false; // destination is not a directory (must be at this point) - if (fa_stat(orig, &fno) != FR_OK) return false; // origin does not exist + if (fvx_stat(orig, &fno) != FR_OK) return false; // origin does not exist + if (move && (fno.fattrib & AM_VRT)) return false; // trying to move a virtual file // build full destination path (on top of destination directory) char* oname = strrchr(orig, '/'); @@ -702,22 +532,22 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { } } else f_closedir(&pdir); - if (fa_opendir(&pdir, orig) != FR_OK) + if (fvx_opendir(&pdir, orig) != FR_OK) return false; *(fname++) = '/'; - while (f_readdir(&pdir, &fno) == FR_OK) { + while (fvx_readdir(&pdir, &fno) == FR_OK) { if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) continue; // filter out virtual entries strncpy(fname, fno.fname, 256 - (fname - orig)); if (fno.fname[0] == 0) { ret = true; break; - } else if (!PathCopyWorker(dest, orig, flags, move)) { + } else if (!PathCopyToFat(dest, orig, flags, move)) { break; } } - f_closedir(&pdir); + fvx_closedir(&pdir); } else if (move) { // moving if destination exists if (fa_stat(dest, &fno) != FR_OK) return false; @@ -733,13 +563,13 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { FIL dfile; u64 fsize; - if (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) { - if (!FileUnlock(orig) || (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)) + if (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) { + if (!FileUnlock(orig) || (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)) return false; ShowProgress(0, 0, orig); // reinit progress bar } - fsize = f_size(&ofile); + fsize = fvx_size(&ofile); if (GetFreeSpace(dest) < fsize) { ShowPrompt(false, "%s\nError: File is too big for destination", deststr); fx_close(&ofile); @@ -754,15 +584,15 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { f_lseek(&dfile, 0); f_sync(&dfile); - f_lseek(&ofile, 0); - f_sync(&ofile); + fvx_lseek(&ofile, 0); + fvx_sync(&ofile); ret = true; if (flags && (*flags & CALC_SHA)) sha_init(SHA256_MODE); for (u64 pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { UINT bytes_read = 0; UINT bytes_written = 0; - if (fx_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) + if (fvx_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) ret = false; if (!ShowProgress(pos + (bytes_read / 2), fsize, orig)) ret = false; @@ -775,7 +605,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { } ShowProgress(1, 1, orig); - fx_close(&ofile); + fvx_close(&ofile); fx_close(&dfile); if (!ret) f_unlink(dest); else if (flags && (*flags & CALC_SHA)) { @@ -802,10 +632,8 @@ bool PathCopy(const char* destdir, const char* orig, u32* flags) { char fopath[256]; strncpy(fdpath, destdir, 255); strncpy(fopath, orig, 255); - if (flags && (*flags & BUILD_PATH)) DirBuilderWorker(fdpath); - bool res = (odrvtype & DRV_VIRTUAL) ? PathCopyVrtToFat(fdpath, fopath, flags) : - PathCopyWorker(fdpath, fopath, flags, false); - return res; + if (flags && (*flags & BUILD_PATH)) fvx_rmkdir(destdir); + return PathCopyToFat(fdpath, fopath, flags, false); } else if (!(odrvtype & DRV_VIRTUAL)) { // FAT to virtual if (odrvtype & ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) { ShowPrompt(false, "Copy operation is not allowed"); @@ -830,50 +658,17 @@ bool PathMove(const char* destdir, const char* orig, u32* flags) { char fopath[256]; strncpy(fdpath, destdir, 255); strncpy(fopath, orig, 255); - if (flags && (*flags & BUILD_PATH)) DirBuilderWorker(fdpath); + if (flags && (*flags & BUILD_PATH)) fvx_rmkdir(destdir); bool same_drv = (strncmp(orig, destdir, 2) == 0); - bool res = PathCopyWorker(fdpath, fopath, flags, same_drv); + bool res = PathCopyToFat(fdpath, fopath, flags, same_drv); if (res && (!flags || !(*flags&SKIP_CUR))) PathDelete(orig); return res; } } -bool PathDeleteWorker(char* fpath) { - FILINFO fno; - - // this code handles directory content deletion - if (fa_stat(fpath, &fno) != FR_OK) return false; // fpath does not exist - if (fno.fattrib & AM_DIR) { // process folder contents - DIR pdir; - char* fname = fpath + strnlen(fpath, 255); - - if (fa_opendir(&pdir, fpath) != FR_OK) - return false; - *(fname++) = '/'; - - while (f_readdir(&pdir, &fno) == FR_OK) { - if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) - continue; // filter out virtual entries - strncpy(fname, fno.fname, fpath + 255 - fname); - if (fno.fname[0] == 0) { - break; - } else { // return value won't matter - PathDeleteWorker(fpath); - } - } - f_closedir(&pdir); - *(--fname) = '\0'; - } - - return (fa_unlink(fpath) == FR_OK); -} - bool PathDelete(const char* path) { - char fpath[256]; // 256 is the maximum length of a full path if (!CheckDirWritePermissions(path)) return false; - strncpy(fpath, path, 256); - // ShowString("Deleting files, please wait..."); // handled elsewhere - return PathDeleteWorker(fpath); + return (fvx_runlink(path) == FR_OK); } bool PathRename(const char* path, const char* newname) { diff --git a/source/fs/fsutil.h b/source/fs/fsutil.h index 0f92674..37a0416 100644 --- a/source/fs/fsutil.h +++ b/source/fs/fsutil.h @@ -40,9 +40,6 @@ u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file); /** Inject file into file @offset **/ bool FileInjectFile(const char* dest, const char* orig, u32 offset); -/** Recursively build a directory **/ -bool DirBuilder(const char* destdir); - /** Create a new directory in cpath **/ bool DirCreate(const char* cpath, const char* dirname); diff --git a/source/game/gameutil.c b/source/game/gameutil.c index 0a3ff5d..42ef26a 100644 --- a/source/game/gameutil.c +++ b/source/game/gameutil.c @@ -996,10 +996,8 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt) { if (!CheckWritePermissions(destptr)) return 1; - if (!inplace) { - // ensure the output dir exists - // warning: this will only build output dirs in the root dir (!!!) - if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) + if (!inplace) { // ensure the output dir exists + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) return 1; } @@ -1360,8 +1358,7 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) { f_unlink(dest); // remove the file if it already exists // ensure the output dir exists - // warning: this will only build output dirs in the root dir (!!!) - if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) return 1; // build CIA from game file @@ -1390,8 +1387,7 @@ u32 DumpCxiSrlFromTmdFile(const char* path) { char* dname = dest + strnlen(dest, 256); // ensure the output dir exists - // warning: this will only build output dirs in the root dir (!!!) - if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) return 1; // get path to CXI/SRL and decrypt (if encrypted) @@ -1717,9 +1713,7 @@ u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) { if (dump) { u32 dump_size = TIKDB_SIZE(tik_info); - // ensure the output dir exists - // warning: this will only build output dirs in the root dir (!!!) - if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists return 1; f_unlink(path_out); if ((dump_size <= 16) || (fvx_qwrite(path_out, tik_info, 0, dump_size, &br) != FR_OK) || (br != dump_size)) @@ -1800,9 +1794,7 @@ u32 BuildSeedInfo(const char* path, bool dump) { if (dump) { u32 dump_size = SEEDDB_SIZE(seed_info); - // ensure the output dir exists - // warning: this will only build output dirs in the root dir (!!!) - if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists return 1; f_unlink(path_out); if ((dump_size <= 16) || (fvx_qwrite(path_out, seed_info, 0, dump_size, &br) != FR_OK) || (br != dump_size)) diff --git a/source/game/keydbutil.c b/source/game/keydbutil.c index 2cb436e..554a663 100644 --- a/source/game/keydbutil.c +++ b/source/game/keydbutil.c @@ -19,8 +19,7 @@ u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) { if (!inplace) { // ensure the output dir exists - // warning: this will only build output dirs in the root dir (!!!) - if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) return 1; } @@ -123,9 +122,7 @@ u32 BuildKeyDb(const char* path, bool dump) { dump_size += sizeof(AesKeyInfo); if (dump_size >= MAX_KEYDB_SIZE) return 1; } - // ensure the output dir exists - // warning: this will only build output dirs in the root dir (!!!) - if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists return 1; f_unlink(path_out); if (!dump_size || (fvx_qwrite(path_out, key_info, 0, dump_size, &br) != FR_OK) || (br != dump_size)) diff --git a/source/virtual/vff.c b/source/virtual/vff.c index 7dca181..0f82141 100644 --- a/source/virtual/vff.c +++ b/source/virtual/vff.c @@ -1,11 +1,22 @@ #include "sddata.h" #include "virtual.h" #include "ffconf.h" +#include "vff.h" + +#if _USE_LFN != 0 +#define _MAX_FN_LEN (_MAX_LFN) +#else +#define _MAX_FN_LEN (8+3) +#endif + +#define _VFIL_ENABLED (!_FS_TINY) +#define _VDIR_ENABLED ((sizeof(DIR) - sizeof(_FDID) >= sizeof(VirtualDir)) && (_USE_LFN != 0)) #define VFIL(fp) ((VirtualFile*) (void*) fp->buf) +#define VDIR(dp) ((VirtualDir*) (void*) &(dp->dptr)) FRESULT fvx_open (FIL* fp, const TCHAR* path, BYTE mode) { - #if !_FS_TINY + #if _VFIL_ENABLED VirtualFile* vfile = VFIL(fp); memset(fp, 0, sizeof(FIL)); if (GetVirtualFile(vfile, path)) { @@ -19,7 +30,7 @@ FRESULT fvx_open (FIL* fp, const TCHAR* path, BYTE mode) { } FRESULT fvx_read (FIL* fp, void* buff, UINT btr, UINT* br) { - #if !_FS_TINY + #if _VFIL_ENABLED if (fp->obj.fs == NULL) { VirtualFile* vfile = VFIL(fp); int res = ReadVirtualFile(vfile, buff, fp->fptr, btr, (u32*) br); @@ -31,7 +42,7 @@ FRESULT fvx_read (FIL* fp, void* buff, UINT btr, UINT* br) { } FRESULT fvx_write (FIL* fp, const void* buff, UINT btw, UINT* bw) { - #if !_FS_TINY + #if _VFIL_ENABLED if (fp->obj.fs == NULL) { VirtualFile* vfile = VFIL(fp); int res = WriteVirtualFile(vfile, buff, fp->fptr, btw, (u32*) bw); @@ -43,14 +54,14 @@ FRESULT fvx_write (FIL* fp, const void* buff, UINT btw, UINT* bw) { } FRESULT fvx_close (FIL* fp) { - #if !_FS_TINY + #if _VFIL_ENABLED if (fp->obj.fs == NULL) return FR_OK; #endif return fx_close( fp ); } FRESULT fvx_lseek (FIL* fp, FSIZE_t ofs) { - #if !_FS_TINY + #if _VFIL_ENABLED if (fp->obj.fs == NULL) { fp->fptr = ofs; return FR_OK; @@ -60,7 +71,7 @@ FRESULT fvx_lseek (FIL* fp, FSIZE_t ofs) { } FRESULT fvx_sync (FIL* fp) { - #if !_FS_TINY + #if _VFIL_ENABLED if (fp->obj.fs == NULL) return FR_OK; #endif return f_sync( fp ); @@ -72,16 +83,66 @@ FRESULT fvx_stat (const TCHAR* path, FILINFO* fno) { if (!GetVirtualFile(&vfile, path)) return FR_NO_PATH; fno->fsize = vfile.size; fno->fdate = fno->ftime = 0; - fno->fattrib = (vfile.flags & VFLAG_DIR) ? AM_DIR : 0; + fno->fattrib = (vfile.flags & VFLAG_DIR) ? (AM_DIR|AM_VRT) : AM_VRT; // could be better... if (_USE_LFN != 0) GetVirtualFilename(fno->fname, &vfile, _MAX_LFN + 1); return FR_OK; - } else return fa_stat(path, fno); + } else return fa_stat( path, fno ); +} + +FRESULT fvx_rename (const TCHAR* path_old, const TCHAR* path_new) { + if ((GetVirtualSource(path_old)) || CheckAliasDrive(path_old)) return FR_DENIED; + return f_rename( path_old, path_new ); +} + +FRESULT fvx_unlink (const TCHAR* path) { + if (GetVirtualSource(path)) return FR_DENIED; + return fa_unlink( path ); +} + +FRESULT fvx_mkdir (const TCHAR* path) { + if (GetVirtualSource(path)) return FR_DENIED; + return fa_mkdir( path ); +} + +FRESULT fvx_opendir (DIR* dp, const TCHAR* path) { + if (_VDIR_ENABLED) { + VirtualDir* vdir = VDIR(dp); + memset(dp, 0, sizeof(DIR)); + if (GetVirtualDir(vdir, path)) + return FR_OK; + } + return fa_opendir( dp, path ); +} + +FRESULT fvx_closedir (DIR* dp) { + if (_VDIR_ENABLED) { + if (dp->obj.fs == NULL) return FR_OK; + } + return f_closedir( dp ); +} + +FRESULT fvx_readdir (DIR* dp, FILINFO* fno) { + if (_VDIR_ENABLED) { + if (dp->obj.fs == NULL) { + VirtualDir* vdir = VDIR(dp); + VirtualFile vfile; + if (ReadVirtualDir(&vfile, vdir)) { + fno->fsize = vfile.size; + fno->fdate = fno->ftime = 0; + fno->fattrib = (vfile.flags & VFLAG_DIR) ? (AM_DIR|AM_VRT) : AM_VRT; + GetVirtualFilename(fno->fname, &vfile, _MAX_LFN + 1); + } else *(fno->fname) = 0; + return FR_OK; + } + } + return f_readdir( dp, fno ); } FRESULT fvx_qread (const TCHAR* path, void* buff, FSIZE_t ofs, UINT btr, UINT* br) { FIL fp; FRESULT res; + UINT brt = 0; res = fvx_open(&fp, path, FA_READ | FA_OPEN_EXISTING); if (res != FR_OK) return res; @@ -92,15 +153,19 @@ FRESULT fvx_qread (const TCHAR* path, void* buff, FSIZE_t ofs, UINT btr, UINT* b return res; } - res = fvx_read(&fp, buff, btr, br); + res = fvx_read(&fp, buff, btr, &brt); fvx_close(&fp); + if (br) *br = brt; + else if ((res == FR_OK) && (brt != btr)) res = FR_DENIED; + return res; } FRESULT fvx_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw, UINT* bw) { FIL fp; FRESULT res; + UINT bwt = 0; res = fvx_open(&fp, path, FA_WRITE | FA_OPEN_ALWAYS); if (res != FR_OK) return res; @@ -111,8 +176,141 @@ FRESULT fvx_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw, return res; } - res = fvx_write(&fp, buff, btw, bw); + res = fvx_write(&fp, buff, btw, &bwt); fvx_close(&fp); + if (bw) *bw = bwt; + else if ((res == FR_OK) && (bwt != btw)) res = FR_DENIED; + return res; } + +#if !_LFN_UNICODE // this will not work for unicode +FRESULT worker_fvx_rmkdir (TCHAR* tpath) { + DIR tmp_dir; + if (fa_opendir(&tmp_dir, tpath) != FR_OK) { + TCHAR* slash = strrchr(tpath, '/'); + if (!slash) return FR_DENIED; + *slash = '\0'; + FRESULT res; + if ((res = worker_fvx_rmkdir(tpath)) != FR_OK) return res; + *slash = '/'; + return fa_mkdir(tpath); + } else { + f_closedir(&tmp_dir); + return FR_OK; + } +} +#endif + +FRESULT fvx_rmkdir (const TCHAR* path) { + #if !_LFN_UNICODE // this will not work for unicode + TCHAR tpath[_MAX_FN_LEN+1]; + strncpy(tpath, path, _MAX_FN_LEN); + return worker_fvx_rmkdir( tpath ); + #else + return FR_DENIED; + #endif +} + +#if !_LFN_UNICODE // this will not work for unicode +FRESULT worker_fvx_runlink (TCHAR* tpath) { + FILINFO fno; + FRESULT res; + + // this code handles directory content deletion + if ((res = fa_stat(tpath, &fno)) != FR_OK) return res; // tpath does not exist + if (fno.fattrib & AM_DIR) { // process folder contents + DIR pdir; + TCHAR* fname = tpath + strnlen(tpath, 255); + + + if ((res = fa_opendir(&pdir, tpath)) != FR_OK) return res; + *(fname++) = '/'; + + while (f_readdir(&pdir, &fno) == FR_OK) { + if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) + continue; // filter out virtual entries + strncpy(fname, fno.fname, tpath + 255 - fname); + if (fno.fname[0] == 0) { + break; + } else { // return value won't matter + worker_fvx_runlink(tpath); + } + } + f_closedir(&pdir); + *(--fname) = '\0'; + } + + return fa_unlink( tpath ); +} +#endif + +FRESULT fvx_runlink (const TCHAR* path) { + #if !_LFN_UNICODE // this will not work for unicode + TCHAR tpath[_MAX_FN_LEN+1]; + strncpy(tpath, path, _MAX_FN_LEN); + return worker_fvx_runlink( tpath ); + #else + return FR_DENIED; + #endif +} + +// inspired by http://www.geeksforgeeks.org/wildcard-character-matching/ +FRESULT fvx_match_name(const TCHAR* path, const TCHAR* pattern) { + // handling non asterisk chars + for (; *pattern != '*'; pattern++, path++) { + if ((*pattern == '\0') && (*path == '\0')) { + return FR_OK; // end reached simultaneously, match found + } else if ((*pattern == '\0') || (*path == '\0')) { + return FR_NO_FILE; // end reached on only one, failure + } else if ((*pattern != '?') && (tolower(*pattern) != tolower(*path))) { + return FR_NO_FILE; // chars don't match, failure + } + } + // handling the asterisk (matches one or more chars in path) + if ((*(pattern+1) == '?') || (*(pattern+1) == '*')) { + return FR_NO_FILE; // stupid user shenanigans, failure + } else if (*path == '\0') { + return FR_NO_FILE; // asterisk, but end reached on path, failure + } else if (*(pattern+1) == '\0') { + return FR_OK; // nothing after the asterisk, match found + } else { // we couldn't really go without recursion here + for (path++; *path != '\0'; path++) { + if (fvx_match_name(path, pattern + 1) == FR_OK) return FR_OK; + } + } + + return FR_NO_FILE; +} + +FRESULT fvx_preaddir (DIR* dp, FILINFO* fno, const TCHAR* pattern) { + FRESULT res; + while ((res = fvx_readdir(dp, fno)) == FR_OK) + if (!pattern || !*(fno->fname) || (fvx_match_name(fno->fname, pattern) == FR_OK)) break; + return res; +} + +FRESULT fvx_findpath (TCHAR* path, const TCHAR* pattern) { + strncpy(path, pattern, _MAX_FN_LEN); + TCHAR* fname = strrchr(path, '/'); + if (!fname) return FR_DENIED; + *fname = '\0'; + + TCHAR* npattern = strrchr(pattern, '/'); + if (!npattern) return FR_DENIED; + npattern++; + + DIR pdir; + FILINFO fno; + FRESULT res; + if ((res = fvx_opendir(&pdir, path)) != FR_OK) return res; + if (fvx_preaddir(&pdir, &fno, npattern) != FR_OK) *(fno.fname) = '\0'; + fvx_closedir( &pdir ); + + *(fname++) = '/'; + strncpy(fname, fno.fname, _MAX_FN_LEN - (fname - path)); + if (!*(fno.fname)) return FR_NO_FILE; + + return FR_OK; +} diff --git a/source/virtual/vff.h b/source/virtual/vff.h index 5ba7c19..550b764 100644 --- a/source/virtual/vff.h +++ b/source/virtual/vff.h @@ -3,6 +3,8 @@ #include "common.h" #include "ff.h" +#define AM_VRT 0x40 // Virtual (FILINFO FAT attribute) + #define fvx_tell(fp) ((fp)->fptr) #define fvx_size(fp) ((fp)->obj.objsize) @@ -15,7 +17,22 @@ FRESULT fvx_close (FIL* fp); FRESULT fvx_lseek (FIL* fp, FSIZE_t ofs); FRESULT fvx_sync (FIL* fp); FRESULT fvx_stat (const TCHAR* path, FILINFO* fno); +FRESULT fvx_rename (const TCHAR* path_old, const TCHAR* path_new); +FRESULT fvx_unlink (const TCHAR* path); +FRESULT fvx_mkdir (const TCHAR* path); +FRESULT fvx_opendir (DIR* dp, const TCHAR* path); +FRESULT fvx_closedir (DIR* dp); +FRESULT fvx_readdir (DIR* dp, FILINFO* fno); // additional quick read / write functions FRESULT fvx_qread (const TCHAR* path, void* buff, FSIZE_t ofs, UINT btr, UINT* br); FRESULT fvx_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw, UINT* bw); + +// additional recursive functions +FRESULT fvx_rmkdir (const TCHAR* path); +FRESULT fvx_runlink (const TCHAR* path); + +// additional wildcard based functions +FRESULT fvx_match_name(const TCHAR* path, const TCHAR* pattern); +FRESULT fvx_preaddir (DIR* dp, FILINFO* fno, const TCHAR* pattern); +FRESULT fvx_findfile (const TCHAR* path); diff --git a/source/virtual/virtual.c b/source/virtual/virtual.c index f12da1a..b1cf508 100644 --- a/source/virtual/virtual.c +++ b/source/virtual/virtual.c @@ -131,38 +131,6 @@ bool GetVirtualDir(VirtualDir* vdir, const char* path) { return GetVirtualFile(&vfile, path) && OpenVirtualDir(vdir, &vfile); } -bool GetVirtualDirContents(DirStruct* contents, char* fpath, int fnsize, const char* pattern, bool recursive) { - VirtualDir vdir; - VirtualFile vfile; - char* fname = fpath + strnlen(fpath, fnsize - 1); - (fname++)[0] = '/'; - if (!GetVirtualDir(&vdir, fpath)) - return false; // get dir reader object - while ((contents->n_entries < MAX_DIR_ENTRIES) && (ReadVirtualDir(&vfile, &vdir))) { - DirEntry* entry = &(contents->entry[contents->n_entries]); - char name[256]; - GetVirtualFilename(name, &vfile, 256); - strncpy(fname, name, (fnsize - 1) - (fname - fpath)); - if (!pattern || MatchName(pattern, fname)) { - strncpy(entry->path, fpath, 256); - entry->name = entry->path + (fname - fpath); - entry->size = vfile.size; - entry->type = (vfile.flags & VFLAG_DIR) ? T_DIR : T_FILE; - entry->marked = 0; - if (contents->n_entries >= MAX_DIR_ENTRIES) - break; // Too many entries, still okay - if (!recursive || (entry->type != T_DIR)) - contents->n_entries++; - } - if (recursive && (vfile.flags & VFLAG_DIR)) { - if (!GetVirtualDirContents(contents, fpath, fnsize, pattern, recursive)) - break; - } - } - - return true; // not much we can check here -} - bool GetVirtualFilename(char* name, const VirtualFile* vfile, u32 n_chars) { if (!(vfile->flags & VFLAG_LV3)) strncpy(name, vfile->name, n_chars); else if (!GetVGameLv3Filename(name, vfile, n_chars)) return false; diff --git a/source/virtual/virtual.h b/source/virtual/virtual.h index 3f1919c..ce7775f 100644 --- a/source/virtual/virtual.h +++ b/source/virtual/virtual.h @@ -1,7 +1,6 @@ #pragma once #include "common.h" -#include "fsdir.h" #include "nand.h" #define VRT_SYSNAND NAND_SYSNAND @@ -54,8 +53,6 @@ bool OpenVirtualDir(VirtualDir* vdir, VirtualFile* ventry); bool GetVirtualFile(VirtualFile* vfile, const char* path); bool GetVirtualDir(VirtualDir* vdir, const char* path); -bool GetVirtualDirContents(DirStruct* contents, char* fpath, int fnsize, const char* pattern, bool recursive); - bool GetVirtualFilename(char* name, const VirtualFile* vfile, u32 n_chars); int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count, u32* bytes_read);