/*//////////////////////////////////////////////////////////////////////////////////////// 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 //for mkdir #include #define GB_SIZE 1073741824.0 #include "fileOps.h" #include "video.h" #include "gettext.h" #include "util.h" #include "wpad.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 = 65536 * 8; static u32 blockIdx = 0; static u32 blockInfo[2] = {0,0}; static u32 blockReady = 0; static u32 stopThread; static u64 folderSize = 0; static u64 bytesCopiedPrevFiles = 0; void __Copy_Spinner(s64 x, u64 max) { static time_t start; static u32 expected; static u32 prev_d; f32 percent, size; u32 d, h, m, s; /* First time */ if (!x) { start = time(0); expected = 300; } /* Elapsed time */ d = time(0) - start; if (x != max) { /* only update the display once a second results in 25% speed improvement */ if (prev_d == d) return; prev_d = d; /* Expected time */ if (d && x) expected = (expected * 3 + d * max / x) / 4; /* Remaining time */ d = (expected > d) ? (expected - d) : 1; } /* Calculate time values */ h = d / 3600; m = (d / 60) % 60; s = d % 60; /* Calculate percentage/size */ percent = (x * 100.0) / max; size = (max / GB_SIZE); Con_ClearLine(); /* Show progress */ if (x != max) { printf_(gt("%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d"), percent, size, "/-\\|"[(x / 10) % 4], h, m, s); printf("\r"); fflush(stdout); } else { printf_(gt("%.2fGB copied in %d:%02d:%02d"), size, h, m, s); printf(" \n"); // printf(gt("Press any button...")); // printf("\n"); // Wpad_WaitButtons(); } __console_flush(1); } // return false if the file doesn't exist bool fsop_GetFileSizeBytes (char *path, size_t *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 (char *source) { DIR *pdir; struct dirent *pent; char newSource[300]; u64 bytes = 0; pdir=opendir(source); while ((pent=readdir(pdir)) != NULL) { // Skip it if (strcmp (pent->d_name, ".") == 0 || strcmp (pent->d_name, "..") == 0) continue; sprintf (newSource, "%s/%s", source, pent->d_name); // If it is a folder... recurse... if (fsop_DirExist (newSource)) { bytes += fsop_GetFolderBytes (newSource); } else // It is a file ! { size_t s; fsop_GetFileSizeBytes (newSource, &s); bytes += s; } } closedir(pdir); //Debug ("fsop_GetFolderBytes (%s) = %llu", source, bytes); return bytes; } u32 fsop_GetFolderKb (char *source) { u32 ret = (u32) round ((double)fsop_GetFolderBytes (source) / 1000.0); return ret; } u32 fsop_GetFreeSpaceKb (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 ; } bool fsop_FileExist (char *fn) { FILE * f; f = fopen(fn, "rb"); if (!f) return false; fclose(f); return true; } bool fsop_DirExist (char *path) { DIR *dir; dir=opendir(path); if (dir) { closedir(dir); return true; } return false; } static void *thread_CopyFileReader (void *arg) { 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 doCopyFile (char *source, char *target) { int err = 0; u32 size; u32 bytes, rb,wb; if (strstr (source, "usb:") && strstr (target, "usb:")) { block = 1024*1024; } else block = 65536 * 8; fs = fopen(source, "rb"); if (!fs) { return false; } ft = fopen(target, "wt"); 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 = memalign (32, block*2); if (buff == NULL) { fclose (fs); fclose (ft); return false; } blockIdx = 0; blockReady = 0; blockInfo[0] = 0; blockInfo[1] = 0; threadStack = memalign(32,STACKSIZE); LWP_CreateThread (&hthread, thread_CopyFileReader, NULL, threadStack, STACKSIZE, 30); while (stopThread != 0) usleep (5); bytes = 0; 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 (rb != wb) err = 1; if (rb == 0) err = 1; bytes += rb; __Copy_Spinner(bytes + bytesCopiedPrevFiles, folderSize); } 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); bytesCopiedPrevFiles += bytes; //keep total bytes when copying dir if (err) { unlink (target); return false; } return true; } bool fsop_CopyFile (char *source, char *target) { size_t s; fsop_GetFileSizeBytes (source, &s); folderSize = s; __Copy_Spinner(0, folderSize); // init spinner bytesCopiedPrevFiles = 0; return doCopyFile (source, target); } /* Semplified folder make */ bool fsop_MakeFolder (char *path) { if (mkdir(path, S_IREAD | S_IWRITE) == 0) return true; return false; } /* Recursive copyfolder */ static bool doCopyFolder (char *source, char *target) { DIR *pdir; struct dirent *pent; char newSource[300], newTarget[300]; bool ret = true; // If target folder doesn't exist, create it ! if (!fsop_DirExist (target)) { fsop_MakeFolder (target); } pdir=opendir(source); while ((pent=readdir(pdir)) != NULL && ret == true) { // Skip it if (strcmp (pent->d_name, ".") == 0 || strcmp (pent->d_name, "..") == 0) continue; sprintf (newSource, "%s/%s", source, pent->d_name); sprintf (newTarget, "%s/%s", target, pent->d_name); // If it is a folder... recurse... if (fsop_DirExist (newSource)) { ret = doCopyFolder (newSource, newTarget); } else // It is a file ! { ret = doCopyFile (newSource, newTarget); } } closedir(pdir); return ret; } bool fsop_CopyFolder (char *source, char *target) { folderSize = fsop_GetFolderBytes(source); __Copy_Spinner(0, folderSize); // init spinner bytesCopiedPrevFiles = 0; return doCopyFolder (source, target); } // Pass :///.../ or ://.../ bool fsop_CreateFolderTree (char *path) { int i; int start, len; char buff[300]; char b[8]; char *p; start = 0; strcpy (b, "://"); p = strstr (path, b); if (p == NULL) { strcpy (b, ":/"); p = strstr (path, b); if (p == NULL) return false; // path must contain } start = (p - path) + strlen(b); len = strlen(path); for (i = start; i <= len; i++) { if (path[i] == '/' || i == len) { strcpy (buff, path); buff[i] = 0; if (!fsop_DirExist(buff)) fsop_MakeFolder(buff); } } // Check if the tree has been created return fsop_DirExist (path); } void fsop_deleteFolder (char *source) { DIR *pdir; struct dirent *pent; char newSource[300]; pdir = opendir(source); while ((pent=readdir(pdir)) != NULL) { // Skip it if (strcmp (pent->d_name, ".") == 0 || strcmp (pent->d_name, "..") == 0) continue; sprintf (newSource, "%s/%s", source, pent->d_name); // If it is a folder... recurse... if (fsop_DirExist(newSource)) { fsop_deleteFolder(newSource); } else // It is a file ! { dbg_printf("Deleting file: %s\n",newSource); remove(newSource); } } closedir(pdir); dbg_printf("Deleting directory: %s\n",source); unlink(source); } // Count the number of folder in a full path. It can be path1/path2/path3 or ://path1/path2/path3 int fsop_CountFolderTree (char *path) { int i; int start, len; char b[8]; char *p; int count = 0; start = 0; strcpy (b, "://"); p = strstr (path, b); if (p == NULL) { strcpy (b, ":/"); p = strstr (path, b); } if (p == NULL) start = 0; else start = (p - path) + strlen(b); len = strlen(path); if (path[len-1] == '/') len--; if (len <= 0) return 0; for (i = start; i <= len; i++) { if (path[i] == '/' || i == len) { count ++; } } // Check if the tree has been created return count; } // read a file from disk u8 *fsop_ReadFile (char *path, size_t bytes2read, size_t *bytesReaded) { FILE *f; size_t size = 0; f = fopen(path, "rb"); if (!f) { if (bytesReaded) *bytesReaded = size; return NULL; } //Get file size fseek( f, 0, SEEK_END); size = ftell(f); if (size == 0) { fclose (f); return NULL; } // goto to start fseek( f, 0, SEEK_SET); if (bytes2read > 0 && bytes2read < size) size = bytes2read; u8 *buff = malloc (size+1); size = fread (buff, 1, size, f); fclose (f); buff[size] = 0; // maybe we have readed a ascii file, let's 0 terminate it... if (bytesReaded) *bytesReaded = size; return buff; } // write a buffer to disk bool fsop_WriteFile (char *path, u8 *buff, size_t len) { FILE *f; size_t size = 0; f = fopen(path, "wb"); if (!f) { return false; } size = fwrite (buff, 1, len, f); fclose (f); if (size == len) return true; return false; }