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 "ui.h"
#include "fs.h" #include "fs.h"
#include "virtual.h" #include "virtual.h"
#include "alias.h"
#include "image.h" #include "image.h"
#include "sha.h" #include "sha.h"
#include "sdmmc.h" #include "sdmmc.h"
@ -10,7 +11,7 @@
#define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200 #define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200
#define NORM_FS 10 #define NORM_FS 10
#define VIRT_FS 5 #define VIRT_FS 7
#define SKIP_CUR (1<<3) #define SKIP_CUR (1<<3)
#define OVERWRITE_CUR (1<<4) #define OVERWRITE_CUR (1<<4)
@ -35,11 +36,6 @@ static char search_pattern[256] = { 0 };
static char search_path[256] = { 0 }; static char search_path[256] = { 0 };
bool InitSDCardFS() { bool InitSDCardFS() {
#ifndef EXEC_GATEWAY
// TODO: Magic?
*(u32*)0x10000020 = 0;
*(u32*)0x10000020 = 0x340;
#endif
fs_mounted[0] = (f_mount(fs, "0:", 1) == FR_OK); fs_mounted[0] = (f_mount(fs, "0:", 1) == FR_OK);
return fs_mounted[0]; return fs_mounted[0];
} }
@ -57,10 +53,14 @@ bool InitExtFS() {
fs_mounted[7] = (f_mount(fs + 7, "7:", 1) == FR_OK); 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; return true;
} }
void DeinitExtFS() { void DeinitExtFS() {
SetupNandSdDrive(NULL, NULL, NULL, 0);
SetupNandSdDrive(NULL, NULL, NULL, 1);
for (u32 i = NORM_FS - 1; i > 0; i--) { for (u32 i = NORM_FS - 1; i > 0; i--) {
if (fs_mounted[i]) { if (fs_mounted[i]) {
char fsname[8]; char fsname[8];
@ -88,7 +88,10 @@ void SetFSSearch(const char* pattern, const char* path) {
int PathToNumFS(const char* path) { int PathToNumFS(const char* path) {
int fsnum = *path - (int) '0'; int fsnum = *path - (int) '0';
if ((fsnum < 0) || (fsnum >= NORM_FS) || (path[1] != ':')) { 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 -1;
} }
return fsnum; return fsnum;
@ -281,7 +284,7 @@ bool GetTempFileName(char* path) {
char* cc = tempname; char* cc = tempname;
// this does not try all permutations // this does not try all permutations
for (; (*cc <= 'Z') && (cc - tempname < 8); (*cc)++) { 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++; if (*cc == 'Z') cc++;
} }
return (cc - tempname < 8) ? true : false; 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) { bool FileSetData(const char* path, const u8* data, size_t size, size_t foffset, bool create) {
if (!CheckWritePermissions(path)) return false; if (!CheckWritePermissions(path)) return false;
if (PathToNumFS(path) >= 0) { if ((PathToNumFS(path) >= 0) || (CheckAliasDrive(path))) {
UINT bytes_written = 0; UINT bytes_written = 0;
FIL file; FIL file;
if (!CheckWritePermissions(path)) return false; if (fa_open(&file, path, FA_WRITE | (create ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS)) != FR_OK)
if (f_open(&file, path, FA_WRITE | (create ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS)) != FR_OK)
return false; return false;
f_lseek(&file, foffset); f_lseek(&file, foffset);
f_write(&file, data, size, &bytes_written); 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) 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; UINT bytes_read = 0;
FIL file; 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; return 0;
f_lseek(&file, foffset); f_lseek(&file, foffset);
if (f_read(&file, data, size, &bytes_read) != FR_OK) { 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) { size_t FileGetSize(const char* path) {
if (PathToNumFS(path) >= 0) { if ((PathToNumFS(path) >= 0) || (CheckAliasDrive(path))) {
FILINFO fno; FILINFO fno;
if (f_stat(path, &fno) != FR_OK) if (fa_stat(path, &fno) != FR_OK)
return 0; return 0;
return fno.fsize; return fno.fsize;
} else if (GetVirtualSource(path)) { } else if (GetVirtualSource(path)) {
@ -372,14 +374,14 @@ bool FileGetSha256(const char* path, u8* sha256) {
FIL file; FIL file;
size_t fsize; 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; return false;
fsize = f_size(&file); fsize = f_size(&file);
f_lseek(&file, 0); f_lseek(&file, 0);
f_sync(&file); f_sync(&file);
for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { 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) if (f_read(&file, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK)
ret = false; ret = false;
if (!ShowProgress(pos + bytes_read, fsize, path)) if (!ShowProgress(pos + bytes_read, fsize, path))
@ -451,7 +453,7 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) {
dsize = dvfile.size; dsize = dvfile.size;
} else { } else {
vdest = false; 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; return false;
dsize = f_size(&dfile); dsize = f_size(&dfile);
f_lseek(&dfile, offset); f_lseek(&dfile, offset);
@ -468,7 +470,7 @@ bool FileInjectFile(const char* dest, const char* orig, u32 offset) {
osize = ovfile.size; osize = ovfile.size;
} else { } else {
vorig = false; 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); if (!vdest) f_close(&dfile);
return false; return false;
} }
@ -563,7 +565,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) {
FIL ofile; FIL ofile;
u32 osize; 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; return false;
f_lseek(&ofile, 0); f_lseek(&ofile, 0);
f_sync(&ofile); f_sync(&ofile);
@ -620,7 +622,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) {
return false; return false;
// check if destination exists // 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) { if (*flags & SKIP_ALL) {
*flags |= SKIP_CUR; *flags |= SKIP_CUR;
return true; return true;
@ -636,7 +638,7 @@ bool PathCopyVirtual(const char* destdir, const char* orig, u32* flags) {
dname++; dname++;
if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name")) if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name"))
return false; return false;
} while (f_stat(dest, NULL) == FR_OK); } while (fa_stat(dest, NULL) == FR_OK);
} else if (user_select == 3) { } else if (user_select == 3) {
*flags |= SKIP_CUR; *flags |= SKIP_CUR;
return true; 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; return false;
f_lseek(&dfile, 0); f_lseek(&dfile, 0);
f_sync(&dfile); f_sync(&dfile);
@ -688,12 +690,12 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
FILINFO fno; FILINFO fno;
bool ret = false; 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 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); f_closedir(&tmp_dir);
} else if (!(fno.fattrib & AM_DIR)) return false; // destination is not a directory (must be at this point) } 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) // build full destination path (on top of destination directory)
char* oname = strrchr(orig, '/'); char* oname = strrchr(orig, '/');
@ -716,7 +718,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
} }
// check if destination exists // 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) { if (*flags & SKIP_ALL) {
*flags |= SKIP_CUR; *flags |= SKIP_CUR;
return true; return true;
@ -731,7 +733,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
do { do {
if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name")) if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name"))
return false; return false;
} while (f_stat(dest, NULL) == FR_OK); } while (fa_stat(dest, NULL) == FR_OK);
} else if (user_select == 2) { } else if (user_select == 2) {
*flags |= OVERWRITE_CUR; *flags |= OVERWRITE_CUR;
} else if (user_select == 3) { } 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 // the copy process takes place here
if (!ShowProgress(0, 0, orig)) return false; 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); ret = (f_rename(orig, dest) == FR_OK);
} else if (fno.fattrib & AM_DIR) { // processing folders (same for move & copy) } else if (fno.fattrib & AM_DIR) { // processing folders (same for move & copy)
DIR pdir; DIR pdir;
char* fname = orig + strnlen(orig, 256); char* fname = orig + strnlen(orig, 256);
// create the destination folder if it does not already exist // 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"); ShowPrompt(false, "Error: Overwriting file with dir");
return false; return false;
} else f_closedir(&pdir); } else f_closedir(&pdir);
if (f_opendir(&pdir, orig) != FR_OK) if (fa_opendir(&pdir, orig) != FR_OK)
return false; return false;
*(fname++) = '/'; *(fname++) = '/';
@ -778,7 +780,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
} }
f_closedir(&pdir); f_closedir(&pdir);
} else if (move) { // moving if destination exists } else if (move) { // moving if destination exists
if (f_stat(dest, &fno) != FR_OK) if (fa_stat(dest, &fno) != FR_OK)
return false; return false;
if (fno.fattrib & AM_DIR) { if (fno.fattrib & AM_DIR) {
ShowPrompt(false, "Error: Overwriting dir with file"); ShowPrompt(false, "Error: Overwriting dir with file");
@ -792,7 +794,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
FIL dfile; FIL dfile;
size_t fsize; 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; return false;
fsize = f_size(&ofile); fsize = f_size(&ofile);
if (GetFreeSpace(dest) < fsize) { if (GetFreeSpace(dest) < fsize) {
@ -801,7 +803,7 @@ bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
return false; 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"); ShowPrompt(false, "Error: Cannot create destination file");
f_close(&ofile); f_close(&ofile);
return false; return false;
@ -860,8 +862,8 @@ bool PathMove(const char* destdir, const char* orig, u32* flags) {
if (!CheckWritePermissions(destdir)) return false; if (!CheckWritePermissions(destdir)) return false;
if (!CheckWritePermissions(orig)) return false; if (!CheckWritePermissions(orig)) return false;
if (flags) *flags = *flags & ~(SKIP_CUR|OVERWRITE_CUR); // reset local flags if (flags) *flags = *flags & ~(SKIP_CUR|OVERWRITE_CUR); // reset local flags
if (GetVirtualSource(destdir) || GetVirtualSource(orig)) { if ((PathToNumFS(destdir) < 0) || (PathToNumFS(orig) < 0)) {
ShowPrompt(false, "Error: Moving virtual files not possible"); ShowPrompt(false, "Error: Moving is not possible here");
return false; return false;
} else { } else {
char fdpath[256]; // 256 is the maximum length of a full path char fdpath[256]; // 256 is the maximum length of a full path
@ -879,12 +881,12 @@ bool PathDeleteWorker(char* fpath) {
FILINFO fno; FILINFO fno;
// this code handles directory content deletion // 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 if (fno.fattrib & AM_DIR) { // process folder contents
DIR pdir; DIR pdir;
char* fname = fpath + strnlen(fpath, 255); char* fname = fpath + strnlen(fpath, 255);
if (f_opendir(&pdir, fpath) != FR_OK) if (fa_opendir(&pdir, fpath) != FR_OK)
return false; return false;
*(fname++) = '/'; *(fname++) = '/';
@ -928,7 +930,7 @@ bool PathRename(const char* path, const char* newname) {
strncpy(temp, path, oldname - path); strncpy(temp, path, oldname - path);
if (!GetTempFileName(temp)) return false; if (!GetTempFileName(temp)) return false;
if (f_rename(path, temp) == FR_OK) { 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"); ShowPrompt(false, "Destination exists in folder");
f_rename(temp, path); // something went wrong - try renaming back f_rename(temp, path); // something went wrong - try renaming back
return false; return false;
@ -958,7 +960,7 @@ void CreateScreenshot() {
for (; n < 1000; n++) { for (; n < 1000; n++) {
snprintf(filename, 16, "0:/snap%03i.bmp", (int) 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; if (n >= 1000) return;
@ -1036,12 +1038,13 @@ bool GetRootDirContentsWorker(DirStruct* contents) {
"SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP", "SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP",
"EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP", "EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP",
"IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", "IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP",
"SYSNAND SD", "EMUNAND SD",
"SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL", "SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL",
"MEMORY VIRTUAL", "MEMORY VIRTUAL",
"LAST SEARCH" "LAST SEARCH"
}; };
static const char* drvnum[] = { 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; 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++) { for (u32 pdrv = 0; (pdrv < NORM_FS+VIRT_FS) && (n_entries < MAX_ENTRIES); pdrv++) {
DirEntry* entry = &(contents->entry[n_entries]); DirEntry* entry = &(contents->entry[n_entries]);
if ((pdrv < NORM_FS) && !fs_mounted[pdrv]) continue; 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); memset(entry->path, 0x00, 64);
snprintf(entry->path + 0, 4, drvnum[pdrv]); snprintf(entry->path + 0, 4, drvnum[pdrv]);
snprintf(entry->path + 4, 32, "[%s] %s", drvnum[pdrv], drvname[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); char* fname = fpath + strnlen(fpath, fnsize - 1);
bool ret = false; bool ret = false;
if (f_opendir(&pdir, fpath) != FR_OK) if (fa_opendir(&pdir, fpath) != FR_OK)
return false; return false;
(fname++)[0] = '/'; (fname++)[0] = '/';

View File

@ -5,9 +5,10 @@
#include "platform.h" #include "platform.h"
#include "nand.h" #include "nand.h"
#include "virtual.h" #include "virtual.h"
#include "alias.h"
#include "image.h" #include "image.h"
#define VERSION "0.6.8" #define VERSION "0.6.9"
#define N_PANES 2 #define N_PANES 2
#define IMG_DRV "789I" #define IMG_DRV "789I"
@ -797,6 +798,8 @@ u32 GodMode() {
} else if (!switched) { // standard unswitched command set } else if (!switched) { // standard unswitched command set
if (GetVirtualSource(current_path) && (pad_state & BUTTON_X)) { if (GetVirtualSource(current_path) && (pad_state & BUTTON_X)) {
ShowPrompt(false, "Not allowed in virtual path"); 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 } else if (pad_state & BUTTON_X) { // delete a file
u32 n_marked = 0; u32 n_marked = 0;
for (u32 c = 0; c < current_dir->n_entries; c++) for (u32 c = 0; c < current_dir->n_entries; c++)
@ -836,6 +839,8 @@ u32 GodMode() {
} }
if (clipboard->n_entries) if (clipboard->n_entries)
last_clipboard_size = 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 } else if (pad_state & BUTTON_Y) { // paste files
const char* optionstr[2] = { "Copy path(s)", "Move path(s)" }; const char* optionstr[2] = { "Copy path(s)", "Move path(s)" };
char promptstr[64]; char promptstr[64];
@ -846,7 +851,7 @@ u32 GodMode() {
TruncateString(namestr, clipboard->entry[0].name, 20, 12); TruncateString(namestr, clipboard->entry[0].name, 20, 12);
snprintf(promptstr, 64, "Paste \"%s\" here?", namestr); snprintf(promptstr, 64, "Paste \"%s\" here?", namestr);
} else snprintf(promptstr, 64, "Paste %lu paths here?", clipboard->n_entries); } 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); ShowSelectPrompt(2, optionstr, promptstr) : (ShowPrompt(true, promptstr) ? 1 : 0);
if (user_select) { if (user_select) {
for (u32 c = 0; c < clipboard->n_entries; c++) { for (u32 c = 0; c < clipboard->n_entries; c++) {
@ -872,6 +877,8 @@ u32 GodMode() {
} else { // switched command set } else { // switched command set
if (GetVirtualSource(current_path) && (pad_state & (BUTTON_X|BUTTON_Y))) { if (GetVirtualSource(current_path) && (pad_state & (BUTTON_X|BUTTON_Y))) {
ShowPrompt(false, "Not allowed in virtual path"); 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 } else if ((pad_state & BUTTON_X) && (curr_entry->type != T_DOTDOT)) { // rename a file
char newname[256]; char newname[256];
char namestr[20+1]; char namestr[20+1];