Improved virtual directory handling

This commit is contained in:
d0k3 2016-11-15 23:06:01 +01:00
parent 525b5b8810
commit 924dd8216e
10 changed files with 194 additions and 175 deletions

58
source/dir.c Normal file
View File

@ -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;
}

29
source/dir.h Normal file
View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}
return true;
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
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)

View File

@ -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);

View File

@ -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
// process special flag
if ((vfile->flags & VFLAG_N3DS_ONLY) && (GetUnitPlatform() != PLATFORM_N3DS))
return false; // this is not on O3DS consoles
return true;
// found if arriving here
return true;
}
if (num >= n_templates) return false;
return false;
}
int ReadVMemFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) {

View File

@ -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);

View File

@ -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) {

View File

@ -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);