diff --git a/src/FSDirReplacements.cpp b/src/FSDirReplacements.cpp index 6e2ab74..e2092ba 100644 --- a/src/FSDirReplacements.cpp +++ b/src/FSDirReplacements.cpp @@ -11,8 +11,8 @@ } #define ASYNC_RESULT_HANDLER [client, block, asyncData](FSStatus res) -> FSStatus { \ - DEBUG_FUNCTION_LINE_VERBOSE("Result was %d", res); \ - return send_result_async(client, block, asyncData, res);\ + DEBUG_FUNCTION_LINE_VERBOSE("Result was %d", res); \ + return send_result_async(client, block, asyncData, res); \ } DECL_FUNCTION(FSStatus, FSOpenDir, FSClient *client, FSCmdBlock *block, char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask) { @@ -33,17 +33,24 @@ DECL_FUNCTION(FSStatus, FSOpenDir, FSClient *client, FSCmdBlock *block, char *pa DECL_FUNCTION(FSStatus, FSOpenDirAsync, FSClient *client, FSCmdBlock *block, char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) { DEBUG_FUNCTION_LINE_VERBOSE("%s", path); - FSStatus result = FSOpenDirWrapper(path, handle, errorMask, - [client, block, handle, errorMask, asyncData] - (char *_path) -> FSStatus { - return real_FSOpenDirAsync(client, block, _path, handle, errorMask, asyncData); - }, - ASYNC_RESULT_HANDLER); - if (result != FS_STATUS_USE_REAL_OS) { - return result; + FSErrorFlag realErrorMask = errorMask; + + // Even real_FSOpenDir is still calling our FSOpenDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask. + if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) { + FSStatus result = FSOpenDirWrapper(path, handle, errorMask, + [client, block, handle, errorMask, asyncData] + (char *_path) -> FSStatus { + return real_FSOpenDirAsync(client, block, _path, handle, errorMask, asyncData); + }, + ASYNC_RESULT_HANDLER); + if (result != FS_STATUS_USE_REAL_OS) { + return result; + } + } else { + realErrorMask = FS_ERROR_FLAG_ALL; } - return real_FSOpenDirAsync(client, block, path, handle, errorMask, asyncData); + return real_FSOpenDirAsync(client, block, path, handle, realErrorMask, asyncData); } DECL_FUNCTION(FSStatus, FSReadDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask) { @@ -58,12 +65,18 @@ DECL_FUNCTION(FSStatus, FSReadDir, FSClient *client, FSCmdBlock *block, FSDirect DECL_FUNCTION(FSStatus, FSReadDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask, FSAsyncData *asyncData) { DEBUG_FUNCTION_LINE_VERBOSE(); - FSStatus result = FSReadDirWrapper(handle, entry, errorMask, ASYNC_RESULT_HANDLER); - if (result != FS_STATUS_USE_REAL_OS) { - return result; + FSErrorFlag realErrorMask = errorMask; + // Even real_FSReadDir is still calling our FSReadDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask. + if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) { + FSStatus result = FSReadDirWrapper(handle, entry, errorMask, ASYNC_RESULT_HANDLER); + if (result != FS_STATUS_USE_REAL_OS) { + return result; + } + } else { + realErrorMask = FS_ERROR_FLAG_ALL; } - return real_FSReadDirAsync(client, block, handle, entry, errorMask, asyncData); + return real_FSReadDirAsync(client, block, handle, entry, realErrorMask, asyncData); } DECL_FUNCTION(FSStatus, FSCloseDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) { @@ -78,12 +91,18 @@ DECL_FUNCTION(FSStatus, FSCloseDir, FSClient *client, FSCmdBlock *block, FSDirec DECL_FUNCTION(FSStatus, FSCloseDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) { DEBUG_FUNCTION_LINE_VERBOSE(); - FSStatus result = FSCloseDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER); - if (result != FS_STATUS_USE_REAL_OS) { - return result; + FSErrorFlag realErrorMask = errorMask; + // Even real_FSCloseDir is still calling our FSCloseDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask. + if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) { + FSStatus result = FSCloseDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER); + if (result != FS_STATUS_USE_REAL_OS) { + return result; + } + } else { + realErrorMask = FS_ERROR_FLAG_ALL; } - return real_FSCloseDirAsync(client, block, handle, errorMask, asyncData); + return real_FSCloseDirAsync(client, block, handle, realErrorMask, asyncData); } DECL_FUNCTION(FSStatus, FSRewindDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) { @@ -98,12 +117,18 @@ DECL_FUNCTION(FSStatus, FSRewindDir, FSClient *client, FSCmdBlock *block, FSDire DECL_FUNCTION(FSStatus, FSRewindDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) { DEBUG_FUNCTION_LINE_VERBOSE(); - FSStatus result = FSRewindDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER); - if (result != FS_STATUS_USE_REAL_OS) { - return result; + FSErrorFlag realErrorMask = errorMask; + // Even real_FSRewindDir is still calling our FSRewindDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask. + if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) { + FSStatus result = FSRewindDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER); + if (result != FS_STATUS_USE_REAL_OS) { + return result; + } + } else { + realErrorMask = FS_ERROR_FLAG_ALL; } - return real_FSRewindDirAsync(client, block, handle, errorMask, asyncData); + return real_FSRewindDirAsync(client, block, handle, realErrorMask, asyncData); } DECL_FUNCTION(FSStatus, FSMakeDir, FSClient *client, FSCmdBlock *block, char *path, FSErrorFlag errorMask) { diff --git a/src/FSWrapper.cpp b/src/FSWrapper.cpp index a8025df..5be00b9 100644 --- a/src/FSWrapper.cpp +++ b/src/FSWrapper.cpp @@ -134,6 +134,8 @@ void freeDirHandle(uint32_t handle) { dir_handle_mutex.unlock(); } +extern "C" FSStatus (*real_FSOpenDir)(FSClient *, FSCmdBlock *, char *, FSDirectoryHandle *, FSErrorFlag); + FSStatus FSOpenDirWrapper(char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask, @@ -177,10 +179,30 @@ FSStatus FSOpenDirWrapper(char *path, dir_handles[handle_index].path[0] = '\0'; strncat(dir_handles[handle_index].path, pathForCheck, sizeof(dir_handles[handle_index].path) - 1); - DCFlushRange(&dir_handles[handle_index], sizeof(dirMagic_t)); + if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) { + auto dir_info = &dir_handles[handle_index]; + + dir_info->readResult = nullptr; + dir_info->readResultCapacity = 0; + dir_info->readResultNumberOfEntries = 0; + + dir_info->realDirHandle = 0; + + if (gFSClient && gFSCmd) { + FSDirectoryHandle realHandle = 0; + if (real_FSOpenDir(gFSClient, gFSCmd, path, &realHandle, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS) == FS_STATUS_OK) { + dir_info->realDirHandle = realHandle; + } else { + DEBUG_FUNCTION_LINE_VERBOSE("Failed to open real dir %s", path); + } + } else { + DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null"); + } + DCFlushRange(dir_info, sizeof(dirMagic_t)); + } + OSUnlockMutex(dir_handles[handle_index].mutex); } else { - DEBUG_FUNCTION_LINE("Dir not found %s", pathForCheck); if (gReplacementInfo.contentReplacementInfo.fallbackOnError) { return FS_STATUS_USE_REAL_OS; } @@ -198,6 +220,8 @@ FSStatus FSOpenDirWrapper(char *path, return FS_STATUS_USE_REAL_OS; } +extern "C" FSStatus (*real_FSReadDir)(FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask); + FSStatus FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask, @@ -218,6 +242,19 @@ FSStatus FSReadDirWrapper(FSDirectoryHandle handle, OSLockMutex(dir_handles[handle_index].mutex); DIR *dir = dir_handles[handle_index].dir; + if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) { + auto dir_info = &dir_handles[handle_index]; + + // Init list if needed + if (dir_info->readResultCapacity == 0) { + dir_info->readResult = (FSDirectoryEntry *) malloc(sizeof(FSDirectoryEntry)); + if (dir_info->readResult != nullptr) { + dir_info->readResultCapacity = 1; + } + } + DCFlushRange(dir_info, sizeof(dirMagic_t)); + } + struct dirent *entry_ = readdir(dir); FSStatus result = FS_STATUS_END; if (entry_) { @@ -244,13 +281,73 @@ FSStatus FSReadDirWrapper(FSDirectoryHandle handle, } } } + + if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) { + auto dir_info = &dir_handles[handle_index]; + if (dir_info->readResultNumberOfEntries >= dir_info->readResultCapacity) { + auto newCapacity = dir_info->readResultCapacity * 2; + dir_info->readResult = (FSDirectoryEntry *) realloc(dir_info->readResult, newCapacity * sizeof(FSDirectoryEntry)); + dir_info->readResultCapacity = newCapacity; + if (dir_info->readResult == nullptr) { + OSFatal("Failed to alloc memory for dir entry list"); + } + } + + memcpy(&dir_info->readResult[dir_info->readResultNumberOfEntries], entry, sizeof(FSDirectoryEntry)); + dir_info->readResultNumberOfEntries++; + + DCFlushRange(dir_info->readResult, sizeof(FSDirectoryEntry) * dir_info->readResultNumberOfEntries); + DCFlushRange(dir_info, sizeof(dirMagic_t)); + } + result = FS_STATUS_OK; + } else if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) { + auto dir_info = &dir_handles[handle_index]; + // Read the real directory. + if (dir_info->realDirHandle != 0) { + if (gFSClient && gFSCmd) { + FSDirectoryEntry realDirEntry; + FSStatus readDirResult = FS_STATUS_OK; + result = FS_STATUS_END; + while (readDirResult == FS_STATUS_OK) { + readDirResult = real_FSReadDir(gFSClient, gFSCmd, dir_info->realDirHandle, &realDirEntry, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS); + if (readDirResult == FS_STATUS_OK) { + bool found = false; + for (int i = 0; i < dir_info->readResultNumberOfEntries; i++) { + auto curResult = &dir_info->readResult[i]; + // Check if this is a new result + if (strncmp(curResult->name, realDirEntry.name, sizeof(realDirEntry.name) - 1) == 0) { + found = true; + break; + } + } + // If it's new we can use it :) + if (!found) { + memcpy(entry, &realDirEntry, sizeof(FSDirectoryEntry)); + result = FS_STATUS_OK; + break; + } + } else if (readDirResult == FS_STATUS_END) { + result = FS_STATUS_END; + break; + } else { + DEBUG_FUNCTION_LINE("real_FSReadDir returned an unexpected error: %08X", readDirResult); + result = FS_STATUS_END; + break; + } + } + } else { + DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null"); + } + } } OSUnlockMutex(dir_handles[handle_index].mutex); return result_handler(result); } +extern "C" FSStatus (*real_FSCloseDir)(FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask); + FSStatus FSCloseDirWrapper(FSDirectoryHandle handle, FSErrorFlag errorMask, const std::function &result_handler) { @@ -274,11 +371,38 @@ FSStatus FSCloseDirWrapper(FSDirectoryHandle handle, result = FS_STATUS_MEDIA_ERROR; } + if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) { + auto dir_info = &dir_handles[handle_index]; + + if (dir_info->realDirHandle != 0) { + if (gFSClient && gFSCmd) { + auto realResult = real_FSCloseDir(gFSClient, gFSCmd, dir_info->realDirHandle, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS); + if (realResult == FS_STATUS_OK) { + dir_info->realDirHandle = 0; + } else { + DEBUG_FUNCTION_LINE("Failed to closed dir %d", realResult); + } + } else { + DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null"); + } + } + + if (dir_info->readResult != nullptr) { + free(dir_info->readResult); + dir_info->readResult = nullptr; + dir_info->readResultCapacity = 0; + dir_info->readResultNumberOfEntries = 0; + } + DCFlushRange(dir_info, sizeof(dirMagic_t)); + } + OSUnlockMutex(dir_handles[handle_index].mutex); freeDirHandle(handle_index); return result_handler(result); } +extern "C" FSStatus (*real_FSRewindDir)(FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask); + FSStatus FSRewindDirWrapper(FSDirectoryHandle handle, FSErrorFlag errorMask, const std::function &result_handler) { @@ -297,6 +421,29 @@ FSStatus FSRewindDirWrapper(FSDirectoryHandle handle, DIR *dir = dir_handles[real_handle].dir; rewinddir(dir); + + if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) { + auto dir_info = &dir_handles[handle_index]; + + if (dir_info->readResult != nullptr) { + dir_info->readResultNumberOfEntries = 0; + memset(dir_info->readResult, 0, sizeof(FSDirectoryEntry) * dir_info->readResultCapacity); + } + + if (dir_info->realDirHandle != 0) { + if (gFSClient && gFSCmd) { + if (real_FSRewindDir(gFSClient, gFSCmd, dir_info->realDirHandle, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS) == FS_STATUS_OK) { + dir_info->realDirHandle = 0; + } else { + DEBUG_FUNCTION_LINE("Failed to rewind dir"); + } + } else { + DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null"); + } + } + DCFlushRange(dir_info, sizeof(dirMagic_t)); + } + OSUnlockMutex(dir_handles[handle_index].mutex); return result_handler(FS_STATUS_OK); } diff --git a/src/FSWrapper.h b/src/FSWrapper.h index 3cb1003..e4f12d0 100644 --- a/src/FSWrapper.h +++ b/src/FSWrapper.h @@ -7,11 +7,18 @@ #include typedef struct dirMagic { - uint32_t handle; - DIR *dir; - bool in_use; - char path[256]; - OSMutex *mutex; + uint32_t handle{}; + DIR *dir{}; + bool in_use{}; + char path[256]{}; + + OSMutex *mutex{}; + + FSDirectoryEntry * readResult = nullptr; + int readResultCapacity = 0; + int readResultNumberOfEntries = 0; + + FSDirectoryHandle realDirHandle = 0; } dirMagic_t; typedef struct fileMagic { @@ -21,6 +28,12 @@ typedef struct fileMagic { OSMutex *mutex; } fileMagic_t; + +#define ERROR_FLAG_MASK (0xFFFF0000) +#define FORCE_REAL_FUNC_MAGIC (0x42420000) +#define FORCE_REAL_FUNC_WITH_FULL_ERRORS (FORCE_REAL_FUNC_MAGIC | 0x0000FFFF) + +#define HANDLE_INDICATOR_MASK 0xFFFFFF00 #define HANDLE_INDICATOR_MASK 0xFFFFFF00 #define HANDLE_MASK (0x000000FF) #define DIR_HANDLE_MAGIC 0x30000000 diff --git a/src/globals.cpp b/src/globals.cpp index 7457504..db3dcbc 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -1,3 +1,7 @@ +#include #include "globals.h" -RPXLoader_ReplacementInformation gReplacementInfo __attribute__((section(".data"))); \ No newline at end of file +RPXLoader_ReplacementInformation gReplacementInfo __attribute__((section(".data"))); + +FSClient * gFSClient __attribute__((section(".data"))) = nullptr; +FSCmdBlock * gFSCmd __attribute__((section(".data"))) = nullptr; \ No newline at end of file diff --git a/src/globals.h b/src/globals.h index 5a1c6b1..33a05fa 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,5 +1,6 @@ #include #include +#include typedef struct MetaInformation_t { char shortname[64]; @@ -48,4 +49,6 @@ typedef struct RPXLoader_ReplacementInformation_t { } RPXLoader_ReplacementInformation; -extern RPXLoader_ReplacementInformation gReplacementInfo; \ No newline at end of file +extern RPXLoader_ReplacementInformation gReplacementInfo; +extern FSClient * gFSClient; +extern FSCmdBlock * gFSCmd; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 24194cb..11e33f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,11 @@ WUMS_APPLICATION_ENDS() { } } gReplacementInfo.rpxReplacementInfo.isRPXReplaced = false; + if (gFSClient) { + FSDelClient(gFSClient, FS_ERROR_FLAG_ALL); + free(gFSClient); + } + free(gFSCmd); } WUMS_APPLICATION_STARTS() { @@ -59,6 +64,23 @@ WUMS_APPLICATION_STARTS() { } WHBLogUdpInit(); if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) { + auto fsClient = (FSClient *) memalign(0x20, sizeof(FSClient)); + auto fsCmd = (FSCmdBlock *) memalign(0x20, sizeof(FSCmdBlock)); + + if (fsClient == nullptr || fsCmd == nullptr) { + DEBUG_FUNCTION_LINE("Failed to alloc memory for fsclient or fsCmd"); + free(fsClient); + free(fsCmd); + } else { + auto rc = FSAddClient(fsClient, FS_ERROR_FLAG_ALL); + if (rc < 0) { + DEBUG_FUNCTION_LINE("Failed to add FSClient"); + } else { + FSInitCmdBlock(fsCmd); + gFSClient = fsClient; + gFSCmd = fsCmd; + } + } return; }