diff --git a/source/fatfs/ffconf.h b/source/fatfs/ffconf.h index 1043cf2..f0fe182 100644 --- a/source/fatfs/ffconf.h +++ b/source/fatfs/ffconf.h @@ -34,7 +34,7 @@ / 2: Enable with LF-CRLF conversion. */ -#define _USE_FIND 1 +#define _USE_FIND 0 /* This option switches filtered directory read functions, f_findfirst() and / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ diff --git a/source/fatfs/store.c b/source/fatfs/store.c new file mode 100644 index 0000000..47cb02e --- /dev/null +++ b/source/fatfs/store.c @@ -0,0 +1,44 @@ +#include "store.h" +#include "ff.h" + +#define STORE_BUFFER ((DirStruct*)0x21300000) + +static bool is_stored = false; + +bool IsStoredDrive(const char* path) { + return is_stored && (strncmp(path, "Z:", 3) == 0); +} + +void StoreDirContents(DirStruct* contents) { + memcpy(STORE_BUFFER, contents, sizeof(DirStruct)); + is_stored = true; +} + +void GetStoredDirContents(DirStruct* contents) { + // warning: this assumes the store buffer is filled with data that makes sense + DirStruct* stored = STORE_BUFFER; + u32 skip = 0; + + // basic sanity checking + if (!is_stored || (stored->n_entries > MAX_ENTRIES)) return; + + // copy available entries, remove missing from storage + for (u32 i = 0; i < stored->n_entries; i++) { + DirEntry* entry = &(stored->entry[i]); + if (strncmp(entry->name, "..", 3) == 0) continue; // disregard dotdot entry + if (f_stat(entry->path, NULL) != FR_OK) { + skip++; // remember this has to be removed from the stored struct + } else { // entry is valid + if (skip) { // move remaining entries to the left + stored->n_entries -= skip; + memmove(entry - skip, entry, (stored->n_entries - i) * sizeof(DirEntry)); + entry -= skip; + skip = 0; + } + if (contents->n_entries < MAX_ENTRIES) + memcpy(&(contents->entry[contents->n_entries++]), entry, sizeof(DirEntry)); + else break; + } + } + stored->n_entries -= skip; +} diff --git a/source/fatfs/store.h b/source/fatfs/store.h new file mode 100644 index 0000000..75328bd --- /dev/null +++ b/source/fatfs/store.h @@ -0,0 +1,7 @@ +#pragma once + +#include "fs.h" + +bool IsStoredDrive(const char* path); +void StoreDirContents(DirStruct* contents); +void GetStoredDirContents(DirStruct* contents); diff --git a/source/fs.c b/source/fs.c index 494652f..f13af64 100644 --- a/source/fs.c +++ b/source/fs.c @@ -2,6 +2,7 @@ #include "fs.h" #include "virtual.h" #include "image.h" +#include "store.h" #include "sha.h" #include "sdmmc.h" #include "ff.h" @@ -74,7 +75,7 @@ void DeinitSDCardFS() { int PathToNumFS(const char* path) { int fsnum = *path - (int) '0'; if ((fsnum < 0) || (fsnum >= NORM_FS) || (path[1] != ':')) { - if (!GetVirtualSource(path)) ShowPrompt(false, "Invalid path (%s)", path); + if (!GetVirtualSource(path) && !IsStoredDrive(path)) ShowPrompt(false, "Invalid path (%s)", path); return -1; } return fsnum; @@ -949,6 +950,34 @@ 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; +} + bool GetRootDirContentsWorker(DirStruct* contents) { static const char* drvname[] = { "SDCARD", @@ -986,11 +1015,12 @@ bool GetRootDirContentsWorker(DirStruct* contents) { return contents->n_entries; } -bool GetVirtualDirContentsWorker(DirStruct* contents, const char* path) { +bool GetVirtualDirContentsWorker(DirStruct* contents, const char* path, const char* pattern) { if (strchr(path, '/')) return false; // only top level paths for (u32 n = 0; (n < virtualFileList_size) && (contents->n_entries < MAX_ENTRIES); n++) { VirtualFile vfile; DirEntry* entry = &(contents->entry[contents->n_entries]); + if (pattern && !MatchName(pattern, virtualFileList[n])) continue; snprintf(entry->path, 256, "%s/%s", path, virtualFileList[n]); if (!FindVirtualFile(&vfile, entry->path, 0)) continue; entry->name = entry->path + strnlen(path, 256) + 1; @@ -1003,7 +1033,7 @@ bool GetVirtualDirContentsWorker(DirStruct* contents, const char* path) { return true; // not much we can check here } -bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, bool recursive) { +bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const char* pattern, bool recursive) { DIR pdir; FILINFO fno; char* fname = fpath + strnlen(fpath, fnsize - 1); @@ -1020,7 +1050,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, bool rec if (fno.fname[0] == 0) { ret = true; break; - } else { + } else if (!pattern || MatchName(pattern, fname)) { DirEntry* entry = &(contents->entry[contents->n_entries]); strncpy(entry->path, fpath, 256); entry->name = entry->path + (fname - fpath); @@ -1037,7 +1067,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, bool rec break; } if (recursive && (fno.fattrib & AM_DIR)) { - if (!GetDirContentsWorker(contents, fpath, fnsize, recursive)) + if (!GetDirContentsWorker(contents, fpath, fnsize, pattern, recursive)) break; } } @@ -1046,7 +1076,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, bool rec return ret; } -void GetDirContents(DirStruct* contents, const char* path) { +void SearchDirContents(DirStruct* contents, const char* path, const char* pattern, bool recursive) { contents->n_entries = 0; if (!(*path)) { // root directory if (!GetRootDirContentsWorker(contents)) @@ -1060,18 +1090,22 @@ void GetDirContents(DirStruct* contents, const char* path) { contents->entry->size = 0; contents->n_entries = 1; if (GetVirtualSource(path)) { - if (!GetVirtualDirContentsWorker(contents, path)) + if (!GetVirtualDirContentsWorker(contents, path, pattern)) contents->n_entries = 0; } else { char fpath[256]; // 256 is the maximum length of a full path strncpy(fpath, path, 256); - if (!GetDirContentsWorker(contents, fpath, 256, false)) + if (!GetDirContentsWorker(contents, fpath, 256, pattern, recursive)) contents->n_entries = 0; } SortDirStruct(contents); } } +void GetDirContents(DirStruct* contents, const char* path) { + SearchDirContents(contents, path, NULL, false); +} + uint64_t GetFreeSpace(const char* path) { DWORD free_clusters; diff --git a/source/fs.h b/source/fs.h index 8dd26cc..51e6495 100644 --- a/source/fs.h +++ b/source/fs.h @@ -90,6 +90,9 @@ bool DirCreate(const char* cpath, const char* dirname); /** Create a screenshot of the current framebuffer **/ void CreateScreenshot(); +/** Search under a given path **/ +void SearchDirContents(DirStruct* contents, const char* path, const char* pattern, bool recursive); + /** Get directory content under a given path **/ void GetDirContents(DirStruct* contents, const char* path); diff --git a/source/godmode.c b/source/godmode.c index a2b43c4..eb48edc 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -6,8 +6,9 @@ #include "nand.h" #include "virtual.h" #include "image.h" +#include "store.h" -#define VERSION "0.6.2" +#define VERSION "0.6.3" #define N_PANES 2 #define IMG_DRV "789I" @@ -544,13 +545,26 @@ u32 GodMode() { } // basic navigation commands - if ((pad_state & BUTTON_A) && (curr_entry->type != T_FILE) && (curr_entry->type != T_DOTDOT)) { // one level up - strncpy(current_path, curr_entry->path, 256); - GetDirContents(current_dir, current_path); - if (*current_path && (current_dir->n_entries > 1)) { - cursor = 1; - scroll = 0; - } else cursor = 0; + if ((pad_state & BUTTON_A) && (curr_entry->type != T_FILE) && (curr_entry->type != T_DOTDOT)) { // for dirs + if (switched) { // search directory + char searchstr[256]; + char namestr[20+1]; + snprintf(searchstr, 256, "*.*"); + TruncateString(namestr, curr_entry->name, 20, 8); + if (ShowStringPrompt(searchstr, 256, "Search %s?\nEnter search below.", namestr)) { + ShowString("Searching path, please wait..."); + snprintf(current_path, 256, "Z:"); + SearchDirContents(current_dir, curr_entry->path, searchstr, true); + StoreDirContents(current_dir); + } + } else { // one level up + strncpy(current_path, curr_entry->path, 256); + GetDirContents(current_dir, current_path); + if (*current_path && (current_dir->n_entries > 1)) { + cursor = 1; + scroll = 0; + } else cursor = 0; + } } else if ((pad_state & BUTTON_A) && (curr_entry->type == T_FILE)) { // process a file u32 file_type = IdentifyImage(curr_entry->path); bool injectable = (clipboard->n_entries == 1) && diff --git a/source/nand/nand.c b/source/nand/nand.c index d9776e3..e5c90da 100644 --- a/source/nand/nand.c +++ b/source/nand/nand.c @@ -6,7 +6,7 @@ #include "nand.h" #include "image.h" -#define NAND_BUFFER ((u8*)0x21300000) +#define NAND_BUFFER ((u8*)0x21400000) #define NAND_BUFFER_SIZE (0x100000) // must be multiple of 0x200 #define NAND_MIN_SECTORS ((GetUnitPlatform() == PLATFORM_N3DS) ? 0x26C000 : 0x1D7800) diff --git a/source/ui.c b/source/ui.c index 8a573fc..d2aee83 100644 --- a/source/ui.c +++ b/source/ui.c @@ -440,7 +440,7 @@ bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alpha } bool ShowStringPrompt(char* inputstr, u32 max_size, const char *format, ...) { - const char* alphabet = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz(){}[]'`^,~!@#$%&0123456789=+-_."; + const char* alphabet = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz(){}[]'`^,~*?!@#$%&0123456789=+-_."; bool ret = false; va_list va;