diff --git a/source/dir.c b/source/dir.c new file mode 100644 index 0000000..9abd957 --- /dev/null +++ b/source/dir.c @@ -0,0 +1,58 @@ +#include "dir.h" + +void DirEntryCpy(DirEntry* dest, const DirEntry* orig) { + memcpy(dest, orig, sizeof(DirEntry)); + dest->name = dest->path + (orig->name - orig->path); +} + +void SortDirStruct(DirStruct* contents) { + for (u32 s = 0; s < contents->n_entries; s++) { + DirEntry* cmp0 = &(contents->entry[s]); + DirEntry* min0 = cmp0; + if (cmp0->type == T_DOTDOT) continue; + for (u32 c = s + 1; c < contents->n_entries; c++) { + DirEntry* cmp1 = &(contents->entry[c]); + if (min0->type != cmp1->type) { + if (min0->type > cmp1->type) + min0 = cmp1; + continue; + } + if (strncasecmp(min0->name, cmp1->name, 256) > 0) + min0 = cmp1; + } + if (min0 != cmp0) { + DirEntry swap; // swap entries and fix names + DirEntryCpy(&swap, cmp0); + DirEntryCpy(cmp0, min0); + DirEntryCpy(min0, &swap); + } + } +} + +// 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/dir.h b/source/dir.h new file mode 100644 index 0000000..0461673 --- /dev/null +++ b/source/dir.h @@ -0,0 +1,29 @@ +#pragma once + +#include "common.h" + +#define MAX_DIR_ENTRIES 1024 + +typedef enum { + T_ROOT, + T_DIR, + T_FILE, + T_DOTDOT +} EntryType; + +typedef struct { + char* name; // should point to the correct portion of the path + char path[256]; + u64 size; + EntryType type; + u8 marked; +} DirEntry; + +typedef struct { + u32 n_entries; + DirEntry entry[MAX_DIR_ENTRIES]; +} DirStruct; + +void DirEntryCpy(DirEntry* dest, const DirEntry* orig); +void SortDirStruct(DirStruct* contents); +bool MatchName(const char *pattern, const char *path); diff --git a/source/fs.c b/source/fs.c index 675f3e8..13efb32 100644 --- a/source/fs.c +++ b/source/fs.c @@ -1029,63 +1029,6 @@ void CreateScreenshot() { FileSetData(filename, MAIN_BUFFER, 54 + (400 * 240 * 3 * 2), 0, true); } -void DirEntryCpy(DirEntry* dest, const DirEntry* orig) { - memcpy(dest, orig, sizeof(DirEntry)); - dest->name = dest->path + (orig->name - orig->path); -} - -void SortDirStruct(DirStruct* contents) { - for (u32 s = 0; s < contents->n_entries; s++) { - DirEntry* cmp0 = &(contents->entry[s]); - DirEntry* min0 = cmp0; - if (cmp0->type == T_DOTDOT) continue; - for (u32 c = s + 1; c < contents->n_entries; c++) { - DirEntry* cmp1 = &(contents->entry[c]); - if (min0->type != cmp1->type) { - if (min0->type > cmp1->type) - min0 = cmp1; - continue; - } - if (strncasecmp(min0->name, cmp1->name, 256) > 0) - min0 = cmp1; - } - if (min0 != cmp0) { - DirEntry swap; // swap entries and fix names - DirEntryCpy(&swap, cmp0); - DirEntryCpy(cmp0, min0); - DirEntryCpy(min0, &swap); - } - } -} - -// 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", @@ -1103,7 +1046,7 @@ bool GetRootDirContentsWorker(DirStruct* contents) { u32 n_entries = 0; // virtual root objects hacked in - for (u32 pdrv = 0; (pdrv < NORM_FS+VIRT_FS) && (n_entries < MAX_ENTRIES); pdrv++) { + for (u32 pdrv = 0; (pdrv < NORM_FS+VIRT_FS) && (n_entries < MAX_DIR_ENTRIES); pdrv++) { DirEntry* entry = &(contents->entry[n_entries]); if (!DriveType(drvnum[pdrv])) continue; // drive not available memset(entry->path, 0x00, 64); @@ -1124,24 +1067,6 @@ bool GetRootDirContentsWorker(DirStruct* contents) { return contents->n_entries; } -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; - entry->size = vfile.size; - entry->type = T_FILE; - entry->marked = 0; - contents->n_entries++; - } - - return true; // not much we can check here -} - bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const char* pattern, bool recursive) { DIR pdir; FILINFO fno; @@ -1171,7 +1096,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const ch entry->size = fno.fsize; } entry->marked = 0; - if (contents->n_entries >= MAX_ENTRIES) { + if (contents->n_entries >= MAX_DIR_ENTRIES) { ret = true; // Too many entries, still okay break; } @@ -1201,7 +1126,7 @@ void SearchDirContents(DirStruct* contents, const char* path, const char* patter contents->entry->size = 0; contents->n_entries = 1; if (DriveType(path) & DRV_VIRTUAL) { - if (!GetVirtualDirContentsWorker(contents, path, pattern)) + if (!GetVirtualDirContents(contents, path, pattern)) contents->n_entries = 0; } else { char fpath[256]; // 256 is the maximum length of a full path diff --git a/source/fs.h b/source/fs.h index 8c1dc29..9814225 100644 --- a/source/fs.h +++ b/source/fs.h @@ -1,15 +1,7 @@ #pragma once #include "common.h" - -typedef enum { - T_ROOT, - T_DIR, - T_FILE, - T_DOTDOT -} EntryType; - -#define MAX_ENTRIES 1024 +#include "dir.h" // primary drive types #define DRV_UNKNOWN (0<<0) @@ -43,19 +35,6 @@ typedef enum { #define SKIP_ALL (1<<1) #define OVERWRITE_ALL (1<<2) -typedef struct { - char* name; // should point to the correct portion of the path - char path[256]; - u64 size; - EntryType type; - u8 marked; -} DirEntry; - -typedef struct { - u32 n_entries; - DirEntry entry[MAX_ENTRIES]; -} DirStruct; - bool InitSDCardFS(); bool InitExtFS(); void DeinitExtFS(); @@ -133,8 +112,5 @@ uint64_t GetPartitionOffsetSector(const char* path); /** Function to identify the type of a drive **/ int DriveType(const char* path); -/** Check for soecial search drive **/ +/** Check for special search drive **/ bool IsSearchDrive(const char* path); - -/** Helper function for copying DirEntry structs */ -void DirEntryCpy(DirEntry* dest, const DirEntry* orig); diff --git a/source/virtual/virtual.c b/source/virtual/virtual.c index 418504c..bf7b6d2 100644 --- a/source/virtual/virtual.c +++ b/source/virtual/virtual.c @@ -26,8 +26,16 @@ bool CheckVirtualDrive(const char* path) { return virtual_src; // this is safe for SysNAND & memory } -bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size) -{ +bool ReadVirtualDir(VirtualFile* vfile, u32 virtual_src) { + if (virtual_src & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND)) { + return ReadVNandDir(vfile, virtual_src); + } else if (virtual_src & VRT_MEMORY) { + return ReadVMemDir(vfile); + } + return false; +} + +bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size) { // get / fix the name char* fname = strchr(path, '/'); if (!fname) return false; @@ -39,17 +47,38 @@ bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size) if (!virtual_src || (fname - path != 3)) return false; - // get virtual file struct from appropriate function - if (virtual_src & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND)) { - if (!FindVNandFile(vfile, virtual_src, fname, size)) return false; - } else if (virtual_src & VRT_MEMORY) { - if (!FindVMemFile(vfile, fname, size)) return false; - } else return false; + // read virtual dir, match the path / size + ReadVirtualDir(NULL, virtual_src); // reset dir reader + while (ReadVirtualDir(vfile, virtual_src)) { + vfile->flags |= virtual_src; // add source flag + if (((strncasecmp(fname, vfile->name, 32) == 0) || + (size && (vfile->size == size)))) // search by size should be a last resort solution + return true; // file found + } - // add the virtual source to the virtual file flags - vfile->flags |= virtual_src; + // failed if arriving + return false; +} + +bool GetVirtualDirContents(DirStruct* contents, const char* path, const char* pattern) { + u32 virtual_src = GetVirtualSource(path); + if (!virtual_src) return false; // not a virtual path + if (strchr(path, '/')) return false; // only top level paths - return true; + VirtualFile vfile; + ReadVirtualDir(NULL, virtual_src); // reset dir reader + while ((contents->n_entries < MAX_DIR_ENTRIES) && (ReadVirtualDir(&vfile, virtual_src))) { + DirEntry* entry = &(contents->entry[contents->n_entries]); + if (pattern && !MatchName(pattern, vfile.name)) continue; + snprintf(entry->path, 256, "%s/%s", path, vfile.name); + entry->name = entry->path + strnlen(path, 256) + 1; + entry->size = vfile.size; + entry->type = T_FILE; + entry->marked = 0; + contents->n_entries++; + } + + return true; // not much we can check here } int ReadVirtualFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count, u32* bytes_read) diff --git a/source/virtual/virtual.h b/source/virtual/virtual.h index 043a323..2f91b27 100644 --- a/source/virtual/virtual.h +++ b/source/virtual/virtual.h @@ -1,6 +1,7 @@ #pragma once #include "common.h" +#include "dir.h" #include "nand.h" #define VRT_SYSNAND NAND_SYSNAND @@ -10,14 +11,6 @@ #define VFLAG_A9LH_AREA (1<<20) -static const char* virtualFileList[] = { // must have a match in virtualFileTemplates[] - "twln.bin", "twlp.bin", "agbsave.bin", "firm0.bin", "firm1.bin", "ctrnand_fat.bin", - "ctrnand_full.bin", "nand.bin", "nand_minsize.bin", "nand_hdr.bin", "twlmbr.bin", "sector0x96.bin", - "itcm.mem", "arm9.mem", "arm9ext.mem", "vram.mem", "dsp.mem", "axiwram.mem", - "fcram.mem", "fcramext.mem", "dtcm.mem", "bootrom_unp.mem" -}; -static const u32 virtualFileList_size = sizeof(virtualFileList) / sizeof(char*); - // virtual file flag (subject to change): // bits 0...9 : reserved for NAND virtual sources and info // bits 10...19: reserved for other virtual sources @@ -34,5 +27,6 @@ typedef struct { u32 GetVirtualSource(const char* path); bool CheckVirtualDrive(const char* path); bool FindVirtualFile(VirtualFile* vfile, const char* path, u32 size); +bool GetVirtualDirContents(DirStruct* contents, const char* path, const char* pattern); int ReadVirtualFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count, u32* bytes_read); int WriteVirtualFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count, u32* bytes_written); diff --git a/source/virtual/vmem.c b/source/virtual/vmem.c index ca38a38..f35281f 100644 --- a/source/virtual/vmem.c +++ b/source/virtual/vmem.c @@ -19,27 +19,30 @@ static const VirtualFile vMemFileTemplates[] = { { "bootrom_unp.mem" , 0xFFFF0000, 0x00008000, 0xFF, 0 } }; -bool FindVMemFile(VirtualFile* vfile, const char* name, u32 size) { - // parse the template list, get the correct one - u32 n_templates = sizeof(vMemFileTemplates) / sizeof(VirtualFile); - const VirtualFile* curr_template = NULL; - for (u32 i = 0; i < n_templates; i++) { - curr_template = &vMemFileTemplates[i]; - if (((strncasecmp(name, curr_template->name, 32) == 0) || - (size && (curr_template->size == size)))) // search by size should be a last resort solution - break; - curr_template = NULL; +bool ReadVMemDir(VirtualFile* vfile) { + static int num = -1; + int n_templates = sizeof(vMemFileTemplates) / sizeof(VirtualFile); + const VirtualFile* templates = vMemFileTemplates; + + if (!vfile) { // NULL pointer -> reset dir reader / internal number + num = -1; + return true; } - if (!curr_template) return false; - // copy current template to vfile - memcpy(vfile, curr_template, sizeof(VirtualFile)); + while (++num < n_templates) { + // copy current template to vfile + memcpy(vfile, templates + num, sizeof(VirtualFile)); + + // process special flag + if ((vfile->flags & VFLAG_N3DS_ONLY) && (GetUnitPlatform() != PLATFORM_N3DS)) + return false; // this is not on O3DS consoles + + // found if arriving here + return true; + } + if (num >= n_templates) return false; - // process special flag - if ((vfile->flags & VFLAG_N3DS_ONLY) && (GetUnitPlatform() != PLATFORM_N3DS)) - return false; // this is not on O3DS consoles - - return true; + return false; } int ReadVMemFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) { diff --git a/source/virtual/vmem.h b/source/virtual/vmem.h index 8e3f3c0..3c0cff3 100644 --- a/source/virtual/vmem.h +++ b/source/virtual/vmem.h @@ -3,6 +3,6 @@ #include "common.h" #include "virtual.h" -bool FindVMemFile(VirtualFile* vfile, const char* name, u32 size); +bool ReadVMemDir(VirtualFile* vfile); int ReadVMemFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count); int WriteVMemFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count); diff --git a/source/virtual/vnand.c b/source/virtual/vnand.c index d0f4aec..5acd0fc 100644 --- a/source/virtual/vnand.c +++ b/source/virtual/vnand.c @@ -34,41 +34,46 @@ bool CheckVNandDrive(u32 nand_src) { return GetNandSizeSectors(nand_src); } -bool FindVNandFile(VirtualFile* vfile, u32 nand_src, const char* name, u32 size) { - // get virtual type (O3DS/N3DS/NO3DS) - u32 virtual_type = CheckNandType(nand_src); - // workaround if CheckNandType() comes up with no result (empty EmuNAND) - if (!virtual_type) virtual_type = (GetUnitPlatform() == PLATFORM_3DS) ? NAND_TYPE_O3DS : NAND_TYPE_N3DS; +bool ReadVNandDir(VirtualFile* vfile, u32 nand_src) { + static int num = -1; + int n_templates = sizeof(vNandFileTemplates) / sizeof(VirtualFile); + const VirtualFile* templates = vNandFileTemplates; - // parse the template list, get the correct one - u32 n_templates = sizeof(vNandFileTemplates) / sizeof(VirtualFile); - const VirtualFile* curr_template = NULL; - for (u32 i = 0; i < n_templates; i++) { - curr_template = &vNandFileTemplates[i]; - if ((curr_template->flags & virtual_type) && ((strncasecmp(name, curr_template->name, 32) == 0) || - (size && (curr_template->size == size)))) // search by size should be a last resort solution - break; - curr_template = NULL; - } - if (!curr_template) return false; - - // copy current template to vfile - memcpy(vfile, curr_template, sizeof(VirtualFile)); - - // process special flags - if ((vfile->keyslot == 0x05) && !CheckSlot0x05Crypto()) - return false; // keyslot 0x05 not properly set up - if ((vfile->flags & VFLAG_NEEDS_OTP) && !CheckSector0x96Crypto()) - return false; // sector 0x96 crypto not set up - if (!(nand_src & VRT_SYSNAND) || (*(vu32*) 0x101401C0)) - vfile->flags &= ~VFLAG_A9LH_AREA; // flag is meaningless outside of A9LH / SysNAND - if (vfile->flags & VFLAG_NAND_SIZE) { - if ((nand_src != NAND_SYSNAND) && (GetNandSizeSectors(NAND_SYSNAND) != GetNandSizeSectors(nand_src))) - return false; // EmuNAND/ImgNAND is too small - vfile->size = GetNandSizeSectors(NAND_SYSNAND) * 0x200; + if (!vfile) { // NULL pointer -> reset dir reader / internal number + num = -1; + return true; } - return true; + while (++num < n_templates) { + // get NAND type (O3DS/N3DS/NO3DS), workaround for empty EmuNAND + u32 nand_type = CheckNandType(nand_src); + if (!nand_type) nand_type = (GetUnitPlatform() == PLATFORM_3DS) ? NAND_TYPE_O3DS : NAND_TYPE_N3DS; + + // copy current template to vfile + memcpy(vfile, templates + num, sizeof(VirtualFile)); + + // process / check special flags + if (!(vfile->flags & nand_type)) + continue; // virtual file has wrong NAND type + if ((vfile->keyslot == 0x05) && !CheckSlot0x05Crypto()) + continue; // keyslot 0x05 not properly set up + if ((vfile->flags & VFLAG_NEEDS_OTP) && !CheckSector0x96Crypto()) + return false; // sector 0x96 crypto not set up + if (!(nand_src & VRT_SYSNAND) || (*(vu32*) 0x101401C0)) + vfile->flags &= ~VFLAG_A9LH_AREA; // flag is meaningless outside of A9LH / SysNAND + if (vfile->flags & VFLAG_NAND_SIZE) { + if ((nand_src != NAND_SYSNAND) && (GetNandSizeSectors(NAND_SYSNAND) != GetNandSizeSectors(nand_src))) + continue; // EmuNAND/ImgNAND is too small + vfile->size = GetNandSizeSectors(NAND_SYSNAND) * 0x200; + } + + // found if arriving here + vfile->flags |= nand_src; + return true; + } + if (num >= n_templates) return false; + + return false; } int ReadVNandFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) { diff --git a/source/virtual/vnand.h b/source/virtual/vnand.h index c11ddc6..0c2b40a 100644 --- a/source/virtual/vnand.h +++ b/source/virtual/vnand.h @@ -4,6 +4,6 @@ #include "virtual.h" bool CheckVNandDrive(u32 nand_src); -bool FindVNandFile(VirtualFile* vfile, u32 nand_src, const char* name, u32 size); +bool ReadVNandDir(VirtualFile* vfile, u32 nand_src); int ReadVNandFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count); int WriteVNandFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count);