/*//////////////////////////////////////////////////////////////////////////////////////// fsop contains coomprensive set of function for file and folder handling en exposed s_fsop fsop structure can be used by callback to update operation status ////////////////////////////////////////////////////////////////////////////////////////*/ #include #include #include #include #include #include #include #include #include #include "fileOps/fileOps.h" #include "gecko/gecko.hpp" #include "loader/utils.h" #define SET(a, b) a = b; DCFlushRange(&a, sizeof(a)); #define STACKSIZE 8192 static u8 *buff = NULL; static FILE *fs = NULL, *ft = NULL; static u32 block = 32768; static u32 blockIdx = 0; static u32 blockInfo[2] = {0,0}; static u32 blockReady = 0; static s32 stopThread; static u64 folderSize = 0; u64 FolderProgressBytes; // return false if the file doesn't exist bool fsop_GetFileSizeBytes(const char *path, u32 *filesize) // for me stats st_size report always 0 :( { FILE *f; size_t size = 0; f = fopen(path, "rb"); if(!f) { if(filesize) *filesize = size; return false; } //Get file size fseek(f, 0, SEEK_END); size = ftell(f); if(filesize) *filesize = size; fclose(f); return true; } /* Recursive fsop_GetFolderBytes */ u64 fsop_GetFolderBytes(const char *source) { DIR *pdir; struct dirent *pent; char newSource[1024]; u64 bytes = 0; pdir = opendir(source); while((pent = readdir(pdir)) != NULL) { // Skip it if(pent->d_name[0] == '.') continue; snprintf(newSource, sizeof(newSource), "%s/%s", source, pent->d_name); // If it is a folder... recurse... if(fsop_FolderExist(newSource)) bytes += fsop_GetFolderBytes(newSource); else // It is a file ! { u32 s; fsop_GetFileSizeBytes(newSource, &s); bytes += s; } } closedir(pdir); return bytes; } u32 fsop_GetFolderKb(const char *source) { u32 ret = (u32)round((double)fsop_GetFolderBytes (source) / 1000.0); return ret; } u32 fsop_GetFreeSpaceKb(const char *path) // Return free kb on the device passed { struct statvfs s; statvfs(path, &s); u32 ret = (u32)round(((double)s.f_bfree / 1000.0) * s.f_bsize); return ret ; } static void *thread_CopyFileReader() { u32 rb; stopThread = 0; DCFlushRange(&stopThread, sizeof(stopThread)); do { SET(rb, fread(&buff[blockIdx*block], 1, block, fs)); SET(blockInfo[blockIdx], rb); SET(blockReady, 1); while(blockReady && !stopThread) usleep(1); } while(stopThread == 0); stopThread = -1; DCFlushRange(&stopThread, sizeof(stopThread)); return 0; } bool fsop_CopyFile(const char *source, const char *target, progress_callback_t spinner, void *spinner_data) { //gprintf("Creating file: %s\n", target); int err = 0; u32 size; u32 rb, wb; fs = fopen(source, "rb"); if(!fs) return false; ft = fopen(target, "wb"); if(!ft) { fclose(fs); return false; } //Get file size fseek(fs, 0, SEEK_END); size = ftell(fs); if (size == 0) { fclose(fs); fclose(ft); return true; } // Return to beginning.... fseek(fs, 0, SEEK_SET); u8 *threadStack = NULL; lwp_t hthread = LWP_THREAD_NULL; buff = malloc(block * 2); if(buff == NULL) return false; blockIdx = 0; blockReady = 0; blockInfo[0] = 0; blockInfo[1] = 0; u32 bytes = 0; threadStack = malloc(STACKSIZE); if(threadStack == NULL) { free(buff); return false; } LWP_CreateThread(&hthread, thread_CopyFileReader, NULL, threadStack, STACKSIZE, 30); while(stopThread != 0) usleep(5); u32 bi; do { while(!blockReady) usleep(1); // Let's wait for incoming block from the thread bi = blockIdx; // let's th thread to read the next buff SET(blockIdx, 1 - blockIdx); SET(blockReady, 0); rb = blockInfo[bi]; // write current block wb = fwrite(&buff[bi*block], 1, rb, ft); if(wb != rb || rb == 0) err = 1; bytes += rb; if(spinner) { FolderProgressBytes += rb; spinner(FolderProgressBytes, folderSize, spinner_data); } } while(bytes < size && err == 0); stopThread = 1; DCFlushRange(&stopThread, sizeof(stopThread)); while(stopThread != -1) usleep(5); LWP_JoinThread(hthread, NULL); free(threadStack); stopThread = 1; DCFlushRange(&stopThread, sizeof(stopThread)); fclose(fs); fclose(ft); free(buff); if(err) { unlink(target); return false; } return true; } /* Recursive copyfolder */ static bool doCopyFolder(const char *source, const char *target, progress_callback_t spinner, void *spinner_data) { DIR *pdir; struct dirent *pent; char newSource[1024], newTarget[1024]; bool ret = true; // If target folder doesn't exist, create it ! fsop_MakeFolder(target); pdir = opendir(source); while((pent = readdir(pdir)) != NULL && ret == true) { // Skip it if(pent->d_name[0] == '.') continue; snprintf(newSource, sizeof(newSource), "%s/%s", source, pent->d_name); snprintf(newTarget, sizeof(newTarget), "%s/%s", target, pent->d_name); // If it is a folder... recurse... if(fsop_FolderExist(newSource)) ret = doCopyFolder(newSource, newTarget, spinner, spinner_data); else // It is a file ! ret = fsop_CopyFile(newSource, newTarget, spinner, spinner_data); } closedir(pdir); return ret; } bool fsop_CopyFolder(const char *source, const char *target, progress_callback_t spinner, void *spinner_data) { gprintf("DML game USB->SD job started!\n"); FolderProgressBytes = 0; folderSize = fsop_GetFolderBytes(source); return doCopyFolder(source, target, spinner, spinner_data); } void fsop_deleteFolder(const char *source) { DIR *pdir; struct dirent *pent; char newSource[1024]; pdir = opendir(source); /* first delete all subfolders and files in the folder */ while((pent = readdir(pdir)) != NULL) { // Skip it if(pent->d_name[0] == '.') continue; snprintf(newSource, sizeof(newSource), "%s/%s", source, pent->d_name); // If it is a folder... recurse... if(fsop_FolderExist(newSource)) { closedir(pdir); fsop_deleteFolder(newSource); pdir = opendir(source); } else // It is a file ! { closedir(pdir); fsop_deleteFile(newSource); pdir = opendir(source); } } closedir(pdir); /* now actually delete the folder */ gprintf("Deleting directory: %s\n", source); unlink(source);// using POSIX unlink to delete the folder } bool fsop_FileExist(const char *fn) { FILE * f; f = fopen(fn, "rb"); if(f) { fclose(f); return true; } return false; } void fsop_ReadFileLoc(const char *path, const u32 size, void *loc) { FILE *f = fopen(path, "rb"); fread(loc, size, 1, f); fclose(f); } u8 *fsop_ReadFile(const char *path, u32 *size) { *(size) = 0; u32 filesize = 0; u8 *mem = NULL; fsop_GetFileSizeBytes(path, &filesize); if(filesize > 0) { mem = (u8*)MEM2_alloc(filesize); if(mem != NULL) { //gprintf("Reading file: %s\n", path); fsop_ReadFileLoc(path, filesize, mem); *(size) = filesize; } } return mem; } bool fsop_WriteFile(const char *path, const void *mem, const u32 size) { if(mem == NULL || size == 0) return false; FILE *f = fopen(path, "wb"); if(f == NULL) return false; //gprintf("Writing file: %s\n", path); fwrite(mem, size, 1, f); fclose(f); return true; } void fsop_deleteFile(const char *source) { if(!fsop_FileExist(source)) return; remove(source); } bool fsop_FolderExist(const char *path) { DIR *dir; dir = opendir(path); if(dir) { closedir(dir); return true; } return false; } /*void fsop_MakeFolder(const char *path) { if(fsop_FolderExist(path)) return; //gprintf("Folder path to create: %s\n", path); mkdir(path, S_IREAD | S_IWRITE); }*/ bool fsop_MakeFolder(const char *fullpath) { if(!fullpath) return false; bool result = false; char dirnoslash[strlen(fullpath)+1]; strcpy(dirnoslash, fullpath); int pos = strlen(dirnoslash)-1; while(dirnoslash[pos] == '/') { dirnoslash[pos] = '\0'; pos--; } if(fsop_FolderExist(dirnoslash)) { return true; } else { char parentpath[strlen(dirnoslash)+2]; strcpy(parentpath, dirnoslash); char * ptr = strrchr(parentpath, '/'); if(!ptr) { //!Device root directory (must be with '/') strcat(parentpath, "/"); struct stat filestat; if (stat(parentpath, &filestat) == 0) return true; return false; } ptr++; ptr[0] = '\0'; result = fsop_MakeFolder(parentpath); } if(!result) return false; if (mkdir(dirnoslash, 0777) == -1) { return false; } return true; }