Preliminary NAND SD drive support

... no crypto yet. Also some minor bugfixes
This commit is contained in:
d0k3 2016-10-28 21:30:10 +02:00
parent 7f65846499
commit 1fced99d65
4 changed files with 286 additions and 45 deletions

206
source/fatfs/alias.c Normal file
View File

@ -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/<ID0>/<ID1>
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);
}

24
source/fatfs/alias.h Normal file
View File

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

View File

@ -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] = '/';

View File

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