From 738a750593a97898f90ea32cab50aad0c08bf286 Mon Sep 17 00:00:00 2001 From: BullyWiiPlaza Date: Sun, 27 Aug 2017 12:58:57 +0200 Subject: [PATCH] Implement almost working SD card cheats --- src/fs/fs_utils.c | 166 ++--- src/fs/fs_utils.h | 1 + src/fs/sd_fat_devoptab.c | 1462 ++++++++++++++++++-------------------- src/tcp_gecko.cpp | 68 +- tcpgecko.elf | Bin 236212 -> 247444 bytes 5 files changed, 836 insertions(+), 861 deletions(-) diff --git a/src/fs/fs_utils.c b/src/fs/fs_utils.c index ef8885e..abb2dc1 100644 --- a/src/fs/fs_utils.c +++ b/src/fs/fs_utils.c @@ -5,118 +5,113 @@ #include #include "common/fs_defs.h" #include "../dynamic_libs/fs_functions.h" +#include "../utils/logger.h" -int MountFS(void *pClient, void *pCmd, char **mount_path) -{ - int result = -1; +int MountFS(void *pClient, void *pCmd, char **mount_path) { + int result = -1; - void *mountSrc = malloc(FS_MOUNT_SOURCE_SIZE); - if(!mountSrc) - return -3; + void *mountSrc = malloc(FS_MOUNT_SOURCE_SIZE); + if (!mountSrc) + return -3; - char* mountPath = (char*) malloc(FS_MAX_MOUNTPATH_SIZE); - if(!mountPath) { - free(mountSrc); - return -4; - } + char *mountPath = (char *) malloc(FS_MAX_MOUNTPATH_SIZE); + if (!mountPath) { + free(mountSrc); + return -4; + } - memset(mountSrc, 0, FS_MOUNT_SOURCE_SIZE); - memset(mountPath, 0, FS_MAX_MOUNTPATH_SIZE); + memset(mountSrc, 0, FS_MOUNT_SOURCE_SIZE); + memset(mountPath, 0, FS_MAX_MOUNTPATH_SIZE); - // Mount sdcard - if (FSGetMountSource(pClient, pCmd, FS_SOURCETYPE_EXTERNAL, mountSrc, -1) == 0) - { - result = FSMount(pClient, pCmd, mountSrc, mountPath, FS_MAX_MOUNTPATH_SIZE, -1); - if((result == 0) && mount_path) { - *mount_path = (char*)malloc(strlen(mountPath) + 1); - if(*mount_path) - strcpy(*mount_path, mountPath); - } - } + // Mount sdcard + result = FSGetMountSource(pClient, pCmd, FS_SOURCETYPE_EXTERNAL, mountSrc, -1); + if (result == 0) { + result = FSMount(pClient, pCmd, mountSrc, mountPath, FS_MAX_MOUNTPATH_SIZE, -1); + if ((result == 0) && mount_path) { + *mount_path = (char *) malloc(strlen(mountPath) + 1); + if (*mount_path) + strcpy(*mount_path, mountPath); + } else { + log_printf("FSMount error: %i\n", result); + } + } else { + log_printf("FSGetMountSource error: %i\n", result); + } - free(mountPath); - free(mountSrc); - return result; + free(mountPath); + free(mountSrc); + + return result; } -int UmountFS(void *pClient, void *pCmd, const char *mountPath) -{ - int result = -1; - result = FSUnmount(pClient, pCmd, mountPath, -1); - - return result; +int UmountFS(void *pClient, void *pCmd, const char *mountPath) { + return FSUnmount(pClient, pCmd, mountPath, -1); } -int LoadFileToMem(const char *filepath, u8 **inbuffer, u32 *size) -{ - // Always initialize input +int LoadFileToMem(const char *filepath, u8 **inbuffer, u32 *size) { + // Always initialize input *inbuffer = NULL; - if(size) - *size = 0; + if (size) + *size = 0; - int iFd = open(filepath, O_RDONLY); + int iFd = open(filepath, O_RDONLY); if (iFd < 0) return -1; u32 filesize = lseek(iFd, 0, SEEK_END); - lseek(iFd, 0, SEEK_SET); + lseek(iFd, 0, SEEK_SET); u8 *buffer = (u8 *) malloc(filesize); - if (buffer == NULL) - { - close(iFd); + if (buffer == NULL) { + close(iFd); return -2; } - u32 blocksize = 0x4000; - u32 done = 0; - int readBytes = 0; + u32 blocksize = 0x4000; + u32 done = 0; + int readBytes = 0; - while(done < filesize) - { - if(done + blocksize > filesize) { - blocksize = filesize - done; - } - readBytes = read(iFd, buffer + done, blocksize); - if(readBytes <= 0) - break; - done += readBytes; - } + while (done < filesize) { + if (done + blocksize > filesize) { + blocksize = filesize - done; + } + readBytes = read(iFd, buffer + done, blocksize); + if (readBytes <= 0) + break; + done += readBytes; + } - close(iFd); + close(iFd); - if (done != filesize) - { + if (done != filesize) { free(buffer); return -3; } *inbuffer = buffer; - //! sign is optional input - if(size) - *size = filesize; + //! sign is optional input + if (size) + *size = filesize; return filesize; } -int CheckFile(const char * filepath) -{ - if(!filepath) +int CheckFile(const char *filepath) { + if (!filepath) return 0; struct stat filestat; - char dirnoslash[strlen(filepath)+2]; + char dirnoslash[strlen(filepath) + 2]; snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath); - while(dirnoslash[strlen(dirnoslash)-1] == '/') - dirnoslash[strlen(dirnoslash)-1] = '\0'; + while (dirnoslash[strlen(dirnoslash) - 1] == '/') + dirnoslash[strlen(dirnoslash) - 1] = '\0'; - char * notRoot = strrchr(dirnoslash, '/'); - if(!notRoot) - { + char *notRoot = strrchr(dirnoslash, '/'); + if (!notRoot) { strcat(dirnoslash, "/"); } @@ -126,35 +121,29 @@ int CheckFile(const char * filepath) return 0; } -int CreateSubfolder(const char * fullpath) -{ - if(!fullpath) +int CreateSubfolder(const char *fullpath) { + if (!fullpath) return 0; int result = 0; - char dirnoslash[strlen(fullpath)+1]; + char dirnoslash[strlen(fullpath) + 1]; strcpy(dirnoslash, fullpath); - int pos = strlen(dirnoslash)-1; - while(dirnoslash[pos] == '/') - { + int pos = strlen(dirnoslash) - 1; + while (dirnoslash[pos] == '/') { dirnoslash[pos] = '\0'; pos--; } - if(CheckFile(dirnoslash)) - { + if (CheckFile(dirnoslash)) { return 1; - } - else - { - char parentpath[strlen(dirnoslash)+2]; + } else { + char parentpath[strlen(dirnoslash) + 2]; strcpy(parentpath, dirnoslash); - char * ptr = strrchr(parentpath, '/'); + char *ptr = strrchr(parentpath, '/'); - if(!ptr) - { + if (!ptr) { //!Device root directory (must be with '/') strcat(parentpath, "/"); struct stat filestat; @@ -170,11 +159,10 @@ int CreateSubfolder(const char * fullpath) result = CreateSubfolder(parentpath); } - if(!result) + if (!result) return 0; - if (mkdir(dirnoslash, 0777) == -1) - { + if (mkdir(dirnoslash, 0777) == -1) { return 0; } diff --git a/src/fs/fs_utils.h b/src/fs/fs_utils.h index 7022695..95f9847 100644 --- a/src/fs/fs_utils.h +++ b/src/fs/fs_utils.h @@ -6,6 +6,7 @@ extern "C" { #endif #include +#include "../common/fs_defs.h" int MountFS(void *pClient, void *pCmd, char **mount_path); int UmountFS(void *pClient, void *pCmd, const char *mountPath); diff --git a/src/fs/sd_fat_devoptab.c b/src/fs/sd_fat_devoptab.c index c458ad2..c7d338f 100644 --- a/src/fs/sd_fat_devoptab.c +++ b/src/fs/sd_fat_devoptab.c @@ -32,988 +32,944 @@ #include "../dynamic_libs/fs_functions.h" #include "../dynamic_libs/os_functions.h" #include "fs_utils.h" +#include "../utils/logger.h" #define FS_ALIGNMENT 0x40 #define FS_ALIGN(x) (((x) + FS_ALIGNMENT - 1) & ~(FS_ALIGNMENT - 1)) typedef struct _sd_fat_private_t { - char *mount_path; - void *pClient; - void *pCmd; - void *pMutex; + char *mount_path; + void *pClient; + void *pCmd; + void *pMutex; } sd_fat_private_t; typedef struct _sd_fat_file_state_t { - sd_fat_private_t *dev; - int fd; /* File descriptor */ - int flags; /* Opening flags */ - bool read; /* True if allowed to read from file */ - bool write; /* True if allowed to write to file */ - bool append; /* True if allowed to append to file */ - u64 pos; /* Current position within the file (in bytes) */ - u64 len; /* Total length of the file (in bytes) */ - struct _sd_fat_file_state_t *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ - struct _sd_fat_file_state_t *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ + sd_fat_private_t *dev; + int fd; /* File descriptor */ + int flags; /* Opening flags */ + bool read; /* True if allowed to read from file */ + bool write; /* True if allowed to write to file */ + bool append; /* True if allowed to append to file */ + u64 pos; /* Current position within the file (in bytes) */ + u64 len; /* Total length of the file (in bytes) */ + struct _sd_fat_file_state_t *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ + struct _sd_fat_file_state_t *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ } sd_fat_file_state_t; typedef struct _sd_fat_dir_entry_t { - sd_fat_private_t *dev; - int dirHandle; + sd_fat_private_t *dev; + int dirHandle; } sd_fat_dir_entry_t; -static sd_fat_private_t *sd_fat_get_device_data(const char *path) -{ - const devoptab_t *devoptab = NULL; - char name[128] = {0}; - int i; +static sd_fat_private_t *sd_fat_get_device_data(const char *path) { + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; - // Get the device name from the path - strncpy(name, path, 127); - strtok(name, ":/"); + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); - // Search the devoptab table for the specified device name - // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab - // which ignores names with suffixes and causes names - // like "ntfs" and "ntfs1" to be seen as equals - for (i = 3; i < STD_MAX; i++) { - devoptab = devoptab_list[i]; - if (devoptab && devoptab->name) { - if (strcmp(name, devoptab->name) == 0) { - return (sd_fat_private_t *)devoptab->deviceData; - } - } - } + // Search the devoptab table for the specified device name + // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 3; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + return (sd_fat_private_t *) devoptab->deviceData; + } + } + } - return NULL; + return NULL; } -static char *sd_fat_real_path (const char *path, sd_fat_private_t *dev) -{ - // Sanity check - if (!path) - return NULL; +static char *sd_fat_real_path(const char *path, sd_fat_private_t *dev) { + // Sanity check + if (!path) + return NULL; - // Move the path pointer to the start of the actual path - if (strchr(path, ':') != NULL) { - path = strchr(path, ':') + 1; - } + // Move the path pointer to the start of the actual path + if (strchr(path, ':') != NULL) { + path = strchr(path, ':') + 1; + } - int mount_len = strlen(dev->mount_path); + int mount_len = strlen(dev->mount_path); - char *new_name = (char*)malloc(mount_len + strlen(path) + 1); - if(new_name) { - strcpy(new_name, dev->mount_path); - strcpy(new_name + mount_len, path); - return new_name; - } - return new_name; + char *new_name = (char *) malloc(mount_len + strlen(path) + 1); + if (new_name) { + strcpy(new_name, dev->mount_path); + strcpy(new_name + mount_len, path); + return new_name; + } + return new_name; } -static int sd_fat_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(path); - if(!dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if (!dev) { + r->_errno = ENODEV; + return -1; + } - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fileStruct; + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fileStruct; - file->dev = dev; - // Determine which mode the file is opened for - file->flags = flags; + file->dev = dev; + // Determine which mode the file is opened for + file->flags = flags; - const char *mode_str; + const char *mode_str; - if ((flags & 0x03) == O_RDONLY) { - file->read = true; - file->write = false; - file->append = false; - mode_str = "r"; - } else if ((flags & 0x03) == O_WRONLY) { - file->read = false; - file->write = true; - file->append = (flags & O_APPEND); - mode_str = file->append ? "a" : "w"; - } else if ((flags & 0x03) == O_RDWR) { - file->read = true; - file->write = true; - file->append = (flags & O_APPEND); - mode_str = file->append ? "a+" : "r+"; - } else { - r->_errno = EACCES; - return -1; - } + if ((flags & 0x03) == O_RDONLY) { + file->read = true; + file->write = false; + file->append = false; + mode_str = "r"; + } else if ((flags & 0x03) == O_WRONLY) { + file->read = false; + file->write = true; + file->append = (flags & O_APPEND); + mode_str = file->append ? "a" : "w"; + } else if ((flags & 0x03) == O_RDWR) { + file->read = true; + file->write = true; + file->append = (flags & O_APPEND); + mode_str = file->append ? "a+" : "r+"; + } else { + r->_errno = EACCES; + return -1; + } - int fd = -1; + int fd = -1; - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - char *real_path = sd_fat_real_path(path, dev); - if(!path) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return -1; - } + char *real_path = sd_fat_real_path(path, dev); + if (!path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } - int result = FSOpenFile(dev->pClient, dev->pCmd, real_path, mode_str, &fd, -1); + int result = FSOpenFile(dev->pClient, dev->pCmd, real_path, mode_str, &fd, -1); - free(real_path); + free(real_path); - if(result == 0) - { - FSStat stats; - result = FSGetStatFile(dev->pClient, dev->pCmd, fd, &stats, -1); - if(result != 0) { - FSCloseFile(dev->pClient, dev->pCmd, fd, -1); - r->_errno = result; - OSUnlockMutex(dev->pMutex); - return -1; - } - file->fd = fd; - file->pos = 0; - file->len = stats.size; - OSUnlockMutex(dev->pMutex); - return (int)file; - } + if (result == 0) { + FSStat stats; + result = FSGetStatFile(dev->pClient, dev->pCmd, fd, &stats, -1); + if (result != 0) { + FSCloseFile(dev->pClient, dev->pCmd, fd, -1); + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } + file->fd = fd; + file->pos = 0; + file->len = stats.size; + OSUnlockMutex(dev->pMutex); + return (int) file; + } - r->_errno = result; - OSUnlockMutex(dev->pMutex); - return -1; + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; } -static int sd_fat_close_r (struct _reent *r, int fd) -{ - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; - if(!file->dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_close_r(struct _reent *r, int fd) { + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fd; + if (!file->dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(file->dev->pMutex); + OSLockMutex(file->dev->pMutex); - int result = FSCloseFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); + int result = FSCloseFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); - OSUnlockMutex(file->dev->pMutex); + OSUnlockMutex(file->dev->pMutex); - if(result < 0) - { - r->_errno = result; - return -1; - } - return 0; + if (result < 0) { + r->_errno = result; + return -1; + } + return 0; } -static off_t sd_fat_seek_r (struct _reent *r, int fd, off_t pos, int dir) -{ - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; - if(!file->dev) { - r->_errno = ENODEV; - return 0; - } +static off_t sd_fat_seek_r(struct _reent *r, int fd, off_t pos, int dir) { + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fd; + if (!file->dev) { + r->_errno = ENODEV; + return 0; + } - OSLockMutex(file->dev->pMutex); + OSLockMutex(file->dev->pMutex); - switch(dir) - { - case SEEK_SET: - file->pos = pos; - break; - case SEEK_CUR: - file->pos += pos; - break; - case SEEK_END: - file->pos = file->len + pos; - break; - default: - r->_errno = EINVAL; - return -1; - } + switch (dir) { + case SEEK_SET: + file->pos = pos; + break; + case SEEK_CUR: + file->pos += pos; + break; + case SEEK_END: + file->pos = file->len + pos; + break; + default: + r->_errno = EINVAL; + return -1; + } - int result = FSSetPosFile(file->dev->pClient, file->dev->pCmd, file->fd, file->pos, -1); + int result = FSSetPosFile(file->dev->pClient, file->dev->pCmd, file->fd, file->pos, -1); - OSUnlockMutex(file->dev->pMutex); + OSUnlockMutex(file->dev->pMutex); - if(result == 0) - { - return file->pos; - } + if (result == 0) { + return file->pos; + } - return result; + return result; } -static ssize_t sd_fat_write_r (struct _reent *r, int fd, const char *ptr, size_t len) -{ - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; - if(!file->dev) { - r->_errno = ENODEV; - return 0; - } +static ssize_t sd_fat_write_r(struct _reent *r, int fd, const char *ptr, size_t len) { + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fd; + if (!file->dev) { + r->_errno = ENODEV; + return 0; + } - if(!file->write) - { - r->_errno = EACCES; - return 0; - } + if (!file->write) { + r->_errno = EACCES; + return 0; + } - OSLockMutex(file->dev->pMutex); + OSLockMutex(file->dev->pMutex); - size_t len_aligned = FS_ALIGN(len); - if(len_aligned > 0x4000) - len_aligned = 0x4000; + size_t len_aligned = FS_ALIGN(len); + if (len_aligned > 0x4000) + len_aligned = 0x4000; - unsigned char *tmpBuf = (unsigned char *)memalign(FS_ALIGNMENT, len_aligned); - if(!tmpBuf) { - r->_errno = ENOMEM; - OSUnlockMutex(file->dev->pMutex); - return 0; - } + unsigned char *tmpBuf = (unsigned char *) memalign(FS_ALIGNMENT, len_aligned); + if (!tmpBuf) { + r->_errno = ENOMEM; + OSUnlockMutex(file->dev->pMutex); + return 0; + } - size_t done = 0; + size_t done = 0; - while(done < len) - { - size_t write_size = (len_aligned < (len - done)) ? len_aligned : (len - done); - memcpy(tmpBuf, ptr + done, write_size); + while (done < len) { + size_t write_size = (len_aligned < (len - done)) ? len_aligned : (len - done); + memcpy(tmpBuf, ptr + done, write_size); - int result = FSWriteFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, write_size, file->fd, 0, -1); - if(result < 0) - { - r->_errno = result; - break; - } - else if(result == 0) - { - if(write_size > 0) - done = 0; - break; - } - else - { - done += result; - file->pos += result; - } - } + int result = FSWriteFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, write_size, file->fd, 0, -1); + if (result < 0) { + r->_errno = result; + break; + } else if (result == 0) { + if (write_size > 0) + done = 0; + break; + } else { + done += result; + file->pos += result; + } + } - free(tmpBuf); - OSUnlockMutex(file->dev->pMutex); - return done; + free(tmpBuf); + OSUnlockMutex(file->dev->pMutex); + return done; } -static ssize_t sd_fat_read_r (struct _reent *r, int fd, char *ptr, size_t len) -{ - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; - if(!file->dev) { - r->_errno = ENODEV; - return 0; - } +static ssize_t sd_fat_read_r(struct _reent *r, int fd, char *ptr, size_t len) { + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fd; + if (!file->dev) { + r->_errno = ENODEV; + return 0; + } - if(!file->read) - { - r->_errno = EACCES; - return 0; - } + if (!file->read) { + r->_errno = EACCES; + return 0; + } - OSLockMutex(file->dev->pMutex); + OSLockMutex(file->dev->pMutex); - size_t len_aligned = FS_ALIGN(len); - if(len_aligned > 0x4000) - len_aligned = 0x4000; + size_t len_aligned = FS_ALIGN(len); + if (len_aligned > 0x4000) + len_aligned = 0x4000; - unsigned char *tmpBuf = (unsigned char *)memalign(FS_ALIGNMENT, len_aligned); - if(!tmpBuf) { - r->_errno = ENOMEM; - OSUnlockMutex(file->dev->pMutex); - return 0; - } + unsigned char *tmpBuf = (unsigned char *) memalign(FS_ALIGNMENT, len_aligned); + if (!tmpBuf) { + r->_errno = ENOMEM; + OSUnlockMutex(file->dev->pMutex); + return 0; + } - size_t done = 0; + size_t done = 0; - while(done < len) - { - size_t read_size = (len_aligned < (len - done)) ? len_aligned : (len - done); + while (done < len) { + size_t read_size = (len_aligned < (len - done)) ? len_aligned : (len - done); - int result = FSReadFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, read_size, file->fd, 0, -1); - if(result < 0) - { - r->_errno = result; - done = 0; - break; - } - else if(result == 0) - { - //! TODO: error on read_size > 0 - break; - } - else - { - memcpy(ptr + done, tmpBuf, read_size); - done += result; - file->pos += result; - } - } + int result = FSReadFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, read_size, file->fd, 0, -1); + if (result < 0) { + r->_errno = result; + done = 0; + break; + } else if (result == 0) { + //! TODO: error on read_size > 0 + break; + } else { + memcpy(ptr + done, tmpBuf, read_size); + done += result; + file->pos += result; + } + } - free(tmpBuf); - OSUnlockMutex(file->dev->pMutex); - return done; + free(tmpBuf); + OSUnlockMutex(file->dev->pMutex); + return done; } -static int sd_fat_fstat_r (struct _reent *r, int fd, struct stat *st) -{ - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; - if(!file->dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_fstat_r(struct _reent *r, int fd, struct stat *st) { + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fd; + if (!file->dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(file->dev->pMutex); + OSLockMutex(file->dev->pMutex); - // Zero out the stat buffer - memset(st, 0, sizeof(struct stat)); + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); - FSStat stats; - int result = FSGetStatFile(file->dev->pClient, file->dev->pCmd, file->fd, &stats, -1); - if(result != 0) { - r->_errno = result; - OSUnlockMutex(file->dev->pMutex); - return -1; - } + FSStat stats; + int result = FSGetStatFile(file->dev->pClient, file->dev->pCmd, file->fd, &stats, -1); + if (result != 0) { + r->_errno = result; + OSUnlockMutex(file->dev->pMutex); + return -1; + } - st->st_mode = S_IFREG; - st->st_size = stats.size; - st->st_blocks = (stats.size + 511) >> 9; - st->st_nlink = 1; + st->st_mode = S_IFREG; + st->st_size = stats.size; + st->st_blocks = (stats.size + 511) >> 9; + st->st_nlink = 1; - // Fill in the generic entry stats - st->st_dev = stats.ent_id; - st->st_uid = stats.owner_id; - st->st_gid = stats.group_id; - st->st_ino = stats.ent_id; - st->st_atime = stats.mtime; - st->st_ctime = stats.ctime; - st->st_mtime = stats.mtime; - OSUnlockMutex(file->dev->pMutex); - return 0; + // Fill in the generic entry stats + st->st_dev = stats.ent_id; + st->st_uid = stats.owner_id; + st->st_gid = stats.group_id; + st->st_ino = stats.ent_id; + st->st_atime = stats.mtime; + st->st_ctime = stats.ctime; + st->st_mtime = stats.mtime; + OSUnlockMutex(file->dev->pMutex); + return 0; } -static int sd_fat_ftruncate_r (struct _reent *r, int fd, off_t len) -{ - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; - if(!file->dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_ftruncate_r(struct _reent *r, int fd, off_t len) { + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fd; + if (!file->dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(file->dev->pMutex); + OSLockMutex(file->dev->pMutex); - int result = FSTruncateFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); + int result = FSTruncateFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); - OSUnlockMutex(file->dev->pMutex); + OSUnlockMutex(file->dev->pMutex); - if(result < 0) { - r->_errno = result; - return -1; - } + if (result < 0) { + r->_errno = result; + return -1; + } - return 0; + return 0; } -static int sd_fat_fsync_r (struct _reent *r, int fd) -{ - sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd; - if(!file->dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_fsync_r(struct _reent *r, int fd) { + sd_fat_file_state_t *file = (sd_fat_file_state_t *) fd; + if (!file->dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(file->dev->pMutex); + OSLockMutex(file->dev->pMutex); - int result = FSFlushFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); + int result = FSFlushFile(file->dev->pClient, file->dev->pCmd, file->fd, -1); - OSUnlockMutex(file->dev->pMutex); + OSUnlockMutex(file->dev->pMutex); - if(result < 0) { - r->_errno = result; - return -1; - } + if (result < 0) { + r->_errno = result; + return -1; + } - return 0; + return 0; } -static int sd_fat_stat_r (struct _reent *r, const char *path, struct stat *st) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(path); - if(!dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_stat_r(struct _reent *r, const char *path, struct stat *st) { + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if (!dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - // Zero out the stat buffer - memset(st, 0, sizeof(struct stat)); + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); - char *real_path = sd_fat_real_path(path, dev); - if(!real_path) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return -1; - } + char *real_path = sd_fat_real_path(path, dev); + if (!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } - FSStat stats; + FSStat stats; - int result = FSGetStat(dev->pClient, dev->pCmd, real_path, &stats, -1); + int result = FSGetStat(dev->pClient, dev->pCmd, real_path, &stats, -1); - free(real_path); + free(real_path); - if(result < 0) { - r->_errno = result; - OSUnlockMutex(dev->pMutex); - return -1; - } + if (result < 0) { + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } - // mark root also as directory - st->st_mode = ((stats.flag & 0x80000000) || (strlen(dev->mount_path) + 1 == strlen(real_path)))? S_IFDIR : S_IFREG; - st->st_nlink = 1; - st->st_size = stats.size; - st->st_blocks = (stats.size + 511) >> 9; - // Fill in the generic entry stats - st->st_dev = stats.ent_id; - st->st_uid = stats.owner_id; - st->st_gid = stats.group_id; - st->st_ino = stats.ent_id; - st->st_atime = stats.mtime; - st->st_ctime = stats.ctime; - st->st_mtime = stats.mtime; + // mark root also as directory + st->st_mode = ((stats.flag & 0x80000000) || (strlen(dev->mount_path) + 1 == strlen(real_path))) ? S_IFDIR : S_IFREG; + st->st_nlink = 1; + st->st_size = stats.size; + st->st_blocks = (stats.size + 511) >> 9; + // Fill in the generic entry stats + st->st_dev = stats.ent_id; + st->st_uid = stats.owner_id; + st->st_gid = stats.group_id; + st->st_ino = stats.ent_id; + st->st_atime = stats.mtime; + st->st_ctime = stats.ctime; + st->st_mtime = stats.mtime; - OSUnlockMutex(dev->pMutex); + OSUnlockMutex(dev->pMutex); - return 0; + return 0; } -static int sd_fat_link_r (struct _reent *r, const char *existing, const char *newLink) -{ - r->_errno = ENOTSUP; - return -1; +static int sd_fat_link_r(struct _reent *r, const char *existing, const char *newLink) { + r->_errno = ENOTSUP; + return -1; } -static int sd_fat_unlink_r (struct _reent *r, const char *name) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(name); - if(!dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_unlink_r(struct _reent *r, const char *name) { + sd_fat_private_t *dev = sd_fat_get_device_data(name); + if (!dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - char *real_path = sd_fat_real_path(name, dev); - if(!real_path) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return -1; - } + char *real_path = sd_fat_real_path(name, dev); + if (!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } - int result = FSRemove(dev->pClient, dev->pCmd, real_path, -1); + int result = FSRemove(dev->pClient, dev->pCmd, real_path, -1); - free(real_path); + free(real_path); - OSUnlockMutex(dev->pMutex); + OSUnlockMutex(dev->pMutex); - if(result < 0) { - r->_errno = result; - return -1; - } + if (result < 0) { + r->_errno = result; + return -1; + } - return 0; + return 0; } -static int sd_fat_chdir_r (struct _reent *r, const char *name) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(name); - if(!dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_chdir_r(struct _reent *r, const char *name) { + sd_fat_private_t *dev = sd_fat_get_device_data(name); + if (!dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - char *real_path = sd_fat_real_path(name, dev); - if(!real_path) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return -1; - } + char *real_path = sd_fat_real_path(name, dev); + if (!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } - int result = FSChangeDir(dev->pClient, dev->pCmd, real_path, -1); + int result = FSChangeDir(dev->pClient, dev->pCmd, real_path, -1); - free(real_path); + free(real_path); - OSUnlockMutex(dev->pMutex); + OSUnlockMutex(dev->pMutex); - if(result < 0) { - r->_errno = result; - return -1; - } + if (result < 0) { + r->_errno = result; + return -1; + } - return 0; + return 0; } -static int sd_fat_rename_r (struct _reent *r, const char *oldName, const char *newName) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(oldName); - if(!dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_rename_r(struct _reent *r, const char *oldName, const char *newName) { + sd_fat_private_t *dev = sd_fat_get_device_data(oldName); + if (!dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - char *real_oldpath = sd_fat_real_path(oldName, dev); - if(!real_oldpath) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return -1; - } - char *real_newpath = sd_fat_real_path(newName, dev); - if(!real_newpath) { - r->_errno = ENOMEM; - free(real_oldpath); - OSUnlockMutex(dev->pMutex); - return -1; - } + char *real_oldpath = sd_fat_real_path(oldName, dev); + if (!real_oldpath) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + char *real_newpath = sd_fat_real_path(newName, dev); + if (!real_newpath) { + r->_errno = ENOMEM; + free(real_oldpath); + OSUnlockMutex(dev->pMutex); + return -1; + } - int result = FSRename(dev->pClient, dev->pCmd, real_oldpath, real_newpath, -1); + int result = FSRename(dev->pClient, dev->pCmd, real_oldpath, real_newpath, -1); - free(real_oldpath); - free(real_newpath); + free(real_oldpath); + free(real_newpath); - OSUnlockMutex(dev->pMutex); + OSUnlockMutex(dev->pMutex); - if(result < 0) { - r->_errno = result; - return -1; - } + if (result < 0) { + r->_errno = result; + return -1; + } - return 0; + return 0; } -static int sd_fat_mkdir_r (struct _reent *r, const char *path, int mode) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(path); - if(!dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_mkdir_r(struct _reent *r, const char *path, int mode) { + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if (!dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - char *real_path = sd_fat_real_path(path, dev); - if(!real_path) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return -1; - } + char *real_path = sd_fat_real_path(path, dev); + if (!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } - int result = FSMakeDir(dev->pClient, dev->pCmd, real_path, -1); + int result = FSMakeDir(dev->pClient, dev->pCmd, real_path, -1); - free(real_path); + free(real_path); - OSUnlockMutex(dev->pMutex); + OSUnlockMutex(dev->pMutex); - if(result < 0) { - r->_errno = result; - return -1; - } + if (result < 0) { + r->_errno = result; + return -1; + } - return 0; + return 0; } -static int sd_fat_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(path); - if(!dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_statvfs_r(struct _reent *r, const char *path, struct statvfs *buf) { + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if (!dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - // Zero out the stat buffer - memset(buf, 0, sizeof(struct statvfs)); + // Zero out the stat buffer + memset(buf, 0, sizeof(struct statvfs)); - char *real_path = sd_fat_real_path(path, dev); - if(!real_path) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return -1; - } + char *real_path = sd_fat_real_path(path, dev); + if (!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } - u64 size; + u64 size; - int result = FSGetFreeSpaceSize(dev->pClient, dev->pCmd, real_path, &size, -1); + int result = FSGetFreeSpaceSize(dev->pClient, dev->pCmd, real_path, &size, -1); - free(real_path); + free(real_path); - if(result < 0) { - r->_errno = result; - OSUnlockMutex(dev->pMutex); - return -1; - } + if (result < 0) { + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } - // File system block size - buf->f_bsize = 512; + // File system block size + buf->f_bsize = 512; - // Fundamental file system block size - buf->f_frsize = 512; + // Fundamental file system block size + buf->f_frsize = 512; - // Total number of blocks on file system in units of f_frsize - buf->f_blocks = size >> 9; // this is unknown + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = size >> 9; // this is unknown - // Free blocks available for all and for non-privileged processes - buf->f_bfree = buf->f_bavail = size >> 9; + // Free blocks available for all and for non-privileged processes + buf->f_bfree = buf->f_bavail = size >> 9; - // Number of inodes at this point in time - buf->f_files = 0xffffffff; + // Number of inodes at this point in time + buf->f_files = 0xffffffff; - // Free inodes available for all and for non-privileged processes - buf->f_ffree = 0xffffffff; + // Free inodes available for all and for non-privileged processes + buf->f_ffree = 0xffffffff; - // File system id - buf->f_fsid = (int)dev; + // File system id + buf->f_fsid = (int) dev; - // Bit mask of f_flag values. - buf->f_flag = 0; + // Bit mask of f_flag values. + buf->f_flag = 0; - // Maximum length of filenames - buf->f_namemax = 255; + // Maximum length of filenames + buf->f_namemax = 255; - OSUnlockMutex(dev->pMutex); + OSUnlockMutex(dev->pMutex); - return 0; + return 0; } -static DIR_ITER *sd_fat_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) -{ - sd_fat_private_t *dev = sd_fat_get_device_data(path); - if(!dev) { - r->_errno = ENODEV; - return NULL; - } +static DIR_ITER *sd_fat_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) { + sd_fat_private_t *dev = sd_fat_get_device_data(path); + if (!dev) { + r->_errno = ENODEV; + return NULL; + } - sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *) dirState->dirStruct; - OSLockMutex(dev->pMutex); + OSLockMutex(dev->pMutex); - char *real_path = sd_fat_real_path(path, dev); - if(!real_path) { - r->_errno = ENOMEM; - OSUnlockMutex(dev->pMutex); - return NULL; - } + char *real_path = sd_fat_real_path(path, dev); + if (!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return NULL; + } - int dirHandle; + int dirHandle; - int result = FSOpenDir(dev->pClient, dev->pCmd, real_path, &dirHandle, -1); + int result = FSOpenDir(dev->pClient, dev->pCmd, real_path, &dirHandle, -1); - free(real_path); + free(real_path); - OSUnlockMutex(dev->pMutex); + OSUnlockMutex(dev->pMutex); - if(result < 0) - { - r->_errno = result; - return NULL; - } + if (result < 0) { + r->_errno = result; + return NULL; + } - dirIter->dev = dev; - dirIter->dirHandle = dirHandle; + dirIter->dev = dev; + dirIter->dirHandle = dirHandle; - return dirState; + return dirState; } -static int sd_fat_dirclose_r (struct _reent *r, DIR_ITER *dirState) -{ - sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; - if(!dirIter->dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_dirclose_r(struct _reent *r, DIR_ITER *dirState) { + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *) dirState->dirStruct; + if (!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dirIter->dev->pMutex); + OSLockMutex(dirIter->dev->pMutex); - int result = FSCloseDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1); + int result = FSCloseDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1); - OSUnlockMutex(dirIter->dev->pMutex); + OSUnlockMutex(dirIter->dev->pMutex); - if(result < 0) - { - r->_errno = result; - return -1; - } - return 0; + if (result < 0) { + r->_errno = result; + return -1; + } + return 0; } -static int sd_fat_dirreset_r (struct _reent *r, DIR_ITER *dirState) -{ - sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; - if(!dirIter->dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_dirreset_r(struct _reent *r, DIR_ITER *dirState) { + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *) dirState->dirStruct; + if (!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dirIter->dev->pMutex); + OSLockMutex(dirIter->dev->pMutex); - int result = FSRewindDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1); + int result = FSRewindDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1); - OSUnlockMutex(dirIter->dev->pMutex); + OSUnlockMutex(dirIter->dev->pMutex); - if(result < 0) - { - r->_errno = result; - return -1; - } - return 0; + if (result < 0) { + r->_errno = result; + return -1; + } + return 0; } -static int sd_fat_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) -{ - sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct; - if(!dirIter->dev) { - r->_errno = ENODEV; - return -1; - } +static int sd_fat_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) { + sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *) dirState->dirStruct; + if (!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } - OSLockMutex(dirIter->dev->pMutex); + OSLockMutex(dirIter->dev->pMutex); - FSDirEntry * dir_entry = malloc(sizeof(FSDirEntry)); + FSDirEntry *dir_entry = malloc(sizeof(FSDirEntry)); - int result = FSReadDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, dir_entry, -1); - if(result < 0) - { - free(dir_entry); - r->_errno = result; - OSUnlockMutex(dirIter->dev->pMutex); - return -1; - } + int result = FSReadDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, dir_entry, -1); + if (result < 0) { + free(dir_entry); + r->_errno = result; + OSUnlockMutex(dirIter->dev->pMutex); + return -1; + } - // Fetch the current entry - strcpy(filename, dir_entry->name); + // Fetch the current entry + strcpy(filename, dir_entry->name); - if(st) - { - memset(st, 0, sizeof(struct stat)); - st->st_mode = (dir_entry->stat.flag & 0x80000000) ? S_IFDIR : S_IFREG; - st->st_nlink = 1; - st->st_size = dir_entry->stat.size; - st->st_blocks = (dir_entry->stat.size + 511) >> 9; - st->st_dev = dir_entry->stat.ent_id; - st->st_uid = dir_entry->stat.owner_id; - st->st_gid = dir_entry->stat.group_id; - st->st_ino = dir_entry->stat.ent_id; - st->st_atime = dir_entry->stat.mtime; - st->st_ctime = dir_entry->stat.ctime; - st->st_mtime = dir_entry->stat.mtime; - } + if (st) { + memset(st, 0, sizeof(struct stat)); + st->st_mode = (dir_entry->stat.flag & 0x80000000) ? S_IFDIR : S_IFREG; + st->st_nlink = 1; + st->st_size = dir_entry->stat.size; + st->st_blocks = (dir_entry->stat.size + 511) >> 9; + st->st_dev = dir_entry->stat.ent_id; + st->st_uid = dir_entry->stat.owner_id; + st->st_gid = dir_entry->stat.group_id; + st->st_ino = dir_entry->stat.ent_id; + st->st_atime = dir_entry->stat.mtime; + st->st_ctime = dir_entry->stat.ctime; + st->st_mtime = dir_entry->stat.mtime; + } - free(dir_entry); - OSUnlockMutex(dirIter->dev->pMutex); - return 0; + free(dir_entry); + OSUnlockMutex(dirIter->dev->pMutex); + return 0; } // NTFS device driver devoptab static const devoptab_t devops_sd_fat = { - NULL, /* Device name */ - sizeof (sd_fat_file_state_t), - sd_fat_open_r, - sd_fat_close_r, - sd_fat_write_r, - sd_fat_read_r, - sd_fat_seek_r, - sd_fat_fstat_r, - sd_fat_stat_r, - sd_fat_link_r, - sd_fat_unlink_r, - sd_fat_chdir_r, - sd_fat_rename_r, - sd_fat_mkdir_r, - sizeof (sd_fat_dir_entry_t), - sd_fat_diropen_r, - sd_fat_dirreset_r, - sd_fat_dirnext_r, - sd_fat_dirclose_r, - sd_fat_statvfs_r, - sd_fat_ftruncate_r, - sd_fat_fsync_r, - NULL, /* sd_fat_chmod_r */ - NULL, /* sd_fat_fchmod_r */ - NULL /* Device data */ + NULL, /* Device name */ + sizeof(sd_fat_file_state_t), + sd_fat_open_r, + sd_fat_close_r, + sd_fat_write_r, + sd_fat_read_r, + sd_fat_seek_r, + sd_fat_fstat_r, + sd_fat_stat_r, + sd_fat_link_r, + sd_fat_unlink_r, + sd_fat_chdir_r, + sd_fat_rename_r, + sd_fat_mkdir_r, + sizeof(sd_fat_dir_entry_t), + sd_fat_diropen_r, + sd_fat_dirreset_r, + sd_fat_dirnext_r, + sd_fat_dirclose_r, + sd_fat_statvfs_r, + sd_fat_ftruncate_r, + sd_fat_fsync_r, + NULL, /* sd_fat_chmod_r */ + NULL, /* sd_fat_fchmod_r */ + NULL /* Device data */ }; -static int sd_fat_add_device (const char *name, const char *mount_path, void *pClient, void *pCmd) -{ - devoptab_t *dev = NULL; - char *devname = NULL; - char *devpath = NULL; - int i; +static int sd_fat_add_device(const char *name, const char *mount_path, void *pClient, void *pCmd) { + devoptab_t *dev = NULL; + char *devname = NULL; + char *devpath = NULL; + int i; - // Sanity check - if (!name) { - errno = EINVAL; - return -1; - } + // Sanity check + if (!name) { + errno = EINVAL; + return -1; + } - // Allocate a devoptab for this device - dev = (devoptab_t *) malloc(sizeof(devoptab_t) + strlen(name) + 1); - if (!dev) { - errno = ENOMEM; - return -1; - } + // Allocate a devoptab for this device + dev = (devoptab_t *) malloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { + errno = ENOMEM; + return -1; + } - // Use the space allocated at the end of the devoptab for storing the device name - devname = (char*)(dev + 1); - strcpy(devname, name); + // Use the space allocated at the end of the devoptab for storing the device name + devname = (char *) (dev + 1); + strcpy(devname, name); - // create private data - sd_fat_private_t *priv = (sd_fat_private_t *)malloc(sizeof(sd_fat_private_t) + strlen(mount_path) + 1); - if(!priv) { - free(dev); - errno = ENOMEM; - return -1; - } + // create private data + sd_fat_private_t *priv = (sd_fat_private_t *) malloc(sizeof(sd_fat_private_t) + strlen(mount_path) + 1); + if (!priv) { + free(dev); + errno = ENOMEM; + return -1; + } - devpath = (char*)(priv+1); - strcpy(devpath, mount_path); + devpath = (char *) (priv + 1); + strcpy(devpath, mount_path); - // setup private data - priv->mount_path = devpath; - priv->pClient = pClient; - priv->pCmd = pCmd; - priv->pMutex = malloc(OS_MUTEX_SIZE); + // setup private data + priv->mount_path = devpath; + priv->pClient = pClient; + priv->pCmd = pCmd; + priv->pMutex = malloc(OS_MUTEX_SIZE); - if(!priv->pMutex) { - free(dev); - free(priv); - errno = ENOMEM; - return -1; - } + if (!priv->pMutex) { + free(dev); + free(priv); + errno = ENOMEM; + return -1; + } - OSInitMutex(priv->pMutex); + OSInitMutex(priv->pMutex); - // Setup the devoptab - memcpy(dev, &devops_sd_fat, sizeof(devoptab_t)); - dev->name = devname; - dev->deviceData = priv; + // Setup the devoptab + memcpy(dev, &devops_sd_fat, sizeof(devoptab_t)); + dev->name = devname; + dev->deviceData = priv; - // Add the device to the devoptab table (if there is a free slot) - for (i = 3; i < STD_MAX; i++) { - if (devoptab_list[i] == devoptab_list[0]) { - devoptab_list[i] = dev; - return 0; - } - } + // Add the device to the devoptab table (if there is a free slot) + for (i = 3; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0]) { + devoptab_list[i] = dev; + return 0; + } + } - // failure, free all memory - free(priv); - free(dev); + // failure, free all memory + free(priv); + free(dev); - // If we reach here then there are no free slots in the devoptab table for this device - errno = EADDRNOTAVAIL; - return -1; + // If we reach here then there are no free slots in the devoptab table for this device + errno = EADDRNOTAVAIL; + return -1; } -static int sd_fat_remove_device (const char *path, void **pClient, void **pCmd, char **mountPath) -{ - const devoptab_t *devoptab = NULL; - char name[128] = {0}; - int i; +static int sd_fat_remove_device(const char *path, void **pClient, void **pCmd, char **mountPath) { + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; - // Get the device name from the path - strncpy(name, path, 127); - strtok(name, ":/"); + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); - // Find and remove the specified device from the devoptab table - // NOTE: We do this manually due to a 'bug' in RemoveDevice - // which ignores names with suffixes and causes names - // like "ntfs" and "ntfs1" to be seen as equals - for (i = 3; i < STD_MAX; i++) { - devoptab = devoptab_list[i]; - if (devoptab && devoptab->name) { - if (strcmp(name, devoptab->name) == 0) { - devoptab_list[i] = devoptab_list[0]; + // Find and remove the specified device from the devoptab table + // NOTE: We do this manually due to a 'bug' in RemoveDevice + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 3; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + devoptab_list[i] = devoptab_list[0]; - if(devoptab->deviceData) - { - sd_fat_private_t *priv = (sd_fat_private_t *)devoptab->deviceData; - *pClient = priv->pClient; - *pCmd = priv->pCmd; - *mountPath = (char*) malloc(strlen(priv->mount_path)+1); - if(*mountPath) - strcpy(*mountPath, priv->mount_path); - if(priv->pMutex) - free(priv->pMutex); - free(devoptab->deviceData); - } + if (devoptab->deviceData) { + sd_fat_private_t *priv = (sd_fat_private_t *) devoptab->deviceData; + *pClient = priv->pClient; + *pCmd = priv->pCmd; + *mountPath = (char *) malloc(strlen(priv->mount_path) + 1); + if (*mountPath) + strcpy(*mountPath, priv->mount_path); + if (priv->pMutex) + free(priv->pMutex); + free(devoptab->deviceData); + } - free((devoptab_t*)devoptab); - return 0; - } - } - } + free((devoptab_t *) devoptab); + return 0; + } + } + } - return -1; + return -1; } -int mount_sd_fat(const char *path) -{ - int result = -1; +int mount_sd_fat(const char *path) { + int result = -1; - // get command and client - void* pClient = malloc(FS_CLIENT_SIZE); - void* pCmd = malloc(FS_CMD_BLOCK_SIZE); + // get command and client + void *pClient = malloc(FS_CLIENT_SIZE); + void *pCmd = malloc(FS_CMD_BLOCK_SIZE); - if(!pClient || !pCmd) { - // just in case free if not 0 - if(pClient) - free(pClient); - if(pCmd) - free(pCmd); - return -2; - } + if (!pClient || !pCmd) { + // just in case free if not 0 + if (pClient) + free(pClient); + if (pCmd) + free(pCmd); + return -2; + } - FSInit(); - FSInitCmdBlock(pCmd); - FSAddClientEx(pClient, 0, -1); + FSInit(); + FSInitCmdBlock(pCmd); + FSAddClientEx(pClient, 0, -1); - char *mountPath = NULL; + char *mountPath = NULL; - if(MountFS(pClient, pCmd, &mountPath) == 0) { - result = sd_fat_add_device(path, mountPath, pClient, pCmd); - free(mountPath); - } + result = MountFS(pClient, pCmd, &mountPath); + if (result == 0) { + log_print("MountFS success\n"); + result = sd_fat_add_device(path, mountPath, pClient, pCmd); + log_printf("sd_fat_add_device result: %i\n", result); + free(mountPath); + } else { + log_printf("MountFS error: %i\n", result); + } - return result; + return result; } -int unmount_sd_fat(const char *path) -{ - void *pClient = 0; - void *pCmd = 0; - char *mountPath = 0; +int unmount_sd_fat(const char *path) { + void *pClient = 0; + void *pCmd = 0; + char *mountPath = 0; - int result = sd_fat_remove_device(path, &pClient, &pCmd, &mountPath); - if(result == 0) - { - UmountFS(pClient, pCmd, mountPath); - FSDelClient(pClient); - free(pClient); - free(pCmd); - free(mountPath); - //FSShutdown(); - } - return result; + int result = sd_fat_remove_device(path, &pClient, &pCmd, &mountPath); + if (result == 0) { + UmountFS(pClient, pCmd, mountPath); + FSDelClient(pClient); + free(pClient); + free(pCmd); + free(mountPath); + //FSShutdown(); + } + return result; } diff --git a/src/tcp_gecko.cpp b/src/tcp_gecko.cpp index a4fd548..0a6a626 100644 --- a/src/tcp_gecko.cpp +++ b/src/tcp_gecko.cpp @@ -23,6 +23,7 @@ #include "patcher/function_patcher_gx2.h" #include "system/raw_assembly_cheats.h" #include "fs/fs_utils.h" +#include "fs/sd_fat_devoptab.h" void *client; void *commandBlock; @@ -1485,27 +1486,56 @@ static int runTCPGeckoServer(int argc, void *argv) { #define TITLE_ID_LENGTH 16 #define CODES_FILE_PATH_SIZE (SD_FILE_PATH_HEADER_LENGTH + TITLE_ID_LENGTH + EXTENSION_SIZE) -void applyCheatCodes() { - unsigned char filePath[CODES_FILE_PATH_SIZE]; - memset(filePath, '0', sizeof(filePath)); - memcpy(filePath, "sd:/codes/", SD_FILE_PATH_HEADER_LENGTH); // File path header - u64 titleID = OSGetTitleID(); - char asciiTitleID[TITLE_ID_LENGTH]; - snprintf(asciiTitleID, TITLE_ID_LENGTH, "%llX", titleID); - memcpy(filePath + SD_FILE_PATH_HEADER_LENGTH + TITLE_ID_LEADING_ZEROS, asciiTitleID, TITLE_ID_LENGTH); // Title ID - memcpy(filePath + SD_FILE_PATH_HEADER_LENGTH + TITLE_ID_LENGTH, ".gctu", EXTENSION_SIZE); // Extension - filePath[CODES_FILE_PATH_SIZE - 1] = '\0'; // Null-terminated +u64 cachedTitleID = 0; - unsigned char *codes = NULL; - unsigned int codesSize = 0; - int result = LoadFileToMem((const char *) filePath, &codes, &codesSize); +void considerApplyingSDCheats() { + u64 currentTitleID = OSGetTitleID(); - if (result < 0) { - // Error, we won't write any codes - return; + if (cachedTitleID == currentTitleID) { + // log_print("Title ID NOT changed\n"); + } else { + log_print("Title ID changed\n"); + cachedTitleID = currentTitleID; + int result = mount_sd_fat("sd"); + + if (result < 0) { + log_printf("Mounting error: %i\n", result); + return; + } + + unsigned char filePath[CODES_FILE_PATH_SIZE]; + memset(filePath, '0', sizeof(filePath)); + memcpy(filePath, "sd:/codes/", SD_FILE_PATH_HEADER_LENGTH); // File path header + log_printf("Title ID: %lu\n", currentTitleID); + char asciiTitleID[TITLE_ID_LENGTH]; + snprintf(asciiTitleID, TITLE_ID_LENGTH, "%llX", currentTitleID); + memcpy(filePath + SD_FILE_PATH_HEADER_LENGTH + TITLE_ID_LEADING_ZEROS, asciiTitleID, + TITLE_ID_LENGTH); // Title ID + memcpy(filePath + SD_FILE_PATH_HEADER_LENGTH + TITLE_ID_LENGTH, ".gctu", EXTENSION_SIZE); // Extension + filePath[CODES_FILE_PATH_SIZE - 1] = '\0'; // Null-terminated + log_printf("File Path: %s\n", filePath); + + unsigned char *codes = NULL; + unsigned int codesSize = 0; + result = LoadFileToMem((const char *) filePath, &codes, &codesSize); + + if (result < 0) { + log_printf("Reading error: %i\n", result); + // Error, we won't write any codes + goto CLEANUP; + } + + kernelCopyData((unsigned char *) 0x01133000, codes, codesSize); + log_print("Copied!\n"); + + CLEANUP: + + result = unmount_sd_fat("sd"); + + if (result < 0) { + log_printf("Unmounting error: %i\n", result); + } } - - kernelCopyData((unsigned char *) 0x01133000, codes, codesSize); } static int startTCPGeckoThread(int argc, void *argv) { @@ -1538,7 +1568,7 @@ static int startTCPGeckoThread(int argc, void *argv) { while (true) { usleep(9000); - applyCheatCodes(); + considerApplyingSDCheats(); // log_print("Running code handler...\n"); codeHandlerFunction(); diff --git a/tcpgecko.elf b/tcpgecko.elf index 7a26bf06036a04dd1c21d817558f7c3f6135f285..62f423919ce27ef8ffd618199aa1878e46b5ee13 100644 GIT binary patch delta 46907 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 delta 34683 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