diff --git a/source/common/common.h b/source/common/common.h index f5b2c64..0a7c38c 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -54,6 +54,7 @@ // input / output paths #define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9" #define OUTPUT_PATH "0:/gm9out" +#define SCRIPT_PATH "0:/gm9/scripts" // buffer area defines (in use by godmode.c) #define DIR_BUFFER (0x21000000) diff --git a/source/common/ui.c b/source/common/ui.c index ee9c511..3050e03 100644 --- a/source/common/ui.c +++ b/source/common/ui.c @@ -369,11 +369,13 @@ u32 ShowSelectPrompt(u32 n, const char** options, const char *format, ...) { va_end(va); if (n == 0) return 0; // check for low number of options - else if (n == 1) return ShowPrompt(true, "%s\n%s?", str, options[0]) ? 1 : 0; + // else if (n == 1) return ShowPrompt(true, "%s\n%s?", str, options[0]) ? 1 : 0; str_width = GetDrawStringWidth(str); str_height = GetDrawStringHeight(str) + (n * 12) + (3 * 10); if (str_width < 24 * FONT_WIDTH) str_width = 24 * FONT_WIDTH; + for (u32 i = 0; i < n; i++) if (str_width < GetDrawStringWidth(options[i])) + str_width = GetDrawStringWidth(options[i]); x = (str_width >= SCREEN_WIDTH_MAIN) ? 0 : (SCREEN_WIDTH_MAIN - str_width) / 2; y = (str_height >= SCREEN_HEIGHT) ? 0 : (SCREEN_HEIGHT - str_height) / 2; yopt = y + GetDrawStringHeight(str) + 8; diff --git a/source/filesys/fsutil.c b/source/filesys/fsutil.c index 311f3ac..d6c9dcc 100644 --- a/source/filesys/fsutil.c +++ b/source/filesys/fsutil.c @@ -14,6 +14,8 @@ #define SKIP_CUR (1UL<<8) #define OVERWRITE_CUR (1UL<<9) +#define _MAX_FS_OPT 8 // max file selector options + // Volume2Partition resolution table PARTITION VolToPart[] = { {0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, @@ -322,6 +324,10 @@ bool DirInfo(const char* path, u64* tsize, u32* tdirs, u32* tfiles) { return res; } +bool PathExist(const char* path) { + return (fvx_stat(path, NULL) == FR_OK); +} + bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move) { bool to_virtual = GetVirtualSource(dest); bool silent = (flags && (*flags & SILENT)); @@ -636,6 +642,68 @@ bool PathRename(const char* path, const char* newname) { return (f_rename(path, npath) == FR_OK); } +bool FileSelector(char* result, const char* text, const char* path, const char* pattern, bool hide_ext, bool no_dirs) { + DirStruct* contents = (DirStruct*) (void*) TEMP_BUFFER; + char path_local[256]; + strncpy(path_local, path, 256); + + // main loop + while (true) { + u32 n_found = 0; + u32 pos = 0; + GetDirContents(contents, path_local); + + while (pos < contents->n_entries) { + char opt_names[_MAX_FS_OPT+1][32+1]; + DirEntry* res_entry[_MAX_FS_OPT+1] = { NULL }; + u32 n_opt = 0; + for (; pos < contents->n_entries; pos++) { + DirEntry* entry = &(contents->entry[pos]); + if (!((entry->type == T_DIR) && (!no_dirs)) && + !((entry->type == T_FILE) && (fvx_match_name(entry->name, pattern) == FR_OK))) + continue; + if (n_opt == _MAX_FS_OPT) { + snprintf(opt_names[n_opt++], 32, "[more...]"); + break; + } + + char temp_str[256]; + snprintf(temp_str, 256, entry->name); + if (hide_ext && (entry->type == T_FILE)) { + char* dot = strrchr(temp_str, '.'); + if (dot) *dot = '\0'; + } + TruncateString(opt_names[n_opt], temp_str, 32, 8); + res_entry[n_opt++] = entry; + n_found++; + } + if ((pos >= contents->n_entries) && (n_opt < n_found)) + snprintf(opt_names[n_opt++], 32, "[more...]"); + if (!n_opt) break; + + const char* optionstr[_MAX_FS_OPT+1] = { NULL }; + for (u32 i = 0; i <= _MAX_FS_OPT; i++) optionstr[i] = opt_names[i]; + u32 user_select = ShowSelectPrompt(n_opt, optionstr, text); + if (!user_select) return false; + DirEntry* res_local = res_entry[user_select-1]; + if (res_local && (res_local->type == T_DIR)) { // selected dir + if (FileSelector(result, text, res_local->path, pattern, hide_ext, no_dirs)) + return true; + break; + } else if (res_local && (res_local->type == T_FILE)) { // selected file + strncpy(result, res_local->path, 256); + return true; + } + } + if (!n_found) { // not a single matching entry found + char pathstr[32+1]; + TruncateString(pathstr, path_local, 32, 8); + ShowPrompt(false, "%s\nNo usable entries found.", pathstr); + return false; + } + } +} + void CreateScreenshot() { const u8 bmp_header[54] = { 0x42, 0x4D, 0x36, 0xCA, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, diff --git a/source/filesys/fsutil.h b/source/filesys/fsutil.h index d4e7f1d..2e80ccc 100644 --- a/source/filesys/fsutil.h +++ b/source/filesys/fsutil.h @@ -48,6 +48,9 @@ bool DirCreate(const char* cpath, const char* dirname); /** Get # of files, subdirs and total size for directory **/ bool DirInfo(const char* path, u64* tsize, u32* tdirs, u32* tfiles); +/** True if path exists **/ +bool PathExist(const char* path); + /** Direct recursive move / copy of files or directories **/ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move); @@ -63,5 +66,8 @@ bool PathDelete(const char* path); /** Rename file / folder in path to new name **/ bool PathRename(const char* path, const char* newname); +/** Select a file **/ +bool FileSelector(char* result, const char* text, const char* path, const char* pattern, bool hide_ext, bool no_dirs); + /** Create a screenshot of the current framebuffer **/ void CreateScreenshot(); diff --git a/source/godmode.c b/source/godmode.c index 2af0bc7..8f1828d 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -1217,19 +1217,19 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar const char* optionstr[8]; const char* promptstr = "HOME more... menu.\nSelect action:"; u32 n_opt = 0; - int nandbak = ++n_opt; int sdformat = ++n_opt; int bonus = (np_info.count > 0x2000) ? (int) ++n_opt : -1; // 4MB minsize int multi = (CheckMultiEmuNand()) ? (int) ++n_opt : -1; int bsupport = ++n_opt; int hsrestore = ((CheckHealthAndSafetyInject("1:") == 0) || (CheckHealthAndSafetyInject("4:") == 0)) ? (int) ++n_opt : -1; + int scripts = (PathExist(SCRIPT_PATH)) ? (int) ++n_opt : -1; - if (nandbak > 0) optionstr[nandbak - 1] = "Backup NAND"; if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu"; if (bonus > 0) optionstr[bonus - 1] = "Bonus drive setup"; if (multi > 0) optionstr[multi - 1] = "Switch EmuNAND"; if (bsupport > 0) optionstr[bsupport - 1] = "Build support files"; if (hsrestore > 0) optionstr[hsrestore - 1] = "Restore H&S"; + if (scripts > 0) optionstr[scripts - 1] = "Scripts..."; int user_select = ShowSelectPrompt(n_opt, optionstr, promptstr); if (user_select == sdformat) { // format SD card @@ -1310,19 +1310,10 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar GetDirContents(current_dir, current_path); return 0; } - } else if (user_select == nandbak) { // dump NAND backup - n_opt = 0; - int sys = (DriveType("1:")) ? (int) ++n_opt : -1; - int emu = (DriveType("4:")) ? (int) ++n_opt : -1; - if (sys > 0) optionstr[sys - 1] = "Backup SysNAND"; - if (emu > 0) optionstr[emu - 1] = "Backup EmuNAND"; - user_select = (n_opt > 1) ? ShowSelectPrompt(n_opt, optionstr, promptstr) : n_opt; - if (user_select > 0) { - u32 flags = BUILD_PATH | CALC_SHA; - if (PathCopy(OUTPUT_PATH, (user_select == sys) ? "S:/nand.bin" : "E:/nand.bin", &flags)) - ShowPrompt(false, "NAND backup written to " OUTPUT_PATH); - else ShowPrompt(false, "NAND backup failed"); - GetDirContents(current_dir, current_path); + } else if (user_select == scripts) { // scripts menu + char script[256]; + if (FileSelector(script, "HOME scripts... menu.\nSelect action:", SCRIPT_PATH, "*.gm9", true, false)) { + ExecuteGM9Script(script); return 0; } } else return 1;