363 lines
12 KiB
C
Raw Normal View History

#include "draw.h"
#include "fs.h"
#include "fatfs/ff.h"
// don't use this area for anything else!
static FATFS* fs = (FATFS*)0x20316000;
// reserve one MB for this, just to be safe -> 512kb is more than enough!
static DirStruct* curdir_contents = (DirStruct*)0x21000000;
// this is the main buffer
2016-02-26 19:43:30 +01:00
static u8* main_buffer = (u8*)0x21100000;
// this is the main buffer size
static size_t main_buffer_size = 4 * 1024 * 1024;
// number of currently open file systems
static u32 numfs = 0;
bool InitFS() {
#ifndef EXEC_GATEWAY
// TODO: Magic?
*(u32*)0x10000020 = 0;
*(u32*)0x10000020 = 0x340;
#endif
2016-02-27 19:58:41 +01:00
for (numfs = 0; numfs < 7; numfs++) {
char fsname[8];
snprintf(fsname, 8, "%lu:", numfs);
int res = f_mount(fs + numfs, fsname, 1);
if (res != FR_OK) {
if (numfs >= 4) break;
2016-02-27 19:58:41 +01:00
ShowPrompt(false, "Initialising failed! (%lu/%s/%i)", numfs, fsname, res);
DeinitFS();
return false;
}
}
return true;
}
void DeinitFS() {
for (u32 i = 0; i < numfs; i++) {
char fsname[8];
snprintf(fsname, 7, "%lu:", numfs);
f_mount(NULL, fsname, 1);
}
numfs = 0;
}
2016-02-26 19:43:30 +01:00
bool FileCreate(const char* path, u8* data, u32 size) {
FIL file;
UINT bytes_written = 0;
if (f_open(&file, path, FA_READ | FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)
return false;
f_write(&file, data, size, &bytes_written);
f_close(&file);
return (bytes_written == size);
}
bool PathCopyWorker(char* dest, char* orig) {
FILINFO fno;
bool ret = false;
if (f_stat(dest, &fno) != FR_OK) return false; // destination directory does not exist
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
// get filename, build full destination path
char* oname = strrchr(orig, '/');
char* dname = dest + strnlen(dest, 256);
if (oname == NULL) return false; // not a proper origin path
oname++;
*(dname++) = '/';
strncpy(dname, oname, 256 - (dname - dest));
// check if destination exists
if (f_stat(dest, NULL) == FR_OK) {
char tempstr[40];
TruncateString(tempstr, dest, 36, 8);
if (!ShowPrompt(true, "Destination already exists:\n%s\nOverwrite existing file(s)?"))
return false;
}
// the copy process takes place here
ShowProgress(0, 0, orig, true);
if (fno.fattrib & AM_DIR) { // processing folders...
DIR pdir;
char* fname = orig + strnlen(orig, 256);
*(fname++) = '/';
fno.lfname = fname;
fno.lfsize = 256 - (fname - orig);
if (f_stat(dest, NULL) != FR_OK)
f_mkdir(dest);
if (f_stat(dest, NULL) != FR_OK)
return false;
if (f_opendir(&pdir, orig) != FR_OK)
return false;
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;
} else if (!PathCopyWorker(dest, orig)) {
break;
}
}
f_closedir(&pdir);
} else { // processing files...
FIL ofile;
FIL dfile;
size_t fsize;
if (f_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false;
if (f_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) {
f_close(&ofile);
return false;
}
fsize = f_size(&ofile);
f_lseek(&dfile, 0);
f_sync(&dfile);
f_lseek(&ofile, 0);
f_sync(&ofile);
for (size_t pos = 0; pos < fsize; pos += main_buffer_size) {
UINT bytes_read = 0;
UINT bytes_written = 0;
ShowProgress(pos, fsize, orig, false);
f_read(&ofile, main_buffer, main_buffer_size, &bytes_read);
f_write(&dfile, main_buffer, bytes_read, &bytes_written);
if (bytes_read != bytes_written) {
ret = false;
break;
}
}
f_close(&ofile);
f_close(&dfile);
}
return ret;
}
bool PathCopy(const char* destdir, const char* orig) {
char fdpath[256]; // 256 is the maximum length of a full path
char fopath[256];
strncpy(fdpath, destdir, 256);
strncpy(fopath, orig, 256);
return PathCopyWorker(fdpath, fopath);
}
bool PathDeleteWorker(char* fpath) {
FILINFO fno;
bool ret = true;
// the deletion process takes place here
if (f_stat(fpath, &fno) != FR_OK) return false; // fpath does not exist
if (fno.fattrib & AM_DIR) { // processing folders...
DIR pdir;
char* fname = fpath + strnlen(fpath, 256);
*(fname++) = '/';
fno.lfname = fname;
fno.lfsize = 256 - (fname - fpath);
if (f_opendir(&pdir, fpath) != FR_OK)
return false;
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 - fpath));
if (fno.fname[0] == 0) {
break;
} else if (!PathDeleteWorker(fpath)) {
ret = false;
}
}
f_closedir(&pdir);
} else { // processing files...
ret = (f_unlink(fpath) == FR_OK);
}
return ret;
}
bool PathDelete(const char* path) {
char fpath[256]; // 256 is the maximum length of a full path
strncpy(fpath, path, 256);
return PathDeleteWorker(fpath);
}
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
};
u8* buffer = main_buffer + 54;
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;
memcpy(main_buffer, bmp_header, 54);
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_SCREEN0 + (x*240 + y) * 3, 3);
for (u32 x = 0; x < 320; x++)
for (u32 y = 0; y < 240; y++)
memcpy(buffer + (y*400 + x + 40) * 3, BOT_SCREEN0 + (x*240 + y) * 3, 3);
FileCreate(filename, main_buffer, 54 + (400 * 240 * 3 * 2));
}
void SortDirStruct(DirStruct* contents) {
for (u32 s = 0; s < contents->n_entries; s++) {
DirEntry* cmp0 = &(contents->entry[s]);
DirEntry* min0 = cmp0;
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
u32 offset_name_cmp0 = cmp0->name - cmp0->path;
u32 offset_name_min0 = min0->name - min0->path;
memcpy(&swap, cmp0, sizeof(DirEntry));
memcpy(cmp0, min0, sizeof(DirEntry));
memcpy(min0, &swap, sizeof(DirEntry));
cmp0->name = cmp0->path + offset_name_min0;
min0->name = min0->path + offset_name_cmp0;
}
}
}
2016-02-26 19:43:30 +01:00
bool GetRootDirContentsWorker(DirStruct* contents) {
static const char* drvname[16] = {
"SDCARD",
2016-02-27 19:58:41 +01:00
"SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP",
"EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP"
};
for (u32 pdrv = 0; (pdrv < numfs) && (pdrv < MAX_ENTRIES); pdrv++) {
memset(contents->entry[pdrv].path, 0x00, 16);
snprintf(contents->entry[pdrv].path + 0, 4, "%lu:", pdrv);
2016-02-27 19:58:41 +01:00
snprintf(contents->entry[pdrv].path + 4, 32, "[%lu:] %s", pdrv, drvname[pdrv]);
contents->entry[pdrv].name = contents->entry[pdrv].path + 4;
2016-02-27 19:58:41 +01:00
contents->entry[pdrv].size = GetTotalSpace(contents->entry[pdrv].path);
contents->entry[pdrv].type = T_FAT_ROOT;
contents->entry[pdrv].marked = 0;
}
contents->n_entries = numfs;
return contents->n_entries;
}
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) {
entry->type = T_FAT_DIR;
entry->size = 0;
} else {
entry->type = T_FAT_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-26 19:43:30 +01:00
DirStruct* GetDirContents(const char* path) {
curdir_contents->n_entries = 0;
if (strncmp(path, "", 256) == 0) { // root directory
if (!GetRootDirContentsWorker(curdir_contents))
curdir_contents->n_entries = 0; // not required, but so what?
} else {
char fpath[256]; // 256 is the maximum length of a full path
strncpy(fpath, path, 256);
if (!GetDirContentsWorker(curdir_contents, fpath, 256, false))
curdir_contents->n_entries = 0;
SortDirStruct(curdir_contents);
}
return curdir_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' };
int fsnum = -1;
strncpy(fsname, path, 2);
fsnum = *fsname - (int) '0';
if ((fsnum < 0) || (fsnum >= 7) || (fsname[1] != ':'))
return -1;
if (f_getfree(fsname, &free_clusters, &fs_ptr) != FR_OK)
return -1;
2016-02-27 19:58:41 +01:00
return (uint64_t) free_clusters * fs[fsnum].csize * _MAX_SS;
}
2016-02-27 19:58:41 +01:00
uint64_t GetTotalSpace(const char* path)
{
2016-02-27 19:58:41 +01:00
int fsnum = -1;
fsnum = *path - (int) '0';
if ((fsnum < 0) || (fsnum >= 7) || (path[1] != ':'))
return -1;
return (uint64_t) (fs[fsnum].n_fatent - 2) * fs[fsnum].csize * _MAX_SS;
}