894 lines
31 KiB
C
Raw Normal View History

2016-03-28 17:32:29 +02:00
#include "ui.h"
#include "fs.h"
2016-03-21 18:29:55 +01:00
#include "virtual.h"
2016-04-05 20:34:50 +02:00
#include "image.h"
#include "sha.h"
2016-04-05 20:34:50 +02:00
#include "ff.h"
2016-03-21 19:24:06 +01:00
#define MAIN_BUFFER ((u8*)0x21200000)
#define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200
2016-03-11 01:29:14 +01:00
2016-04-04 22:45:49 +02:00
#define NORM_FS 10
2016-04-09 21:50:50 +02:00
#define VIRT_FS 4
2016-03-11 01:29:14 +01:00
// don't use this area for anything else!
static FATFS* fs = (FATFS*)0x20316000;
2016-02-29 22:51:20 +01:00
// write permission level - careful with this
static u32 write_permission_level = 1;
// number of currently open file systems
2016-04-04 22:45:49 +02:00
static bool fs_mounted[NORM_FS] = { false };
2016-03-11 01:29:14 +01:00
bool InitSDCardFS() {
#ifndef EXEC_GATEWAY
// TODO: Magic?
*(u32*)0x10000020 = 0;
*(u32*)0x10000020 = 0x340;
#endif
2016-03-11 01:29:14 +01:00
fs_mounted[0] = (f_mount(fs, "0:", 1) == FR_OK);
return fs_mounted[0];
}
2016-04-05 15:20:48 +02:00
bool InitExtFS() {
if (!fs_mounted[0])
return false;
2016-04-04 22:45:49 +02:00
for (u32 i = 1; i < NORM_FS; i++) {
char fsname[8];
2016-03-11 01:29:14 +01:00
snprintf(fsname, 7, "%lu:", i);
2016-05-01 15:41:11 +02:00
fs_mounted[i] = (f_mount(fs + i, fsname, 1) == FR_OK);
if ((i == 7) && !fs_mounted[7] && (GetMountState() == IMG_RAMDRV)) {
f_mkfs("7:", 0, 0); // format ramdrive if required
2016-05-01 15:41:11 +02:00
f_mount(NULL, fsname, 1);
fs_mounted[7] = (f_mount(fs + 7, "7:", 1) == FR_OK);
}
}
return true;
}
2016-04-05 15:20:48 +02:00
void DeinitExtFS() {
for (u32 i = NORM_FS - 1; i > 0; i--) {
2016-03-11 01:29:14 +01:00
if (fs_mounted[i]) {
char fsname[8];
snprintf(fsname, 7, "%lu:", i);
f_mount(NULL, fsname, 1);
fs_mounted[i] = false;
}
}
}
void DeinitSDCardFS() {
if (fs_mounted[0]) {
f_mount(NULL, "0:", 1);
fs_mounted[0] = false;
}
}
2016-03-16 18:46:05 +01:00
int PathToNumFS(const char* path) {
int fsnum = *path - (int) '0';
2016-04-04 22:45:49 +02:00
if ((fsnum < 0) || (fsnum >= NORM_FS) || (path[1] != ':')) {
2016-04-09 21:56:42 +02:00
if (!GetVirtualSource(path)) ShowPrompt(false, "Invalid path (%s)", path);
2016-03-16 18:46:05 +01:00
return -1;
2016-02-29 22:51:20 +01:00
}
2016-03-16 18:46:05 +01:00
return fsnum;
}
2016-04-05 15:20:48 +02:00
bool IsMountedFS(const char* path) {
int fsnum = PathToNumFS(path);
return ((fsnum >= 0) && (fsnum < NORM_FS)) ? fs_mounted[fsnum] : false;
}
2016-03-16 18:46:05 +01:00
bool CheckWritePermissions(const char* path) {
int pdrv = PathToNumFS(path);
2016-03-21 18:29:55 +01:00
if (pdrv < 0) {
2016-04-09 21:56:42 +02:00
if (GetVirtualSource(path)) // this is a hack, but okay for now
pdrv = (GetVirtualSource(path) == VRT_MEMORY) ? 10 :
(GetVirtualSource(path) == VRT_SYSNAND) ? 1 :
(GetVirtualSource(path) == VRT_EMUNAND) ? 4 : 7;
2016-03-21 18:29:55 +01:00
else return false;
} else if ((pdrv == 7) && (GetMountState() == IMG_RAMDRV)) {
pdrv = 0; // ...and another hack
2016-03-21 18:29:55 +01:00
}
2016-03-16 18:46:05 +01:00
2016-02-29 22:51:20 +01:00
if ((pdrv >= 1) && (pdrv <= 3) && (write_permission_level < 3)) {
if (ShowPrompt(true, "Writing to the SysNAND is locked!\nUnlock it now?"))
return SetWritePermissions(3);
2016-02-29 22:51:20 +01:00
return false;
} else if ((pdrv >= 4) && (pdrv <= 6) && (write_permission_level < 2)) {
if (ShowPrompt(true, "Writing to the EmuNAND is locked!\nUnlock it now?"))
return SetWritePermissions(2);
2016-02-29 22:51:20 +01:00
return false;
2016-04-04 22:45:49 +02:00
} else if ((pdrv >= 7) && (pdrv <= 9) && (write_permission_level < 2)) {
2016-04-05 13:10:23 +02:00
if (ShowPrompt(true, "Writing to images is locked!\nUnlock it now?"))
2016-04-04 22:45:49 +02:00
return SetWritePermissions(2);
return false;
2016-02-29 22:51:20 +01:00
} else if ((pdrv == 0) && (write_permission_level < 1)) {
if (ShowPrompt(true, "Writing to the SD card is locked!\nUnlock it now?"))
return SetWritePermissions(1);
2016-02-29 22:51:20 +01:00
return false;
2016-04-09 21:50:50 +02:00
} else if (pdrv >= 10) {
ShowPrompt(false, "Writing to memory is not allowed!");
2016-04-09 21:50:50 +02:00
return false;
2016-02-29 22:51:20 +01:00
}
return true;
}
bool SetWritePermissions(u32 level) {
if (write_permission_level >= level) {
// no need to ask the user here
write_permission_level = level;
return true;
}
switch (level) {
case 1:
if (!ShowUnlockSequence(1, "You want to enable SD card\nwriting permissions."))
return false;
break;
case 2:
2016-04-05 13:10:23 +02:00
if (!ShowUnlockSequence(2, "You want to enable EmuNAND &\nimage writing permissions.\nKeep backups, just in case."))
return false;
break;
case 3:
2016-04-05 13:10:23 +02:00
if (!ShowUnlockSequence(3, "!This is your only warning!\n \nYou want to enable SysNAND\nwriting permissions.\nThis enables you to do some\nreally dangerous stuff!\nHaving a SysNAND backup and\nNANDmod is recommended."))
return false;
break;
default:
break;
}
2016-02-29 22:51:20 +01:00
write_permission_level = level;
return true;
}
u32 GetWritePermissions() {
return write_permission_level;
}
bool GetTempFileName(char* path) {
// this assumes path is initialized with enough room
char* tempname = strrchr(path, '/');
if (!tempname) return false;
tempname++;
strncpy(tempname, "AAAAAAAA.TMP", 255 - (tempname - 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 (*cc == 'Z') cc++;
}
return (cc - tempname < 8) ? true : false;
}
2016-05-21 14:50:01 +02:00
bool FileSetData(const char* path, const u8* data, size_t size, size_t foffset) {
2016-05-27 01:21:05 +02:00
if (!CheckWritePermissions(path)) return false;
2016-05-21 14:50:01 +02:00
if (PathToNumFS(path) >= 0) {
UINT bytes_written = 0;
FIL file;
if (!CheckWritePermissions(path)) return false;
if (f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS) != FR_OK)
return false;
f_lseek(&file, foffset);
f_write(&file, data, size, &bytes_written);
f_close(&file);
return (bytes_written == size);
} else if (GetVirtualSource(path)) {
VirtualFile vfile;
if (!FindVirtualFile(&vfile, path, 0))
return 0;
return (WriteVirtualFile(&vfile, data, foffset, size, NULL) == 0);
}
return false;
2016-02-26 19:43:30 +01:00
}
size_t FileGetData(const char* path, u8* data, size_t size, size_t foffset)
2016-03-11 01:29:14 +01:00
{
if (PathToNumFS(path) >= 0) {
UINT bytes_read = 0;
FIL file;
if (f_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)
return 0;
f_close(&file);
return bytes_read;
2016-04-09 21:56:42 +02:00
} else if (GetVirtualSource(path)) {
u32 bytes_read = 0;
VirtualFile vfile;
if (!FindVirtualFile(&vfile, path, 0))
return 0;
return (ReadVirtualFile(&vfile, data, foffset, size, &bytes_read) == 0) ? bytes_read : 0;
}
return 0;
2016-03-11 01:29:14 +01:00
}
2016-04-10 15:55:39 +02:00
size_t FileGetSize(const char* path) {
if (PathToNumFS(path) >= 0) {
FILINFO fno;
fno.lfname = NULL;
if (f_stat(path, &fno) != FR_OK)
return 0;
return fno.fsize;
} else if (GetVirtualSource(path)) {
VirtualFile vfile;
if (!FindVirtualFile(&vfile, path, 0))
return 0;
return vfile.size;
}
return 0;
}
bool FileGetSha256(const char* path, u8* sha256) {
bool ret = true;
sha_init(SHA256_MODE);
ShowProgress(0, 0, path);
if (GetVirtualSource(path)) { // for virtual files
VirtualFile vfile;
u32 fsize;
if (!FindVirtualFile(&vfile, path, 0))
return false;
fsize = vfile.size;
for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, fsize - pos);
if (ReadVirtualFile(&vfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)
ret = false;
if (!ShowProgress(pos + read_bytes, fsize, path))
ret = false;
sha_update(MAIN_BUFFER, read_bytes);
}
} else { // for regular FAT files
FIL file;
size_t fsize;
if (f_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;
if (f_read(&file, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK)
ret = false;
if (!ShowProgress(pos + bytes_read, fsize, path))
ret = false;
sha_update(MAIN_BUFFER, bytes_read);
}
f_close(&file);
}
ShowProgress(1, 1, path);
sha_get(sha256);
return ret;
}
2016-03-21 23:19:23 +01:00
bool PathCopyVirtual(const char* destdir, const char* orig) {
char dest[256]; // maximum path name length in FAT
char* oname = strrchr(orig, '/');
char deststr[36 + 1];
char origstr[36 + 1];
bool ret = true;
if (oname == NULL) return false; // not a proper origin path
oname++;
2016-03-22 19:44:21 +01:00
snprintf(dest, 255, "%s/%s", destdir, oname);
2016-03-21 23:19:23 +01:00
TruncateString(deststr, dest, 36, 8);
TruncateString(origstr, orig, 36, 8);
2016-04-09 21:56:42 +02:00
if (GetVirtualSource(dest) && GetVirtualSource(orig)) { // virtual to virtual
2016-03-21 23:19:23 +01:00
VirtualFile dvfile;
VirtualFile ovfile;
u32 osize;
2016-03-22 19:44:21 +01:00
if (!FindVirtualFile(&dvfile, dest, 0))
2016-03-21 23:19:23 +01:00
return false;
2016-03-22 19:44:21 +01:00
if (!FindVirtualFile(&ovfile, orig, 0))
2016-03-21 23:19:23 +01:00
return false;
osize = ovfile.size;
if (dvfile.size != osize) { // almost impossible, but so what...
ShowPrompt(false, "Virtual file size mismatch:\n%s\n%s", origstr, deststr);
return false;
}
2016-05-26 21:08:35 +02:00
if (strncmp(dest, orig, 256) == 0) { // destination == origin
ShowPrompt(false, "Origin equals destination:\n%s\n%s", origstr, deststr);
return false;
}
2016-05-21 15:18:36 +02:00
if ((dvfile.flags & VFLAG_A9LH_AREA) && // check A9LH critical area
!ShowPrompt(true, "This is critical for A9LH:\n%s\nProceed writing to it?", deststr))
return false;
2016-03-21 23:19:23 +01:00
if ((dvfile.keyslot == ovfile.keyslot) && (dvfile.offset == ovfile.offset)) // this improves copy times
dvfile.keyslot = ovfile.keyslot = 0xFF;
2016-04-05 15:20:48 +02:00
DeinitExtFS();
2016-03-21 23:19:23 +01:00
if (!ShowProgress(0, 0, orig)) ret = false;
for (size_t pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos);
if (ReadVirtualFile(&ovfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)
2016-03-21 23:19:23 +01:00
ret = false;
if (!ShowProgress(pos + (read_bytes / 2), osize, orig))
ret = false;
if (WriteVirtualFile(&dvfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)
2016-03-21 23:19:23 +01:00
ret = false;
}
ShowProgress(1, 1, orig);
2016-04-05 15:20:48 +02:00
InitExtFS();
2016-04-09 21:56:42 +02:00
} else if (GetVirtualSource(dest)) { // SD card to virtual (other FAT not allowed!)
2016-03-21 23:19:23 +01:00
VirtualFile dvfile;
FIL ofile;
u32 osize;
if (f_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false;
f_lseek(&ofile, 0);
f_sync(&ofile);
osize = f_size(&ofile);
2016-03-22 19:44:21 +01:00
if (!FindVirtualFile(&dvfile, dest, 0)) {
if (!FindVirtualFile(&dvfile, dest, osize)) {
f_close(&ofile);
return false;
}
snprintf(dest, 255, "%s/%s", destdir, dvfile.name);
if (!ShowPrompt(true, "Entry not found: %s\nInject into %s instead?", deststr, dest)) {
f_close(&ofile);
return false;
}
TruncateString(deststr, dest, 36, 8);
}
2016-05-21 15:18:36 +02:00
if ((dvfile.flags & VFLAG_A9LH_AREA) && // check A9LH critical area
!ShowPrompt(true, "This is critical for A9LH:\n%s\nProceed writing to it?", deststr))
return false;
2016-03-21 23:19:23 +01:00
if (dvfile.size != osize) {
char osizestr[32];
char dsizestr[32];
FormatBytes(osizestr, osize);
FormatBytes(dsizestr, dvfile.size);
if (dvfile.size > osize) {
if (!ShowPrompt(true, "File smaller than available space:\n%s (%s)\n%s (%s)\nContinue?", origstr, osizestr, deststr, dsizestr)) {
f_close(&ofile);
return false;
}
} else {
ShowPrompt(false, "File bigger than available space:\n%s (%s)\n%s (%s)", origstr, osizestr, deststr, dsizestr);
f_close(&ofile);
return false;
}
2016-03-21 23:19:23 +01:00
}
2016-04-05 15:20:48 +02:00
DeinitExtFS();
2016-03-21 23:19:23 +01:00
if (!ShowProgress(0, 0, orig)) ret = false;
for (size_t pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT bytes_read = 0;
if (f_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK)
ret = false;
if (!ShowProgress(pos + (bytes_read / 2), osize, orig))
ret = false;
if (WriteVirtualFile(&dvfile, MAIN_BUFFER, pos, bytes_read, NULL) != 0)
2016-03-21 23:19:23 +01:00
ret = false;
}
ShowProgress(1, 1, orig);
f_close(&ofile);
2016-04-05 15:20:48 +02:00
InitExtFS();
2016-04-09 21:56:42 +02:00
} else if (GetVirtualSource(orig)) { // virtual to any file system
2016-03-21 23:19:23 +01:00
VirtualFile ovfile;
FIL dfile;
u32 osize;
2016-03-22 19:44:21 +01:00
if (!FindVirtualFile(&ovfile, orig, 0))
2016-03-21 23:19:23 +01:00
return false;
2016-05-26 21:08:35 +02:00
2016-03-21 23:19:23 +01:00
// check if destination exists
if (f_stat(dest, NULL) == FR_OK) {
2016-05-26 21:08:35 +02:00
const char* optionstr[3] = {"Choose new name", "Overwrite file", "Skip file"};
u32 user_select = ShowSelectPrompt(3, optionstr, "Destination already exists:\n%s", deststr);
if (user_select == 1) {
do {
char* dname = strrchr(dest, '/');
if (dname == NULL) return false;
dname++;
if (!ShowInputPrompt(dname, 255 - (dname - dest), "Choose new destination name"))
return false;
} while (f_stat(dest, NULL) == FR_OK);
} else if (user_select != 2) return (user_select == 3);
2016-03-21 23:19:23 +01:00
}
2016-05-26 21:08:35 +02:00
2016-03-21 23:19:23 +01:00
if (f_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)
return false;
f_lseek(&dfile, 0);
f_sync(&dfile);
osize = ovfile.size;
if (GetFreeSpace(dest) < osize) {
ShowPrompt(false, "Error: File is too big for destination");
f_close(&dfile);
return false;
}
if (!ShowProgress(0, 0, orig)) ret = false;
for (size_t pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos);
UINT bytes_written = 0;
if (ReadVirtualFile(&ovfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)
2016-03-21 23:19:23 +01:00
ret = false;
if (!ShowProgress(pos + (read_bytes / 2), osize, orig))
ret = false;
if (f_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK)
ret = false;
if (read_bytes != bytes_written)
ret = false;
}
ShowProgress(1, 1, orig);
f_close(&dfile);
} else {
return false;
}
return ret;
}
2016-04-29 02:21:36 +02:00
bool PathCopyWorker(char* dest, char* orig, bool overwrite, bool move) {
2016-03-02 17:22:44 +01:00
FILINFO fno = {.lfname = NULL};
bool ret = false;
2016-03-02 17:22:44 +01:00
2016-03-14 17:22:53 +01:00
if (f_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;
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
2016-03-02 17:22:44 +01:00
// build full destination path (on top of destination directory)
char* oname = strrchr(orig, '/');
2016-03-02 17:22:44 +01:00
char* dname = dest + strnlen(dest, 255);
if (oname == NULL) return false; // not a proper origin path
oname++;
*(dname++) = '/';
strncpy(dname, oname, 256 - (dname - dest));
2016-03-02 17:22:44 +01:00
// check if destination is part of or equal origin
2016-05-26 21:08:35 +02:00
while (strncmp(dest, orig, 255) == 0) {
2016-05-20 14:12:03 +02:00
if (!ShowInputPrompt(dname, 255 - (dname - dest), "Destination is equal to origin\nChoose another name?"))
return false;
2016-05-26 21:08:35 +02:00
}
if (strncmp(dest, orig, strnlen(orig, 255)) == 0) {
2016-03-14 17:22:53 +01:00
if ((dest[strnlen(orig, 255)] == '/') || (dest[strnlen(orig, 255)] == '\0')) {
ShowPrompt(false, "Error: Destination is part of origin");
2016-03-02 17:22:44 +01:00
return false;
2016-03-14 17:22:53 +01:00
}
2016-03-02 17:22:44 +01:00
}
// check if destination exists
2016-03-02 17:32:19 +01:00
if (!overwrite && (f_stat(dest, NULL) == FR_OK)) {
2016-05-26 21:08:35 +02:00
const char* optionstr[3] = {"Choose new name", "Overwrite file(s)", "Skip file(s)"};
2016-03-02 17:32:19 +01:00
char namestr[36 + 1];
2016-03-02 17:22:44 +01:00
TruncateString(namestr, dest, 36, 8);
2016-05-26 21:08:35 +02:00
u32 user_select = ShowSelectPrompt(3, optionstr, "Destination already exists:\n%s", namestr);
if (user_select == 1) {
do {
if (!ShowInputPrompt(dname, 255 - (dname - dest), "Choose new destination name"))
return false;
} while (f_stat(dest, NULL) == FR_OK);
} else if (user_select != 2) return (user_select == 3);
2016-03-02 17:32:19 +01:00
overwrite = true;
}
// the copy process takes place here
2016-03-02 17:22:44 +01:00
if (!ShowProgress(0, 0, orig)) return false;
2016-04-29 02:21:36 +02:00
if (move && f_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);
2016-03-02 17:22:44 +01:00
// create the destination folder if it does not already exist
if ((f_opendir(&pdir, dest) != FR_OK) && (f_mkdir(dest) != FR_OK)) {
2016-04-29 02:21:36 +02:00
ShowPrompt(false, "Error: Overwriting file with dir");
return false;
} else f_closedir(&pdir);
if (f_opendir(&pdir, orig) != FR_OK)
return false;
*(fname++) = '/';
fno.lfname = fname;
fno.lfsize = 256 - (fname - orig);
while (f_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries
if (fname[0] == 0)
strncpy(fname, fno.fname, 256 - (fname - orig));
if (fno.fname[0] == 0) {
ret = true;
break;
2016-04-29 02:21:36 +02:00
} else if (!PathCopyWorker(dest, orig, overwrite, move)) {
break;
}
}
f_closedir(&pdir);
2016-04-29 02:21:36 +02:00
} else if (move) { // moving if destination exists
if (f_stat(dest, &fno) != FR_OK)
return false;
if (fno.fattrib & AM_DIR) {
ShowPrompt(false, "Error: Overwriting dir with file");
return false;
}
if (f_unlink(dest) != FR_OK)
return false;
ret = (f_rename(orig, dest) == FR_OK);
} else { // copying files
FIL ofile;
FIL dfile;
size_t fsize;
if (f_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false;
fsize = f_size(&ofile);
if (GetFreeSpace(dest) < fsize) {
ShowPrompt(false, "Error: File is too big for destination");
f_close(&ofile);
return false;
}
if (f_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) {
2016-04-29 02:21:36 +02:00
ShowPrompt(false, "Error: Cannot create destination file");
f_close(&ofile);
return false;
}
f_lseek(&dfile, 0);
f_sync(&dfile);
f_lseek(&ofile, 0);
f_sync(&ofile);
ret = true;
2016-03-21 19:24:06 +01:00
for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT bytes_read = 0;
2016-03-02 17:22:44 +01:00
UINT bytes_written = 0;
if (f_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK)
ret = false;
if (!ShowProgress(pos + (bytes_read / 2), fsize, orig))
2016-03-02 17:22:44 +01:00
ret = false;
if (f_write(&dfile, MAIN_BUFFER, bytes_read, &bytes_written) != FR_OK)
ret = false;
if (bytes_read != bytes_written)
ret = false;
}
2016-03-02 17:22:44 +01:00
ShowProgress(1, 1, orig);
f_close(&ofile);
f_close(&dfile);
}
2016-03-02 17:22:44 +01:00
*(--dname) = '\0';
return ret;
}
bool PathCopy(const char* destdir, const char* orig) {
2016-02-29 22:51:20 +01:00
if (!CheckWritePermissions(destdir)) return false;
2016-04-09 21:56:42 +02:00
if (GetVirtualSource(destdir) || GetVirtualSource(orig)) {
2016-03-21 23:19:23 +01:00
// users are inventive...
2016-04-09 21:56:42 +02:00
if ((PathToNumFS(orig) > 0) && GetVirtualSource(destdir)) {
2016-03-21 23:19:23 +01:00
ShowPrompt(false, "Only files from SD card are accepted");
return false;
}
return PathCopyVirtual(destdir, orig);
} else {
char fdpath[256]; // 256 is the maximum length of a full path
char fopath[256];
strncpy(fdpath, destdir, 255);
strncpy(fopath, orig, 255);
2016-04-29 02:21:36 +02:00
return PathCopyWorker(fdpath, fopath, false, false);
}
}
bool PathMove(const char* destdir, const char* orig) {
if (!CheckWritePermissions(destdir)) return false;
if (!CheckWritePermissions(orig)) return false;
2016-04-29 02:21:36 +02:00
if (GetVirtualSource(destdir) || GetVirtualSource(orig)) {
ShowPrompt(false, "Error: Moving virtual files not possible");
return false;
} else {
char fdpath[256]; // 256 is the maximum length of a full path
char fopath[256];
strncpy(fdpath, destdir, 255);
strncpy(fopath, orig, 255);
bool same_drv = (PathToNumFS(orig) == PathToNumFS(destdir));
bool res = PathCopyWorker(fdpath, fopath, false, same_drv);
if (res) PathDelete(orig);
return res;
2016-03-21 23:19:23 +01:00
}
}
bool PathDeleteWorker(char* fpath) {
2016-03-02 17:22:44 +01:00
FILINFO fno = {.lfname = NULL};
// this code handles directory content deletion
if (f_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)
return false;
*(fname++) = '/';
fno.lfname = fname;
fno.lfsize = fpath + 255 - fname;
while (f_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries
if (fname[0] == 0)
strncpy(fname, fno.fname, fpath + 255 - fname);
if (fno.fname[0] == 0) {
break;
} else { // return value won't matter
PathDeleteWorker(fpath);
}
}
f_closedir(&pdir);
*(--fname) = '\0';
}
return (f_unlink(fpath) == FR_OK);
}
bool PathDelete(const char* path) {
char fpath[256]; // 256 is the maximum length of a full path
2016-02-29 22:51:20 +01:00
if (!CheckWritePermissions(path)) return false;
strncpy(fpath, path, 256);
return PathDeleteWorker(fpath);
}
2016-03-14 23:38:43 +01:00
bool PathRename(const char* path, const char* newname) {
char npath[256]; // 256 is the maximum length of a full path
char* oldname = strrchr(path, '/');
if (!CheckWritePermissions(path)) return false;
2016-03-14 23:38:43 +01:00
if (!oldname) return false;
oldname++;
strncpy(npath, path, oldname - path);
strncpy(npath + (oldname - path), newname, 255 - (oldname - path));
FRESULT res = f_rename(path, npath);
if (res == FR_EXIST) { // new path already exists (possible LFN/case issue)
char temp[256];
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)) {
ShowPrompt(false, "Destination exists in folder");
f_rename(temp, path); // something went wrong - try renaming back
return false;
} else return true;
} else return false;
} else return (res == FR_OK) ? true : false;
2016-03-14 23:38:43 +01:00
}
2016-03-14 23:58:25 +01:00
bool DirCreate(const char* cpath, const char* dirname) {
char npath[256]; // 256 is the maximum length of a full path
if (!CheckWritePermissions(cpath)) return false;
2016-03-14 23:58:25 +01:00
snprintf(npath, 255, "%s/%s", cpath, dirname);
return (f_mkdir(npath) == FR_OK);
}
void CreateScreenshot() {
2016-02-26 19:43:30 +01:00
const u8 bmp_header[54] = {
0x42, 0x4D, 0x36, 0xCA, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xCA, 0x08, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
2016-03-21 19:24:06 +01:00
u8* buffer = MAIN_BUFFER + 54;
2016-02-26 19:43:30 +01:00
u8* buffer_t = buffer + (400 * 240 * 3);
char filename[16];
static u32 n = 0;
for (; n < 1000; n++) {
snprintf(filename, 16, "0:/snap%03i.bmp", (int) n);
2016-02-27 20:10:41 +01:00
if (f_stat(filename, NULL) != FR_OK) break;
2016-02-26 19:43:30 +01:00
}
if (n >= 1000) return;
2016-03-21 19:24:06 +01:00
memcpy(MAIN_BUFFER, bmp_header, 54);
2016-02-26 19:43:30 +01:00
memset(buffer, 0x1F, 400 * 240 * 3 * 2);
for (u32 x = 0; x < 400; x++)
for (u32 y = 0; y < 240; y++)
memcpy(buffer_t + (y*400 + x) * 3, TOP_SCREEN + (x*240 + y) * 3, 3);
2016-02-26 19:43:30 +01:00
for (u32 x = 0; x < 320; x++)
for (u32 y = 0; y < 240; y++)
memcpy(buffer + (y*400 + x + 40) * 3, BOT_SCREEN + (x*240 + y) * 3, 3);
2016-05-19 21:24:49 +02:00
FileSetData(filename, MAIN_BUFFER, 54 + (400 * 240 * 3 * 2), 0);
2016-02-26 19:43:30 +01:00
}
2016-02-29 22:27:04 +01:00
void DirEntryCpy(DirEntry* dest, const DirEntry* orig) {
memcpy(dest, orig, sizeof(DirEntry));
dest->name = dest->path + (orig->name - orig->path);
}
void SortDirStruct(DirStruct* contents) {
for (u32 s = 0; s < contents->n_entries; s++) {
DirEntry* cmp0 = &(contents->entry[s]);
DirEntry* min0 = cmp0;
2016-03-21 16:58:35 +01:00
if (cmp0->type == T_DOTDOT) continue;
for (u32 c = s + 1; c < contents->n_entries; c++) {
DirEntry* cmp1 = &(contents->entry[c]);
if (min0->type != cmp1->type) {
if (min0->type > cmp1->type)
min0 = cmp1;
continue;
}
if (strncasecmp(min0->name, cmp1->name, 256) > 0)
min0 = cmp1;
}
if (min0 != cmp0) {
DirEntry swap; // swap entries and fix names
2016-02-29 22:27:04 +01:00
DirEntryCpy(&swap, cmp0);
DirEntryCpy(cmp0, min0);
DirEntryCpy(min0, &swap);
}
}
}
2016-02-26 19:43:30 +01:00
bool GetRootDirContentsWorker(DirStruct* contents) {
2016-03-21 18:29:55 +01:00
static const char* drvname[] = {
"SDCARD",
2016-02-27 19:58:41 +01:00
"SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP",
2016-03-21 18:29:55 +01:00
"EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP",
2016-04-04 22:45:49 +02:00
"IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP",
"SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL",
2016-04-09 21:50:50 +02:00
"MEMORY VIRTUAL"
2016-03-21 18:29:55 +01:00
};
static const char* drvnum[] = {
2016-04-09 21:50:50 +02:00
"0:", "1:", "2:", "3:", "4:", "5:", "6:", "7:", "8:", "9:", "S:", "E:", "I:", "M:"
};
2016-03-11 01:29:14 +01:00
u32 n_entries = 0;
2016-03-21 18:29:55 +01:00
// virtual root objects hacked in
2016-04-04 22:45:49 +02:00
for (u32 pdrv = 0; (pdrv < NORM_FS+VIRT_FS) && (n_entries < MAX_ENTRIES); pdrv++) {
2016-03-21 18:29:55 +01:00
DirEntry* entry = &(contents->entry[n_entries]);
2016-04-04 22:45:49 +02:00
if ((pdrv < NORM_FS) && !fs_mounted[pdrv]) continue;
2016-04-09 21:56:42 +02:00
else if ((pdrv >= NORM_FS) && (!CheckVirtualDrive(drvnum[pdrv]))) continue;
2016-03-21 18:29:55 +01:00
memset(entry->path, 0x00, 64);
snprintf(entry->path + 0, 4, drvnum[pdrv]);
snprintf(entry->path + 4, 32, "[%s] %s", drvnum[pdrv], drvname[pdrv]);
2016-04-05 20:34:50 +02:00
if ((GetMountState() == IMG_FAT) && (pdrv == 7)) // FAT image special handling
snprintf(entry->path + 4, 32, "[7:] FAT IMAGE");
2016-05-01 15:41:11 +02:00
else if ((GetMountState() == IMG_RAMDRV) && (pdrv == 7)) // RAM drive special handling
snprintf(entry->path + 4, 32, "[7:] RAMDRIVE");
2016-03-21 18:29:55 +01:00
entry->name = entry->path + 4;
entry->size = GetTotalSpace(entry->path);
entry->type = T_ROOT;
entry->marked = 0;
2016-03-11 01:29:14 +01:00
n_entries++;
}
2016-03-11 01:29:14 +01:00
contents->n_entries = n_entries;
return contents->n_entries;
}
2016-03-21 18:29:55 +01:00
bool GetVirtualDirContentsWorker(DirStruct* contents, const char* path) {
if (strchr(path, '/')) return false; // only top level paths
for (u32 n = 0; (n < virtualFileList_size) && (contents->n_entries < MAX_ENTRIES); n++) {
VirtualFile vfile;
DirEntry* entry = &(contents->entry[contents->n_entries]);
snprintf(entry->path, 256, "%s/%s", path, virtualFileList[n]);
2016-03-22 19:44:21 +01:00
if (!FindVirtualFile(&vfile, entry->path, 0)) continue;
2016-03-21 18:29:55 +01:00
entry->name = entry->path + strnlen(path, 256) + 1;
entry->size = vfile.size;
entry->type = T_FILE;
entry->marked = 0;
contents->n_entries++;
}
return true; // not much we can check here
}
2016-02-26 19:43:30 +01:00
bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fsize, bool recursive) {
DIR pdir;
FILINFO fno;
char* fname = fpath + strnlen(fpath, fsize - 1);
bool ret = false;
if (f_opendir(&pdir, fpath) != FR_OK)
return false;
(fname++)[0] = '/';
fno.lfname = fname;
fno.lfsize = fsize - (fname - fpath);
while (f_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries
if (fname[0] == 0)
strncpy(fname, fno.fname, (fsize - 1) - (fname - fpath));
if (fno.fname[0] == 0) {
ret = true;
break;
} else {
DirEntry* entry = &(contents->entry[contents->n_entries]);
2016-02-26 18:52:30 +01:00
strncpy(entry->path, fpath, 256);
entry->name = entry->path + (fname - fpath);
if (fno.fattrib & AM_DIR) {
2016-03-21 16:58:35 +01:00
entry->type = T_DIR;
entry->size = 0;
} else {
2016-03-21 16:58:35 +01:00
entry->type = T_FILE;
entry->size = fno.fsize;
}
2016-02-27 19:58:41 +01:00
entry->marked = 0;
contents->n_entries++;
if (contents->n_entries >= MAX_ENTRIES)
break;
}
if (recursive && (fno.fattrib & AM_DIR)) {
if (!GetDirContentsWorker(contents, fpath, fsize, recursive))
break;
}
}
f_closedir(&pdir);
return ret;
}
2016-02-29 16:14:39 +01:00
void GetDirContents(DirStruct* contents, const char* path) {
contents->n_entries = 0;
2016-03-02 19:36:20 +01:00
if (!(*path)) { // root directory
2016-02-29 16:14:39 +01:00
if (!GetRootDirContentsWorker(contents))
contents->n_entries = 0; // not required, but so what?
} else {
2016-03-02 19:36:20 +01:00
// create virtual '..' entry
contents->entry->name = contents->entry->path + 8;
strncpy(contents->entry->path, "*?*?*", 8);
strncpy(contents->entry->name, "..", 4);
2016-03-21 16:58:35 +01:00
contents->entry->type = T_DOTDOT;
2016-03-02 19:36:20 +01:00
contents->entry->size = 0;
contents->n_entries = 1;
2016-04-09 21:56:42 +02:00
if (GetVirtualSource(path)) {
2016-03-21 18:29:55 +01:00
if (!GetVirtualDirContentsWorker(contents, path))
contents->n_entries = 0;
} else {
char fpath[256]; // 256 is the maximum length of a full path
strncpy(fpath, path, 256);
if (!GetDirContentsWorker(contents, fpath, 256, false))
contents->n_entries = 0;
}
2016-02-29 16:14:39 +01:00
SortDirStruct(contents);
}
}
2016-02-27 19:58:41 +01:00
uint64_t GetFreeSpace(const char* path)
{
DWORD free_clusters;
2016-02-27 19:58:41 +01:00
FATFS *fs_ptr;
char fsname[4] = { '\0' };
2016-03-16 18:46:05 +01:00
int pdrv = PathToNumFS(path);
2016-03-21 18:29:55 +01:00
if (pdrv < 0) return 0;
2016-02-27 19:58:41 +01:00
2016-03-16 18:46:05 +01:00
snprintf(fsname, 3, "%i:", pdrv);
2016-02-27 19:58:41 +01:00
if (f_getfree(fsname, &free_clusters, &fs_ptr) != FR_OK)
2016-03-21 18:29:55 +01:00
return 0;
2016-03-16 18:46:05 +01:00
return (uint64_t) free_clusters * fs[pdrv].csize * _MAX_SS;
}
2016-02-27 19:58:41 +01:00
uint64_t GetTotalSpace(const char* path)
{
2016-03-16 18:46:05 +01:00
int pdrv = PathToNumFS(path);
2016-03-21 18:29:55 +01:00
if (pdrv < 0) return 0;
2016-02-27 19:58:41 +01:00
2016-03-16 18:46:05 +01:00
return (uint64_t) (fs[pdrv].n_fatent - 2) * fs[pdrv].csize * _MAX_SS;
}
uint64_t GetPartitionOffsetSector(const char* path)
{
int pdrv = PathToNumFS(path);
if (pdrv < 0) return -1;
2016-02-27 19:58:41 +01:00
return (uint64_t) fs[pdrv].volbase;
2016-02-27 19:58:41 +01:00
}