mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Preliminary NAND SD drive support
... no crypto yet. Also some minor bugfixes
This commit is contained in:
parent
7f65846499
commit
1fced99d65
206
source/fatfs/alias.c
Normal file
206
source/fatfs/alias.c
Normal 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
24
source/fatfs/alias.h
Normal 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);
|
90
source/fs.c
90
source/fs.c
@ -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] = '/';
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user