WiiFlow_Lite/source/fileOps/fileOps.c

457 lines
8.8 KiB
C
Raw Normal View History

/*////////////////////////////////////////////////////////////////////////////////////////
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 <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <ogcsys.h>
#include <ogc/lwp_watchdog.h>
#include <malloc.h>
#include <sys/statvfs.h>
#include <sys/stat.h>
#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);
2012-05-13 19:25:26 +02:00
u8 *threadStack = NULL;
lwp_t hthread = LWP_THREAD_NULL;
buff = malloc(block * 2);
2012-05-13 19:25:26 +02:00
if(buff == NULL)
return false;
blockIdx = 0;
blockReady = 0;
blockInfo[0] = 0;
blockInfo[1] = 0;
u32 bytes = 0;
threadStack = malloc(STACKSIZE);
2012-05-13 19:25:26 +02:00
if(threadStack == NULL)
{
free(buff);
2012-05-13 19:25:26 +02:00
return false;
}
2012-05-13 19:25:26 +02:00
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;
}
-fixed displaying errors on source menu -removed vipatch, country patch, private server and cheats defaults from wiiflow.ini. no need for default/global setting for those options. they all default to off internally in wiiflow. -now on file explorer menu pressing home btn returns you to device select so you don't have to keep backing up to get back to devices. -fixed a few minor problems with game configs, especially gc games. now all game config options should work properly. -now using bitwise operators and expressions on m_current_view to control which sources are selected. no more source= under each domain. just sources= under GENERAL. -replaced ocarina option from main settings pg3 with channels type option. no need for a global/default ocarina setting. channels type allows you to select real, emu, or both channels types for channels coverflow. which means no more pressing B on config icon to cycle thru the choices. -fixed a minor bug when downloading covers -changed newid.ini to use only one domain name "[NEWID]". this fixes a possible problem when downloading covers and using newid.ini and multisource. -added install wad option to nand emulation settings menu. I know you can do it via file explorer but this makes it easier for newbies to find. plus you can select which nand to install to. -fixed extract game saves. -fixed extract nand and install GC game by stopping music and controller input while doing these functions. -Install wii game is broke. added a error msg stating this and keeping users from using it. funny the code is almost identical to usbloader gx which works and wiiflow doesn't. -now showing all btns on game selected screen. if wiiflow is locked a error msg appears stating this. -changed makeDir to make the whole path not just one folder. thanks to usbloader gx code. -added .ciso extension to gamecube list maker so you can use them with nintendont.
2016-12-01 01:05:39 +01:00
/*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);
-fixed displaying errors on source menu -removed vipatch, country patch, private server and cheats defaults from wiiflow.ini. no need for default/global setting for those options. they all default to off internally in wiiflow. -now on file explorer menu pressing home btn returns you to device select so you don't have to keep backing up to get back to devices. -fixed a few minor problems with game configs, especially gc games. now all game config options should work properly. -now using bitwise operators and expressions on m_current_view to control which sources are selected. no more source= under each domain. just sources= under GENERAL. -replaced ocarina option from main settings pg3 with channels type option. no need for a global/default ocarina setting. channels type allows you to select real, emu, or both channels types for channels coverflow. which means no more pressing B on config icon to cycle thru the choices. -fixed a minor bug when downloading covers -changed newid.ini to use only one domain name "[NEWID]". this fixes a possible problem when downloading covers and using newid.ini and multisource. -added install wad option to nand emulation settings menu. I know you can do it via file explorer but this makes it easier for newbies to find. plus you can select which nand to install to. -fixed extract game saves. -fixed extract nand and install GC game by stopping music and controller input while doing these functions. -Install wii game is broke. added a error msg stating this and keeping users from using it. funny the code is almost identical to usbloader gx which works and wiiflow doesn't. -now showing all btns on game selected screen. if wiiflow is locked a error msg appears stating this. -changed makeDir to make the whole path not just one folder. thanks to usbloader gx code. -added .ciso extension to gamecube list maker so you can use them with nintendont.
2016-12-01 01:05:39 +01:00
}*/
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;
}
-fixed displaying errors on source menu -removed vipatch, country patch, private server and cheats defaults from wiiflow.ini. no need for default/global setting for those options. they all default to off internally in wiiflow. -now on file explorer menu pressing home btn returns you to device select so you don't have to keep backing up to get back to devices. -fixed a few minor problems with game configs, especially gc games. now all game config options should work properly. -now using bitwise operators and expressions on m_current_view to control which sources are selected. no more source= under each domain. just sources= under GENERAL. -replaced ocarina option from main settings pg3 with channels type option. no need for a global/default ocarina setting. channels type allows you to select real, emu, or both channels types for channels coverflow. which means no more pressing B on config icon to cycle thru the choices. -fixed a minor bug when downloading covers -changed newid.ini to use only one domain name "[NEWID]". this fixes a possible problem when downloading covers and using newid.ini and multisource. -added install wad option to nand emulation settings menu. I know you can do it via file explorer but this makes it easier for newbies to find. plus you can select which nand to install to. -fixed extract game saves. -fixed extract nand and install GC game by stopping music and controller input while doing these functions. -Install wii game is broke. added a error msg stating this and keeping users from using it. funny the code is almost identical to usbloader gx which works and wiiflow doesn't. -now showing all btns on game selected screen. if wiiflow is locked a error msg appears stating this. -changed makeDir to make the whole path not just one folder. thanks to usbloader gx code. -added .ciso extension to gamecube list maker so you can use them with nintendont.
2016-12-01 01:05:39 +01:00