diff --git a/source/fatfs/alias.c b/source/fatfs/alias.c new file mode 100644 index 0000000..7c618d3 --- /dev/null +++ b/source/fatfs/alias.c @@ -0,0 +1,206 @@ +#include "alias.h" +#include "aes.h" +#include "sha.h" + +#define SDCRYPT_BUFFER ((u8*)0x21400000) +#define SDCRYPT_BUFFER_SIZE (0x100000) + +#define NUM_ALIAS_DRV 2 + +char alias_drv[NUM_ALIAS_DRV]; // 1 char ASCII drive number of the alias drive / 0x00 if unused +char alias_path[NUM_ALIAS_DRV][128]; // full path to resolve the alias into + +u8 sd_keyy[NUM_ALIAS_DRV][16]; // key Y belonging to alias drive + +int alias_num (const TCHAR* path) { + int num = -1; + for (u32 i = 0; i < NUM_ALIAS_DRV; i++) { + if (!alias_drv[i]) continue; + if ((path[0] == alias_drv[i]) && (path[1] == ':')) { + num = i; + break; + } + } + return num; +} + +void dealias_path (TCHAR* alias, const TCHAR* path) { + int num = alias_num(path); + if (num >= 0) // set alias (alias is assumed to be 256 byte) + snprintf(alias, 256, "%s%s", alias_path[num], path + 2); + else strncpy(alias, path, 256); +} + +void fx_crypt(XFIL* xfp, void* buff, FSIZE_t off, UINT bt) { + u32 mode = AES_CNT_CTRNAND_MODE; + u8 ctr[16] __attribute__((aligned(32))); + u8 buff16[16]; + u8* buffer = buff; + + // copy CTR and increment it + memcpy(ctr, xfp->iv, 16); + add_ctr(ctr, off / 16); + + // setup the key + setup_aeskeyY(xfp->keyslot, xfp->keyy); + use_aeskey(xfp->keyslot); + + // handle misaligned offset (at beginning) + if (off % 16) { + memcpy(buff16 + (off % 16), buff, 16 - (off % 16)); + ctr_decrypt(buff16, buff16, 1, mode, ctr); + bt -= 16 - (off % 16); + buffer += 16 - (off % 16); + } + + // de/encrypt the data + ctr_decrypt(buff, buff, bt / 16, mode, ctr); + bt -= 16 * (UINT) (bt / 16); + buffer += 16 * (UINT) (bt / 16); + + // handle misaligned offset (at end) + if (bt) { + memcpy(buff16, buff, bt); + ctr_decrypt(buff16, buff16, 1, mode, ctr); + buffer += bt; + bt = 0; + } +} + +FRESULT fx_open (FIL* fp, XFIL* xfp, const TCHAR* path, BYTE mode) { + int num = alias_num(path); + xfp->keyslot = 0x40; + if (num >= 0) { + // get AES counter, see: http://www.3dbrew.org/wiki/Extdata#Encryption + // path is the part of the full path after //Nintendo 3DS// + u8 hashstr[256]; + u8 sha256sum[32]; + u32 plen = 0; + // poor man's UTF-8 -> UTF-16 + for (u32 plen = 0; plen < 128; plen++) { + hashstr[2*plen] = path[2 + plen]; + hashstr[2*plen+1] = 0; + if (path[plen] == 0) break; + } + sha_quick(sha256sum, hashstr, (plen + 1) * 2, SHA256_MODE); + for (u32 i = 0; i < 16; i++) + xfp->iv[i] = sha256sum[i] ^ sha256sum[i+16]; + // copy over key, set keyslot + memcpy(xfp->keyy, sd_keyy[num], 16); + xfp->keyslot = 0x34; + } + + return fa_open(fp, path, mode); +} + +FRESULT fx_read (FIL* fp, XFIL* xfp, void* buff, UINT btr, UINT* br) { + FSIZE_t off = f_tell(fp); + FRESULT res = f_read(fp, buff, btr, br); + if (xfp && (xfp->keyslot < 0x40)) + fx_crypt(xfp, buff, off, btr); + return res; +} + +FRESULT fx_write (FIL* fp, XFIL* xfp, const void* buff, UINT btw, UINT* bw) { + FSIZE_t off = f_tell(fp); + FRESULT res = FR_OK; + if (xfp && (xfp->keyslot < 0x40)) { + *bw = 0; + for (UINT p = 0; (p < btw) && (res == FR_OK); p += SDCRYPT_BUFFER_SIZE) { + UINT pcount = min(SDCRYPT_BUFFER_SIZE, (btw - p)); + UINT bwl = 0; + memcpy(SDCRYPT_BUFFER, (u8*) buff + p, pcount); + fx_crypt(xfp, SDCRYPT_BUFFER, off + p, pcount); + res = f_write(fp, (const void*) SDCRYPT_BUFFER, pcount, &bwl); + *bw += bwl; + } + } else res = f_write(fp, buff, btw, bw); + return res; +} + +FRESULT fa_open (FIL* fp, const TCHAR* path, BYTE mode) { + TCHAR alias[256]; + dealias_path(alias, path); + return f_open(fp, alias, mode); +} + +FRESULT fa_opendir (DIR* dp, const TCHAR* path) { + TCHAR alias[256]; + dealias_path(alias, path); + return f_opendir(dp, alias); +} + +FRESULT fa_stat (const TCHAR* path, FILINFO* fno) { + TCHAR alias[256]; + dealias_path(alias, path); + return f_stat(alias, fno); +} + +// special functions for access of virtual NAND SD drives +bool SetupNandSdDrive(const char* path, const char* sd_path, const char* movable, int num) { + char alias[128]; + + // initial checks + if ((num >= NUM_ALIAS_DRV) || (num < 0)) return false; + alias_drv[num] = 0; + if (!sd_path || !movable || !path) return true; + + // grab the key Y from movable.sed + UINT bytes_read = 0; + FIL file; + if (f_open(&file, movable, FA_READ | FA_OPEN_EXISTING) != FR_OK) + return false; + f_lseek(&file, 0x110); + if ((f_read(&file, sd_keyy[num], 0x10, &bytes_read) != FR_OK) || (bytes_read != 0x10)) { + f_close(&file); + return false; + } + f_close(&file); + + // build the alias path (id0) + u32 sha256sum[8]; + sha_quick(sha256sum, sd_keyy[num], 0x10, SHA256_MODE); + snprintf(alias, 127, "%s/Nintendo 3DS/%08lX%08lX%08lX%08lX", + sd_path, sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]); + + // find the alias path (id1) + char* id1 = alias + strnlen(alias, 127); + DIR pdir; + FILINFO fno; + if (f_opendir(&pdir, alias) != FR_OK) + return false; + (id1++)[0] = '/'; + *id1 = '\0'; + while (f_readdir(&pdir, &fno) == FR_OK) { + if (fno.fname[0] == 0) + break; + if ((strnlen(fno.fname, 64) != 32) || !(fno.fattrib & AM_DIR)) + continue; // check for id1 directory + strncpy(id1, fno.fname, 127 - (id1 - alias)); + break; + } + f_closedir(&pdir); + if (!(*id1)) return false; + + // create the alias drive + return SetupAliasDrive(path, alias, num); +} + +bool SetupAliasDrive(const char* path, const char* alias, int num) { + // initial checks + if ((num >= NUM_ALIAS_DRV) || (num < 0)) return false; + alias_drv[num] = 0; + if (!alias || !path) return true; + + // take over drive path and alias + strncpy(alias_path[num], alias, 128); + if (path[1] != ':') return false; + alias_drv[num] = path[0]; + + return true; +} + +bool CheckAliasDrive(const char* path) { + int num = alias_num(path); + return (num >= 0); +} diff --git a/source/fatfs/alias.h b/source/fatfs/alias.h new file mode 100644 index 0000000..20c942c --- /dev/null +++ b/source/fatfs/alias.h @@ -0,0 +1,24 @@ +#pragma once + +#include "common.h" +#include "ff.h" + +typedef struct { + u8 iv[16]; + u8 keyy[16]; + u32 keyslot; +} __attribute__((packed)) XFIL; + +// wrapper functions for ff.h +// incomplete(!) extension to FatFS to support on-the-fly crypto & path aliases +FRESULT fx_open (FIL* fp, XFIL* xfp, const TCHAR* path, BYTE mode); +FRESULT fx_read (FIL* fp, XFIL* xfp, void* buff, UINT btr, UINT* br); +FRESULT fx_write (FIL* fp, XFIL* xfp, const void* buff, UINT btw, UINT* bw); +FRESULT fa_open (FIL* fp, const TCHAR* path, BYTE mode); +FRESULT fa_opendir (DIR* dp, const TCHAR* path); +FRESULT fa_stat (const TCHAR* path, FILINFO* fno); + +// special functions for access of virtual NAND SD drives +bool SetupNandSdDrive(const char* path, const char* sd_path, const char* movable, int num); +bool SetupAliasDrive(const char* path, const char* alias, int num); +bool CheckAliasDrive(const char* path); diff --git a/source/fs.c b/source/fs.c index 14ced16..8d050e1 100644 --- a/source/fs.c +++ b/source/fs.c @@ -1,6 +1,7 @@ #include "ui.h" #include "fs.h" #include "virtual.h" +#include "alias.h" #include "image.h" #include "sha.h" #include "sdmmc.h" @@ -10,7 +11,7 @@ #define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200 #define NORM_FS 10 -#define VIRT_FS 5 +#define VIRT_FS 7 #define SKIP_CUR (1<<3) #define OVERWRITE_CUR (1<<4) @@ -35,11 +36,6 @@ static char search_pattern[256] = { 0 }; static char search_path[256] = { 0 }; bool InitSDCardFS() { - #ifndef EXEC_GATEWAY - // TODO: Magic? - *(u32*)0x10000020 = 0; - *(u32*)0x10000020 = 0x340; - #endif fs_mounted[0] = (f_mount(fs, "0:", 1) == FR_OK); return fs_mounted[0]; } @@ -57,10 +53,14 @@ bool InitExtFS() { fs_mounted[7] = (f_mount(fs + 7, "7:", 1) == FR_OK); } } + SetupNandSdDrive("A:", "0:", "1:/private/movable.sed", 0); + SetupNandSdDrive("B:", "0:", "4:/private/movable.sed", 1); return true; } void DeinitExtFS() { + SetupNandSdDrive(NULL, NULL, NULL, 0); + SetupNandSdDrive(NULL, NULL, NULL, 1); for (u32 i = NORM_FS - 1; i > 0; i--) { if (fs_mounted[i]) { char fsname[8]; @@ -88,7 +88,10 @@ void SetFSSearch(const char* pattern, const char* path) { int PathToNumFS(const char* path) { int fsnum = *path - (int) '0'; if ((fsnum < 0) || (fsnum >= NORM_FS) || (path[1] != ':')) { - if (!GetVirtualSource(path) && !IsSearchDrive(path)) ShowPrompt(false, "Invalid path (%s)", path); + if (!GetVirtualSource(path) && + !CheckAliasDrive(path) && + !IsSearchDrive(path)) + ShowPrompt(false, "Invalid path (%s)", path); return -1; } return fsnum; @@ -281,7 +284,7 @@ bool GetTempFileName(char* path) { char* cc = tempname; // this does not try all permutations for (; (*cc <= 'Z') && (cc - tempname < 8); (*cc)++) { - if (f_stat(path, NULL) != FR_OK) break; + if (fa_stat(path, NULL) != FR_OK) break; if (*cc == 'Z') cc++; } return (cc - tempname < 8) ? true : false; @@ -289,11 +292,10 @@ bool GetTempFileName(char* path) { bool FileSetData(const char* path, const u8* data, size_t size, size_t foffset, bool create) { if (!CheckWritePermissions(path)) return false; - if (PathToNumFS(path) >= 0) { + if ((PathToNumFS(path) >= 0) || (CheckAliasDrive(path))) { UINT bytes_written = 0; FIL file; - if (!CheckWritePermissions(path)) return false; - if (f_open(&file, path, FA_WRITE | (create ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS)) != FR_OK) + if (fa_open(&file, path, FA_WRITE | (create ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS)) != FR_OK) return false; f_lseek(&file, foffset); f_write(&file, data, size, &bytes_written); @@ -310,10 +312,10 @@ bool FileSetData(const char* path, const u8* data, size_t size, size_t foffset, size_t FileGetData(const char* path, u8* data, size_t size, size_t foffset) { - if (PathToNumFS(path) >= 0) { + if ((PathToNumFS(path) >= 0) || (CheckAliasDrive(path))) { UINT bytes_read = 0; FIL file; - if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) + if (fa_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return 0; f_lseek(&file, foffset); if (f_read(&file, data, size, &bytes_read) != FR_OK) { @@ -333,9 +335,9 @@ size_t FileGetData(const char* path, u8* data, size_t size, size_t foffset) } size_t FileGetSize(const char* path) { - if (PathToNumFS(path) >= 0) { + if ((PathToNumFS(path) >= 0) || (CheckAliasDrive(path))) { FILINFO fno; - if (f_stat(path, &fno) != FR_OK) + if (fa_stat(path, &fno) != FR_OK) return 0; return fno.fsize; } else if (GetVirtualSource(path)) { @@ -372,14 +374,14 @@ bool FileGetSha256(const char* path, u8* sha256) { FIL file; size_t fsize; - if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) + if (fa_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return false; fsize = f_size(&file); f_lseek(&file, 0); f_sync(&file); for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { - UINT bytes_read = 0; + UINT bytes_read = 0; if (f_read(&file, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) ret = false; if (!ShowProgress(pos + bytes_read, fsize, path)) @@ -451,7 +453,7 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) { dsize = dvfile.size; } else { vdest = false; - if (f_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK) + if (fa_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK) return false; dsize = f_size(&dfile); f_lseek(&dfile, offset); @@ -468,7 +470,7 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) { osize = ovfile.size; } else { vorig = false; - if (f_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) { + if (fa_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) { if (!vdest) f_close(&dfile); return false; } @@ -563,7 +565,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) { FIL ofile; u32 osize; - if (f_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) + if (fa_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) return false; f_lseek(&ofile, 0); f_sync(&ofile); @@ -620,7 +622,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) { return false; // check if destination exists - if (flags && !(*flags & OVERWRITE_ALL) && f_stat(dest, NULL) == FR_OK) { + if (flags && !(*flags & OVERWRITE_ALL) && fa_stat(dest, NULL) == FR_OK) { if (*flags & SKIP_ALL) { *flags |= SKIP_CUR; return true; @@ -636,7 +638,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) { dname++; if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name")) return false; - } while (f_stat(dest, NULL) == FR_OK); + } while (fa_stat(dest, NULL) == FR_OK); } else if (user_select == 3) { *flags |= SKIP_CUR; return true; @@ -650,7 +652,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) { } } - if (f_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) + if (fa_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) return false; f_lseek(&dfile, 0); f_sync(&dfile); @@ -688,12 +690,12 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { FILINFO fno; bool ret = false; - if (f_stat(dest, &fno) != FR_OK) { // is root or destination does not exist + if (fa_stat(dest, &fno) != FR_OK) { // is root or destination does not exist DIR tmp_dir; // check if root - if (f_opendir(&tmp_dir, dest) != FR_OK) return false; + 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 (f_stat(orig, &fno) != FR_OK) return false; // origin does not exist + if (fa_stat(orig, &fno) != FR_OK) return false; // origin does not exist // build full destination path (on top of destination directory) char* oname = strrchr(orig, '/'); @@ -716,7 +718,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { } // check if destination exists - if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL)) && (f_stat(dest, NULL) == FR_OK)) { + if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL)) && (fa_stat(dest, NULL) == FR_OK)) { if (*flags & SKIP_ALL) { *flags |= SKIP_CUR; return true; @@ -731,7 +733,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { do { if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name")) return false; - } while (f_stat(dest, NULL) == FR_OK); + } while (fa_stat(dest, NULL) == FR_OK); } else if (user_select == 2) { *flags |= OVERWRITE_CUR; } else if (user_select == 3) { @@ -749,19 +751,19 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { // the copy process takes place here if (!ShowProgress(0, 0, orig)) return false; - if (move && f_stat(dest, NULL) != FR_OK) { // moving if dest not existing + if (move && fa_stat(dest, NULL) != FR_OK) { // moving if dest not existing ret = (f_rename(orig, dest) == FR_OK); } else if (fno.fattrib & AM_DIR) { // processing folders (same for move & copy) DIR pdir; char* fname = orig + strnlen(orig, 256); // create the destination folder if it does not already exist - if ((f_opendir(&pdir, dest) != FR_OK) && (f_mkdir(dest) != FR_OK)) { + if ((fa_opendir(&pdir, dest) != FR_OK) && (f_mkdir(dest) != FR_OK)) { ShowPrompt(false, "Error: Overwriting file with dir"); return false; } else f_closedir(&pdir); - if (f_opendir(&pdir, orig) != FR_OK) + if (fa_opendir(&pdir, orig) != FR_OK) return false; *(fname++) = '/'; @@ -778,7 +780,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { } f_closedir(&pdir); } else if (move) { // moving if destination exists - if (f_stat(dest, &fno) != FR_OK) + if (fa_stat(dest, &fno) != FR_OK) return false; if (fno.fattrib & AM_DIR) { ShowPrompt(false, "Error: Overwriting dir with file"); @@ -792,7 +794,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { FIL dfile; size_t fsize; - if (f_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) + if (fa_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) return false; fsize = f_size(&ofile); if (GetFreeSpace(dest) < fsize) { @@ -801,7 +803,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) { return false; } - if (f_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { + if (fa_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { ShowPrompt(false, "Error: Cannot create destination file"); f_close(&ofile); return false; @@ -860,8 +862,8 @@ bool PathMove(const char* destdir, const char* orig, u32* flags) { if (!CheckWritePermissions(destdir)) return false; if (!CheckWritePermissions(orig)) return false; if (flags) *flags = *flags & ~(SKIP_CUR|OVERWRITE_CUR); // reset local flags - if (GetVirtualSource(destdir) || GetVirtualSource(orig)) { - ShowPrompt(false, "Error: Moving virtual files not possible"); + if ((PathToNumFS(destdir) < 0) || (PathToNumFS(orig) < 0)) { + ShowPrompt(false, "Error: Moving is not possible here"); return false; } else { char fdpath[256]; // 256 is the maximum length of a full path @@ -879,12 +881,12 @@ bool PathDeleteWorker(char* fpath) { FILINFO fno; // this code handles directory content deletion - if (f_stat(fpath, &fno) != FR_OK) return false; // fpath does not exist + 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 (f_opendir(&pdir, fpath) != FR_OK) + if (fa_opendir(&pdir, fpath) != FR_OK) return false; *(fname++) = '/'; @@ -928,7 +930,7 @@ bool PathRename(const char* path, const char* newname) { strncpy(temp, path, oldname - path); if (!GetTempFileName(temp)) return false; if (f_rename(path, temp) == FR_OK) { - if ((f_stat(npath, NULL) == FR_OK) || (f_rename(temp, npath) != FR_OK)) { + if ((fa_stat(npath, NULL) == FR_OK) || (f_rename(temp, npath) != FR_OK)) { ShowPrompt(false, "Destination exists in folder"); f_rename(temp, path); // something went wrong - try renaming back return false; @@ -958,7 +960,7 @@ void CreateScreenshot() { for (; n < 1000; n++) { snprintf(filename, 16, "0:/snap%03i.bmp", (int) n); - if (f_stat(filename, NULL) != FR_OK) break; + if (fa_stat(filename, NULL) != FR_OK) break; } if (n >= 1000) return; @@ -1036,12 +1038,13 @@ bool GetRootDirContentsWorker(DirStruct* contents) { "SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP", "EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP", "IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", + "SYSNAND SD", "EMUNAND SD", "SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL", "MEMORY VIRTUAL", "LAST SEARCH" }; static const char* drvnum[] = { - "0:", "1:", "2:", "3:", "4:", "5:", "6:", "7:", "8:", "9:", "S:", "E:", "I:", "M:", "Z:" + "0:", "1:", "2:", "3:", "4:", "5:", "6:", "7:", "8:", "9:", "A:", "B:", "S:", "E:", "I:", "M:", "Z:" }; u32 n_entries = 0; @@ -1049,7 +1052,8 @@ bool GetRootDirContentsWorker(DirStruct* contents) { for (u32 pdrv = 0; (pdrv < NORM_FS+VIRT_FS) && (n_entries < MAX_ENTRIES); pdrv++) { DirEntry* entry = &(contents->entry[n_entries]); if ((pdrv < NORM_FS) && !fs_mounted[pdrv]) continue; - else if ((pdrv >= NORM_FS) && (!CheckVirtualDrive(drvnum[pdrv])) && !(IsSearchDrive(drvnum[pdrv]))) continue; + else if ((pdrv >= NORM_FS) && (!CheckAliasDrive(drvnum[pdrv])) && + (!CheckVirtualDrive(drvnum[pdrv])) && !(IsSearchDrive(drvnum[pdrv]))) continue; memset(entry->path, 0x00, 64); snprintf(entry->path + 0, 4, drvnum[pdrv]); snprintf(entry->path + 4, 32, "[%s] %s", drvnum[pdrv], drvname[pdrv]); @@ -1092,7 +1096,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const ch char* fname = fpath + strnlen(fpath, fnsize - 1); bool ret = false; - if (f_opendir(&pdir, fpath) != FR_OK) + if (fa_opendir(&pdir, fpath) != FR_OK) return false; (fname++)[0] = '/'; diff --git a/source/godmode.c b/source/godmode.c index de0a7ad..2c7e2cd 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -5,9 +5,10 @@ #include "platform.h" #include "nand.h" #include "virtual.h" +#include "alias.h" #include "image.h" -#define VERSION "0.6.8" +#define VERSION "0.6.9" #define N_PANES 2 #define IMG_DRV "789I" @@ -797,6 +798,8 @@ u32 GodMode() { } else if (!switched) { // standard unswitched command set if (GetVirtualSource(current_path) && (pad_state & BUTTON_X)) { ShowPrompt(false, "Not allowed in virtual path"); + } else if (CheckAliasDrive(current_path) && (pad_state & BUTTON_X)) { + ShowPrompt(false, "Not allowed in alias path"); } else if (pad_state & BUTTON_X) { // delete a file u32 n_marked = 0; for (u32 c = 0; c < current_dir->n_entries; c++) @@ -836,6 +839,8 @@ u32 GodMode() { } if (clipboard->n_entries) last_clipboard_size = clipboard->n_entries; + } else if (IsSearchDrive(current_path) && (pad_state & BUTTON_Y)) { + ShowPrompt(false, "Not allowed in search drive"); } else if (pad_state & BUTTON_Y) { // paste files const char* optionstr[2] = { "Copy path(s)", "Move path(s)" }; char promptstr[64]; @@ -846,7 +851,7 @@ u32 GodMode() { TruncateString(namestr, clipboard->entry[0].name, 20, 12); snprintf(promptstr, 64, "Paste \"%s\" here?", namestr); } else snprintf(promptstr, 64, "Paste %lu paths here?", clipboard->n_entries); - user_select = (!GetVirtualSource(clipboard->entry[0].path) && !GetVirtualSource(current_path)) ? + user_select = ((PathToNumFS(clipboard->entry[0].path) >= 0) && (PathToNumFS(current_path) >= 0)) ? ShowSelectPrompt(2, optionstr, promptstr) : (ShowPrompt(true, promptstr) ? 1 : 0); if (user_select) { for (u32 c = 0; c < clipboard->n_entries; c++) { @@ -872,6 +877,8 @@ u32 GodMode() { } else { // switched command set if (GetVirtualSource(current_path) && (pad_state & (BUTTON_X|BUTTON_Y))) { ShowPrompt(false, "Not allowed in virtual path"); + } else if (CheckAliasDrive(current_path) && (pad_state & (BUTTON_X|BUTTON_Y))) { + ShowPrompt(false, "Not allowed in alias path"); } else if ((pad_state & BUTTON_X) && (curr_entry->type != T_DOTDOT)) { // rename a file char newname[256]; char namestr[20+1];