GodMode9/source/fs/fsutil.c

813 lines
29 KiB
C
Raw Normal View History

2016-12-10 15:32:03 +01:00
#include "fsutil.h"
#include "fsinit.h"
#include "fsdrive.h"
#include "fsperm.h"
2016-10-30 16:20:09 +01:00
#include "sddata.h"
2017-01-27 18:32:52 +01:00
#include "vff.h"
2016-12-10 15:32:03 +01:00
#include "virtual.h"
2016-12-13 17:10:39 +01:00
#include "image.h"
#include "sha.h"
2016-07-13 19:59:36 +02:00
#include "sdmmc.h"
2016-04-05 20:34:50 +02:00
#include "ff.h"
2016-12-10 15:32:03 +01:00
#include "ui.h"
2016-03-11 01:29:14 +01:00
2017-02-22 23:27:34 +01:00
#define SKIP_CUR (1UL<<4)
#define OVERWRITE_CUR (1UL<<5)
2016-07-13 19:59:36 +02:00
// Volume2Partition resolution table
PARTITION VolToPart[] = {
{0, 1}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}
};
uint64_t GetSDCardSize() {
if (sdmmc_sdcard_init() != 0) return 0;
return (u64) getMMCDevice(1)->total_size * 512;
}
bool FormatSDCard(u64 hidden_mb, u32 cluster_size, const char* label) {
2016-07-13 19:59:36 +02:00
u8 mbr[0x200] = { 0 };
u8 mbrdata[0x42] = {
0x80, 0x01, 0x01, 0x00, 0x0C, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x01, 0x01, 0x00, 0x1C, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};
u32 sd_size = getMMCDevice(1)->total_size;
u32 emu_sector = 1;
u32 emu_size = (u32) ((hidden_mb * 1024 * 1024) / 512);
u32 fat_sector = align(emu_sector + emu_size, 0x2000); // align to 4MB
u32 fat_size = (fat_sector < sd_size) ? sd_size - fat_sector : 0;
2016-07-13 19:59:36 +02:00
// FAT size check
if (fat_size < 0x80000) { // minimum free space: 256MB
2016-12-08 13:18:25 +01:00
ShowPrompt(false, "Error: SD card is too small");
2016-07-13 19:59:36 +02:00
return false;
}
sd_size = fat_size;
2016-07-13 19:59:36 +02:00
// build the MBR
memcpy(mbrdata + 0x08, &fat_sector, 4);
memcpy(mbrdata + 0x0C, &fat_size, 4);
memcpy(mbrdata + 0x18, &emu_sector, 4);
memcpy(mbrdata + 0x1C, &emu_size, 4);
2016-07-13 19:59:36 +02:00
memcpy(mbr + 0x1BE, mbrdata, 0x42);
if (hidden_mb) memcpy(mbr, "GATEWAYNAND", 12);
else memset(mbr + 0x1CE, 0, 0x10);
// one last warning....
2017-02-22 23:27:34 +01:00
// 0:/Nintendo 3DS/ write permission is ignored here, this warning is enough
if (!ShowUnlockSequence(5, "!WARNING!\n \nProceeding will format this SD.\nThis will irreversibly delete\nALL data on it."))
2016-07-13 19:59:36 +02:00
return false;
ShowString("Formatting SD, please wait...");
2016-07-13 19:59:36 +02:00
// write the MBR to disk
// !this assumes a fully deinitialized file system!
if ((sdmmc_sdcard_init() != 0) || (sdmmc_sdcard_writesectors(0, 1, mbr) != 0)) {
2016-12-08 13:18:25 +01:00
ShowPrompt(false, "Error: SD card i/o failure");
2016-07-13 19:59:36 +02:00
return false;
}
// format the SD card
2016-12-10 15:32:03 +01:00
InitSDCardFS();
UINT c_size = cluster_size;
bool ret = (f_mkfs("0:", FM_FAT32, c_size, MAIN_BUFFER, MAIN_BUFFER_SIZE) == FR_OK) && (f_setlabel((label) ? label : "0:GM9SD") == FR_OK);
2016-12-10 15:32:03 +01:00
DeinitSDCardFS();
2016-07-13 19:59:36 +02:00
return ret;
}
bool SetupBonusDrive(void) {
2017-02-22 18:44:39 +01:00
if (!ShowUnlockSequence(3, "Format the bonus drive?\nThis will irreversibly delete\nALL data on it."))
return false;
ShowString("Formatting drive, please wait...");
if (GetMountState() & IMG_NAND) InitImgFS(NULL);
bool ret = (f_mkfs("8:", FM_ANY, 0, MAIN_BUFFER, MAIN_BUFFER_SIZE) == FR_OK);
if (ret) {
f_setlabel("8:BONUS");
InitExtFS();
}
return ret;
}
2016-12-13 17:10:39 +01:00
bool FileUnlock(const char* path) {
FIL file;
2016-12-10 15:32:03 +01:00
if (!(DriveType(path) & DRV_FAT)) return true; // can't really check this
2016-12-13 17:10:39 +01:00
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
char pathstr[32 + 1];
TruncateString(pathstr, path, 32, 8);
if (GetMountState() && (strncmp(path, GetMountPath(), 256) == 0) &&
(ShowPrompt(true, "%s\nFile is currently mounted.\nUnmount to unlock?", pathstr))) {
InitImgFS(NULL);
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false;
} else return false;
}
fx_close(&file);
return true;
}
2016-06-17 17:12:11 +02:00
bool FileSetData(const char* path, const u8* data, size_t size, size_t foffset, bool create) {
2017-01-27 18:32:52 +01:00
UINT bw;
2016-05-27 01:21:05 +02:00
if (!CheckWritePermissions(path)) return false;
2017-01-27 18:32:52 +01:00
if ((DriveType(path) & DRV_FAT) && create) f_unlink(path);
return (fvx_qwrite(path, data, foffset, size, &bw) == FR_OK) && (bw == size);
2016-02-26 19:43:30 +01:00
}
size_t FileGetData(const char* path, u8* data, size_t size, size_t foffset) {
2017-01-27 18:32:52 +01:00
UINT br;
if (fvx_qread(path, data, foffset, size, &br) != FR_OK) br = 0;
return br;
2016-03-11 01:29:14 +01:00
}
2016-04-10 15:55:39 +02:00
size_t FileGetSize(const char* path) {
int drvtype = DriveType(path);
if (drvtype & DRV_FAT) {
2016-04-10 15:55:39 +02:00
FILINFO fno;
if (fa_stat(path, &fno) != FR_OK)
2016-04-10 15:55:39 +02:00
return 0;
return fno.fsize;
} else if (drvtype & DRV_VIRTUAL) {
2016-04-10 15:55:39 +02:00
VirtualFile vfile;
2016-11-15 23:34:21 +01:00
if (!GetVirtualFile(&vfile, path))
2016-04-10 15:55:39 +02:00
return 0;
return vfile.size;
}
return 0;
}
bool FileGetSha256(const char* path, u8* sha256) {
bool ret = true;
2017-01-27 18:32:52 +01:00
FIL file;
u64 fsize;
2017-01-27 18:32:52 +01:00
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false;
fsize = fvx_size(&file);
fvx_lseek(&file, 0);
ShowProgress(0, 0, path);
2017-01-27 18:32:52 +01:00
sha_init(SHA256_MODE);
for (u64 pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT bytes_read = 0;
if (fvx_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);
}
2017-01-27 18:32:52 +01:00
sha_get(sha256);
fvx_close(&file);
ShowProgress(1, 1, path);
return ret;
}
2016-12-11 15:40:44 +01:00
u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file) {
2017-01-27 18:32:52 +01:00
FIL file; // used for FAT & virtual
u64 found = (u64) -1;
u64 fsize = FileGetSize(path);
2017-01-27 18:32:52 +01:00
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return found;
2016-12-11 15:40:44 +01:00
// main routine
for (u32 pass = 0; pass < 2; pass++) {
bool show_progress = false;
u64 pos = (pass == 0) ? offset_file : 0;
u64 search_end = (pass == 0) ? fsize : offset_file + size_data;
search_end = (search_end > fsize) ? fsize : search_end;
for (; (pos < search_end) && (found == (u64) -1); pos += MAIN_BUFFER_SIZE - (size_data - 1)) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, search_end - pos);
2017-01-27 18:32:52 +01:00
UINT btr;
fvx_lseek(&file, pos);
if ((fvx_read(&file, MAIN_BUFFER, read_bytes, &btr) != FR_OK) || (btr != read_bytes))
break;
2016-12-11 15:40:44 +01:00
for (u32 i = 0; i + size_data <= read_bytes; i++) {
if (memcmp(MAIN_BUFFER + i, data, size_data) == 0) {
found = pos + i;
break;
}
}
if (!show_progress && (found == (u64) -1) && (pos + read_bytes < fsize)) {
ShowProgress(0, 0, path);
show_progress = true;
}
if (show_progress && (!ShowProgress(pos + read_bytes, fsize, path)))
break;
}
}
2017-01-27 18:32:52 +01:00
fvx_close(&file);
return found;
}
2016-06-17 19:28:43 +02:00
bool FileInjectFile(const char* dest, const char* orig, u32 offset) {
FIL ofile;
FIL dfile;
u64 osize;
u64 dsize;
2016-06-17 19:28:43 +02:00
bool ret;
if (!CheckWritePermissions(dest)) return false;
if (strncmp(dest, orig, 256) == 0) {
ShowPrompt(false, "Error: Can't inject file into itself");
return false;
}
2016-06-17 19:28:43 +02:00
2017-01-27 18:32:52 +01:00
// open destination / origin
if (fvx_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
return false;
if ((fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) &&
(!FileUnlock(orig) || (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK))) {
fvx_close(&dfile);
return false;
2016-06-17 19:28:43 +02:00
}
2017-01-27 18:32:52 +01:00
fvx_lseek(&dfile, offset);
fvx_lseek(&ofile, 0);
dsize = fvx_size(&dfile);
osize = f_size(&ofile);
2016-06-17 19:28:43 +02:00
// check file limits
if (offset + osize > dsize) {
ShowPrompt(false, "Operation would write beyond end of file");
2017-01-27 18:32:52 +01:00
fvx_close(&dfile);
fvx_close(&ofile);
2016-06-17 19:28:43 +02:00
return false;
}
ret = true;
2016-06-17 19:49:19 +02:00
ShowProgress(0, 0, orig);
for (u64 pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
2016-06-17 19:28:43 +02:00
UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos);
UINT bytes_read = read_bytes;
UINT bytes_written = read_bytes;
2017-01-27 18:32:52 +01:00
if (fvx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) {
2016-06-17 19:28:43 +02:00
ret = false;
2017-01-27 18:32:52 +01:00
break;
}
if ((!ShowProgress(pos + (bytes_read / 2), osize, orig)) ||
(fvx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ||
(bytes_read != bytes_written))
2016-06-17 19:28:43 +02:00
ret = false;
}
ShowProgress(1, 1, orig);
2017-01-27 18:32:52 +01:00
fvx_close(&dfile);
fvx_close(&ofile);
2016-06-17 19:28:43 +02:00
return ret;
}
2017-02-22 23:27:34 +01:00
bool PathCopyVrtToVrt(const char* destdir, const char* orig, u32* flags) {
2016-11-30 15:56:22 +01:00
VirtualFile dvfile;
VirtualFile ovfile;
bool ret = true;
2016-03-21 23:19:23 +01:00
char dest[256]; // maximum path name length in FAT
char* oname = strrchr(orig, '/');
2016-11-30 15:56:22 +01:00
if (oname == NULL) return false; // not a proper origin path
snprintf(dest, 255, "%s/%s", destdir, (++oname));
2016-03-21 23:19:23 +01:00
char deststr[36 + 1];
char origstr[36 + 1];
2016-11-30 15:56:22 +01:00
TruncateString(deststr, dest, 36, 8);
TruncateString(origstr, orig, 36, 8);
if (!GetVirtualFile(&dvfile, dest) || !GetVirtualFile(&ovfile, orig))
return false;
u64 osize = ovfile.size;
2016-11-30 15:56:22 +01:00
if (dvfile.size != osize) { // almost impossible, but so what...
ShowPrompt(false, "Virtual file size mismatch:\n%s\n%s", origstr, deststr);
return false;
} else if (strncmp(dest, orig, 256) == 0) { // destination == origin
ShowPrompt(false, "Origin equals destination:\n%s\n%s", origstr, deststr);
return false;
}
2017-02-15 16:34:25 +01:00
if ((dvfile.keyslot == ovfile.keyslot) && (dvfile.offset == ovfile.offset))
dvfile.keyslot = ovfile.keyslot = 0xFF; // this improves copy times for virtual NAND
// check write permissions
2017-02-22 23:27:34 +01:00
if ((!flags || !(*flags & OVERRIDE_PERM)) && !CheckWritePermissions(destdir))
return false;
2016-11-30 15:56:22 +01:00
// unmount critical NAND drives
DismountDriveType(DriveType(destdir)&(DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE));
if (!ShowProgress(0, 0, orig)) ret = false;
for (u64 pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
2016-11-30 15:56:22 +01:00
UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos);
if (ReadVirtualFile(&ovfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)
ret = false;
if (!ShowProgress(pos + (read_bytes / 2), osize, orig))
ret = false;
if (WriteVirtualFile(&dvfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)
ret = false;
}
ShowProgress(1, 1, orig);
InitExtFS();
return ret;
}
2017-02-22 23:27:34 +01:00
bool PathCopyFatToVrt(const char* destdir, const char* orig, u32* flags) {
2016-11-30 15:56:22 +01:00
VirtualFile dvfile;
FIL ofile;
2016-03-21 23:19:23 +01:00
bool ret = true;
2016-11-30 15:56:22 +01:00
char dest[256]; // maximum path name length in FAT
char* oname = strrchr(orig, '/');
2016-03-21 23:19:23 +01:00
if (oname == NULL) return false; // not a proper origin path
2016-11-30 15:56:22 +01:00
snprintf(dest, 255, "%s/%s", destdir, (++oname));
2016-03-21 23:19:23 +01:00
2016-11-30 15:56:22 +01:00
// FAT file size
FILINFO fno;
if (fa_stat(orig, &fno) != FR_OK) return false; // file does not exist
u64 osize = fno.fsize;
2016-11-30 15:56:22 +01:00
// virtual file
if (!GetVirtualFile(&dvfile, dest)) {
VirtualDir vdir;
if (!GetVirtualDir(&vdir, destdir)) return false;
while (true) { // search by size should be a last resort solution
if (!ReadVirtualDir(&dvfile, &vdir))
return false;
if (dvfile.size == osize)
break; // file found
2016-03-21 23:19:23 +01:00
}
2016-11-30 15:56:22 +01:00
if (!ShowPrompt(true, "Entry not found: %s\nInject into %s instead?", dest, dvfile.name))
2016-03-21 23:19:23 +01:00
return false;
2017-02-15 16:34:25 +01:00
snprintf(dest, 255, "%s/%s", destdir, dvfile.name);
2016-11-30 15:56:22 +01:00
} else if (dvfile.size != osize) { // handling for differing sizes
char deststr[36 + 1];
char origstr[36 + 1];
char osizestr[32];
char dsizestr[32];
TruncateString(deststr, dest, 36, 8);
TruncateString(origstr, orig, 36, 8);
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))
2016-03-22 19:44:21 +01:00
return false;
2016-11-30 15:56:22 +01:00
} else {
ShowPrompt(false, "File bigger than available space:\n%s (%s)\n%s (%s)", origstr, osizestr, deststr, dsizestr);
return false;
2016-03-21 23:19:23 +01:00
}
2016-11-30 15:56:22 +01:00
}
2017-02-15 16:34:25 +01:00
// check write permissions
2017-02-22 23:27:34 +01:00
if ((!flags || !(*flags & OVERRIDE_PERM)) && !CheckWritePermissions(destdir))
return false;
2017-02-15 16:34:25 +01:00
2016-11-30 15:56:22 +01:00
// FAT file
2016-12-13 17:10:39 +01:00
if ((fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) &&
(!FileUnlock(orig) || (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK))) {
2016-11-30 15:56:22 +01:00
return false;
2016-12-13 17:10:39 +01:00
}
2016-11-30 15:56:22 +01:00
f_lseek(&ofile, 0);
f_sync(&ofile);
// unmount critical NAND drives
DismountDriveType(DriveType(destdir)&(DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE));
if (!ShowProgress(0, 0, orig)) ret = false;
for (u64 pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
2016-11-30 15:56:22 +01:00
UINT bytes_read = 0;
if (fx_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)
ret = false;
}
ShowProgress(1, 1, orig);
fx_close(&ofile);
InitExtFS();
return ret;
}
bool PathCopyVrtToFat(char* dest, char* orig, u32* flags) {
VirtualFile vfile;
FILINFO fno;
bool ret = false;
2016-11-30 15:56:22 +01:00
if (fa_stat(dest, &fno) != FR_OK) { // is root or destination does not exist
DIR tmp_dir; // check if root
if (fa_opendir(&tmp_dir, dest) != FR_OK) return false;
f_closedir(&tmp_dir);
} else if (!(fno.fattrib & AM_DIR)) return false; // destination is not a directory (must be at this point)
// build full destination path (on top of destination directory)
2016-11-30 15:56:22 +01:00
char* oname = strrchr(orig, '/');
char* dname = dest + strnlen(dest, 255);
2016-11-30 15:56:22 +01:00
if (oname == NULL) return false; // not a proper origin path
oname++;
*(dname++) = '/';
strncpy(dname, oname, 256 - (dname - dest));
2016-11-30 15:56:22 +01:00
// open / check virtual file
if (!GetVirtualFile(&vfile, orig))
2016-11-30 15:56:22 +01:00
return false;
// check if destination exists
if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL)) && (fa_stat(dest, NULL) == FR_OK)) {
2016-11-30 15:56:22 +01:00
if (*flags & SKIP_ALL) {
*flags |= SKIP_CUR;
return true;
2016-03-21 23:19:23 +01:00
}
2016-11-30 15:56:22 +01:00
const char* optionstr[5] =
{"Choose new name", "Overwrite file(s)", "Skip file(s)", "Overwrite all", "Skip all"};
char namestr[36 + 1];
TruncateString(namestr, dest, 36, 8);
2016-11-30 15:56:22 +01:00
u32 user_select = ShowSelectPrompt((*flags & ASK_ALL) ? 5 : 3, optionstr,
"Destination already exists:\n%s", namestr);
2016-11-30 15:56:22 +01:00
if (user_select == 1) {
do {
if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name"))
return false;
} while (fa_stat(dest, NULL) == FR_OK);
} else if (user_select == 2) {
*flags |= OVERWRITE_CUR;
2016-11-30 15:56:22 +01:00
} else if (user_select == 3) {
*flags |= SKIP_CUR;
return true;
} else if (user_select == 4) {
*flags |= OVERWRITE_ALL;
} else if (user_select == 5) {
*flags |= (SKIP_CUR|SKIP_ALL);
return true;
} else {
2016-03-21 23:19:23 +01:00
return false;
}
2016-11-30 15:56:22 +01:00
}
// check destination write permission (special paths only)
if (((*dest == '1') || (strncmp(dest, "0:/Nintendo 3DS", 16) == 0)) &&
(!flags || (*flags & (OVERWRITE_CUR|OVERWRITE_ALL))) &&
2017-02-22 23:27:34 +01:00
(!flags || !(*flags & OVERRIDE_PERM)) &&
2017-02-15 16:34:25 +01:00
!CheckWritePermissions(dest)) return false;
// the copy process takes place here
if (!ShowProgress(0, 0, orig)) return false;
if (vfile.flags & VFLAG_DIR) { // processing folders
DIR pdir;
VirtualDir vdir;
char* fname = orig + strnlen(orig, 256);
// create the destination folder if it does not already exist
if (fa_opendir(&pdir, dest) != FR_OK) {
if (f_mkdir(dest) != FR_OK) {
ShowPrompt(false, "Error: Overwriting file with dir");
return false;
}
} else f_closedir(&pdir);
if (!OpenVirtualDir(&vdir, &vfile))
return false;
*(fname++) = '/';
while (true) {
if (!ReadVirtualDir(&vfile, &vdir)) {
ret = true;
break;
}
char name[256];
if (!GetVirtualFilename(name, &vfile, 256)) break;
strncpy(fname, name, 256 - (fname - orig));
if (!PathCopyVrtToFat(dest, orig, flags))
break;
}
} else { // copying files
FIL dfile;
u64 osize = vfile.size;
2016-03-21 23:19:23 +01:00
if (GetFreeSpace(dest) < osize) {
ShowPrompt(false, "Error: File is too big for destination");
return false;
}
if (fx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) {
ShowPrompt(false, "Error: Cannot create destination file");
return false;
}
f_lseek(&dfile, 0);
f_sync(&dfile);
ret = true;
for (u64 pos = 0; (pos < osize) && ret; pos += MAIN_BUFFER_SIZE) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, osize - pos);
UINT bytes_written = 0;
if (ReadVirtualFile(&vfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0)
ret = false;
if (!ShowProgress(pos + (read_bytes / 2), osize, orig))
ret = false;
if (fx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK)
ret = false;
if (read_bytes != bytes_written)
ret = false;
}
ShowProgress(1, 1, orig);
fx_close(&dfile);
if (!ret) f_unlink(dest);
2016-11-30 15:56:22 +01:00
}
*(--dname) = '\0';
2016-03-21 23:19:23 +01:00
return ret;
}
bool PathCopyWorker(char* dest, char* orig, u32* flags, bool move) {
2016-07-20 00:27:38 +02:00
FILINFO fno;
bool ret = false;
if (fa_stat(dest, &fno) != FR_OK) { // is root or destination does not exist
2016-03-14 17:22:53 +01:00
DIR tmp_dir; // check if root
if (fa_opendir(&tmp_dir, dest) != FR_OK) return false;
2016-03-14 17:22:53 +01:00
f_closedir(&tmp_dir);
} else if (!(fno.fattrib & AM_DIR)) return false; // destination is not a directory (must be at this point)
if (fa_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-06-13 23:51:41 +02:00
if (!ShowStringPrompt(dname, 255 - (dname - dest), "Destination is equal to origin\nChoose another name?"))
2016-05-20 14:12:03 +02:00
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
if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL)) && (fa_stat(dest, NULL) == FR_OK)) {
2016-07-28 03:34:39 +02:00
if (*flags & SKIP_ALL) {
*flags |= SKIP_CUR;
return true;
}
const char* optionstr[5] =
{"Choose new name", "Overwrite file(s)", "Skip file(s)", "Overwrite all", "Skip all"};
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);
u32 user_select = ShowSelectPrompt((*flags & ASK_ALL) ? 5 : 3, optionstr,
"Destination already exists:\n%s", namestr);
2016-05-26 21:08:35 +02:00
if (user_select == 1) {
do {
2016-06-13 23:51:41 +02:00
if (!ShowStringPrompt(dname, 255 - (dname - dest), "Choose new destination name"))
2016-05-26 21:08:35 +02:00
return false;
} while (fa_stat(dest, NULL) == FR_OK);
} else if (user_select == 2) {
*flags |= OVERWRITE_CUR;
} else if (user_select == 3) {
*flags |= SKIP_CUR;
return true;
} else if (user_select == 4) {
*flags |= OVERWRITE_ALL;
} else if (user_select == 5) {
2016-07-28 03:34:39 +02:00
*flags |= (SKIP_CUR|SKIP_ALL);
return true;
} else {
return false;
}
}
// check destination write permission (special paths only)
if (((*dest == '1') || (strncmp(dest, "0:/Nintendo 3DS", 16) == 0)) &&
(!flags || (*flags & (OVERWRITE_CUR|OVERWRITE_ALL))) &&
2017-02-22 23:27:34 +01:00
(!flags || !(*flags & OVERRIDE_PERM)) &&
2017-02-15 16:34:25 +01:00
!CheckWritePermissions(dest)) return false;
// the copy process takes place here
2016-03-02 17:22:44 +01:00
if (!ShowProgress(0, 0, orig)) return false;
if (move && fa_stat(dest, NULL) != FR_OK) { // moving if dest not existing
2016-04-29 02:21:36 +02:00
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 (fa_opendir(&pdir, dest) != FR_OK) {
if (f_mkdir(dest) != FR_OK) {
ShowPrompt(false, "Error: Overwriting file with dir");
return false;
}
} else f_closedir(&pdir);
if (fa_opendir(&pdir, orig) != FR_OK)
return false;
*(fname++) = '/';
while (f_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries
2016-07-20 00:27:38 +02:00
strncpy(fname, fno.fname, 256 - (fname - orig));
if (fno.fname[0] == 0) {
ret = true;
break;
} else if (!PathCopyWorker(dest, orig, flags, move)) {
break;
}
}
f_closedir(&pdir);
2016-04-29 02:21:36 +02:00
} else if (move) { // moving if destination exists
if (fa_stat(dest, &fno) != FR_OK)
2016-04-29 02:21:36 +02:00
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;
u64 fsize;
2016-12-13 17:10:39 +01:00
if (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
if (!FileUnlock(orig) || (fx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK))
return false;
ShowProgress(0, 0, orig); // reinit progress bar
}
fsize = f_size(&ofile);
if (GetFreeSpace(dest) < fsize) {
ShowPrompt(false, "Error: File is too big for destination");
fx_close(&ofile);
return false;
}
2016-10-30 15:57:54 +01:00
if (fx_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");
fx_close(&ofile);
return false;
}
f_lseek(&dfile, 0);
f_sync(&dfile);
f_lseek(&ofile, 0);
f_sync(&ofile);
ret = true;
for (u64 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;
2016-10-30 15:57:54 +01:00
if (fx_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;
2016-10-30 15:57:54 +01:00
if (fx_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);
fx_close(&ofile);
fx_close(&dfile);
2016-06-10 16:46:50 +02:00
if (!ret) f_unlink(dest);
}
2016-03-02 17:22:44 +01:00
*(--dname) = '\0';
return ret;
}
bool PathCopy(const char* destdir, const char* orig, u32* flags) {
2017-02-22 23:27:34 +01:00
if ((!flags || !(*flags & OVERRIDE_PERM)) && !CheckWritePermissions(destdir))
return false;
2017-02-15 16:34:25 +01:00
if (flags) *flags = *flags & ~(SKIP_CUR|OVERWRITE_CUR); // reset local flags
int ddrvtype = DriveType(destdir);
int odrvtype = DriveType(orig);
if (!(ddrvtype & DRV_VIRTUAL)) { // FAT / virtual to FAT
2016-03-21 23:19:23 +01:00
char fdpath[256]; // 256 is the maximum length of a full path
char fopath[256];
strncpy(fdpath, destdir, 255);
strncpy(fopath, orig, 255);
bool res = (odrvtype & DRV_VIRTUAL) ? PathCopyVrtToFat(fdpath, fopath, flags) :
PathCopyWorker(fdpath, fopath, flags, false);
return res;
} else if (!(odrvtype & DRV_VIRTUAL)) { // FAT to virtual
if (odrvtype & ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) {
ShowPrompt(false, "Copy operation is not allowed");
return false; // prevent illegal operations
2016-11-30 15:56:22 +01:00
}
2017-02-22 23:27:34 +01:00
return PathCopyFatToVrt(destdir, orig, flags);
} else return PathCopyVrtToVrt(destdir, orig, flags); // virtual to virtual
2016-04-29 02:21:36 +02:00
}
bool PathMove(const char* destdir, const char* orig, u32* flags) {
2017-02-22 23:27:34 +01:00
if (!flags || !(*flags & OVERRIDE_PERM)) {
if (!CheckWritePermissions(destdir)) return false;
if (!CheckDirWritePermissions(orig)) return false;
}
if (flags) *flags = *flags & ~(SKIP_CUR|OVERWRITE_CUR); // reset local flags
// moving only for regular FAT drives (= not alias drives)
if (!(DriveType(destdir) & DriveType(orig) & DRV_STDFAT)) {
ShowPrompt(false, "Error: Moving is not possible here");
2016-04-29 02:21:36 +02:00
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);
2016-12-10 15:32:03 +01:00
bool same_drv = (strncmp(orig, destdir, 2) == 0);
bool res = PathCopyWorker(fdpath, fopath, flags, same_drv);
2016-07-28 03:34:39 +02:00
if (res && (!flags || !(*flags&SKIP_CUR))) PathDelete(orig);
2016-04-29 02:21:36 +02:00
return res;
2016-03-21 23:19:23 +01:00
}
}
bool PathDeleteWorker(char* fpath) {
2016-07-20 00:27:38 +02:00
FILINFO fno;
// this code handles directory content deletion
if (fa_stat(fpath, &fno) != FR_OK) return false; // fpath does not exist
if (fno.fattrib & AM_DIR) { // process folder contents
DIR pdir;
char* fname = fpath + strnlen(fpath, 255);
if (fa_opendir(&pdir, fpath) != FR_OK)
return false;
*(fname++) = '/';
while (f_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries
2016-07-20 00:27:38 +02:00
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
2017-02-15 16:34:25 +01:00
if (!CheckDirWritePermissions(path)) return false;
strncpy(fpath, path, 256);
2017-02-15 16:34:25 +01:00
ShowString("Deleting files, please wait...");
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, '/');
2017-02-15 16:34:25 +01:00
if (!CheckDirWritePermissions(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));
2016-11-25 21:31:57 +01:00
return (f_rename(path, npath) == FR_OK);
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);
if (fa_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-06-17 17:12:11 +02:00
FileSetData(filename, MAIN_BUFFER, 54 + (400 * 240 * 3 * 2), 0, true);
2016-02-26 19:43:30 +01:00
}