#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 static DirStruct* curdir_contents = (DirStruct*)0x21000000; // this is the main buffer 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 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; 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; } 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 Screenshot() { 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); if (f_stat(filename, NULL) != FR_OK) break; } 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)); } bool GetRootDirContentsWorker(DirStruct* contents) { static const char* drvname[16] = { "SDCARD", "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); snprintf(contents->entry[pdrv].path + 4, 32, "[%lu:] %s", pdrv, drvname[pdrv]); contents->entry[pdrv].name = contents->entry[pdrv].path + 4; 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; } 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]); 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; } 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; } 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; } return curdir_contents; } uint64_t GetFreeSpace(const char* path) { DWORD free_clusters; 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; return (uint64_t) free_clusters * fs[fsnum].csize * _MAX_SS; } uint64_t GetTotalSpace(const char* path) { 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; }