diff --git a/Makefile b/Makefile index c21d96f..9689a1a 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g endif -LIBS := -lwums -lwut -lfunctionpatcher +LIBS := -lwums -lwut -lfunctionpatcher -lmocha #------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level diff --git a/src/FSAReplacements.cpp b/src/FSAReplacements.cpp index f095178..e84cc11 100644 --- a/src/FSAReplacements.cpp +++ b/src/FSAReplacements.cpp @@ -7,20 +7,29 @@ static FSError processFSAShimInThread(FSAShimBuffer *shimBuffer) { FSError res; - if (gThreadsRunning) { + + auto upid = OSGetUPID(); + if (!sLayerInfoForUPID.contains(upid)) { + DEBUG_FUNCTION_LINE_ERR("invalid UPID %d", upid); + OSFatal("Tried to start threads for invalid UPID."); + } + + auto & layerInfo = sLayerInfoForUPID[upid]; + if (layerInfo->threadsRunning) { auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper)); if (param == nullptr) { DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for FSShimWrapper"); OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper"); } + param->upid = OSGetUPID(); param->api = FS_SHIM_API_FSA; param->sync = FS_SHIM_TYPE_SYNC; param->shim = shimBuffer; - if (OSGetCurrentThread() == gThreadData[OSGetCoreId()].thread) { + if (OSGetCurrentThread() == layerInfo->threadData[OSGetCoreId()].thread) { res = processShimBufferForFSA(param); - //No need to clean "param", it has been already free'd in processFSAShimBuffer. + // No need to clean "param", it has been already free'd in processFSAShimBuffer. } else { auto message = (FSShimWrapperMessage *) malloc(sizeof(FSShimWrapperMessage)); if (message == nullptr) { @@ -31,7 +40,7 @@ static FSError processFSAShimInThread(FSAShimBuffer *shimBuffer) { constexpr int32_t messageSize = sizeof(message->messages) / sizeof(message->messages[0]); OSInitMessageQueue(&message->messageQueue, message->messages, messageSize); - if (!sendMessageToThread(message)) { + if (!sendMessageToThread(layerInfo, message)) { DEBUG_FUNCTION_LINE_ERR("Failed to send message to thread"); OSFatal("ContentRedirectionModule: Failed send message to thread"); } diff --git a/src/FSReplacements.cpp b/src/FSReplacements.cpp index 7a6afdf..d0e0ad7 100644 --- a/src/FSReplacements.cpp +++ b/src/FSReplacements.cpp @@ -2,8 +2,10 @@ #include "FileUtils.h" #include "utils/StringTools.h" #include "utils/logger.h" +#include #include #include +#include FSStatus processFSError(FSError fsError, FSClient *client, FSErrorFlag errorMask) { auto result = fsError >= 0 ? static_cast(fsError) : fsaDecodeFsaStatusToFsStatus(fsError); @@ -89,7 +91,15 @@ void handleAsyncRequestsCallback(IOSError err, void *context) { bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBlock *block, FSErrorFlag errorMask, FSAsyncData *asyncData) { bool res = false; - if (gThreadsRunning) { + + auto upid = OSGetUPID(); + if (!sLayerInfoForUPID.contains(upid)) { + DEBUG_FUNCTION_LINE_ERR("invalid UPID %d", upid); + OSFatal("Tried to start threads for invalid UPID."); + } + + auto & layerInfo = sLayerInfoForUPID[upid]; + if (layerInfo->threadsRunning) { // we **don't** need to free this in this function. auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper)); if (param == nullptr) { @@ -97,6 +107,7 @@ bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBl OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper"); } + param->upid = OSGetUPID(); param->api = FS_SHIM_API_FS; param->sync = FS_SHIM_TYPE_ASYNC; param->shim = shimBuffer; @@ -111,7 +122,7 @@ bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBl // Copy by value param->asyncFS.errorMask = errorMask; - if (OSGetCurrentThread() == gThreadData[OSGetCoreId()].thread) { + if (OSGetCurrentThread() == layerInfo->threadData[OSGetCoreId()].thread) { processShimBufferForFS(param); // because we're doing this in sync, free(param) has already been called at this point. res = true; @@ -122,7 +133,7 @@ bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBl OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapperMessage"); } message->param = param; - res = sendMessageToThread(message); + res = sendMessageToThread(layerInfo, message); // the other thread is call free for us, so we can return early! } } else { @@ -217,7 +228,6 @@ DECL_FUNCTION(FSStatus, FSOpenFileExAsync, FSClient *client, FSCmdBlock *block, auto *hackyBuffer = (uint32_t *) &shimBuffer->response; hackyBuffer[1] = (uint32_t) handle; if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) { - return FS_STATUS_OK; } } @@ -555,28 +565,74 @@ DECL_FUNCTION(FSStatus, FSChangeDirAsync, FSClient *client, FSCmdBlock *block, c return real_FSChangeDirAsync(client, block, path, errorMask, asyncData); } -function_replacement_data_t fs_file_function_replacements[] = { - REPLACE_FUNCTION(FSOpenFileExAsync, LIBRARY_COREINIT, FSOpenFileExAsync), - REPLACE_FUNCTION(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync), - REPLACE_FUNCTION(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync), - REPLACE_FUNCTION(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync), - REPLACE_FUNCTION_VIA_ADDRESS(FSReadFileGeneric, 0x3201C400 + 0x4ecc0, 0x101C400 + 0x4ecc0), - REPLACE_FUNCTION(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync), - REPLACE_FUNCTION(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync), - REPLACE_FUNCTION(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync), - REPLACE_FUNCTION_VIA_ADDRESS(FSWriteFileGeneric, 0x3201C400 + 0x4eec0, 0x101C400 + 0x4eec0), - REPLACE_FUNCTION(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync), - REPLACE_FUNCTION(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync), - REPLACE_FUNCTION(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync), - REPLACE_FUNCTION(FSFlushFileAsync, LIBRARY_COREINIT, FSFlushFileAsync), - REPLACE_FUNCTION(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync), +DECL_FUNCTION(void, __PPCExit, uint32_t u1) { + auto UPID = OSGetUPID(); + if (UPID != 2 && UPID != 15 && sLayerInfoForUPID.contains(UPID)) { + DEBUG_FUNCTION_LINE_ERR("Clear layer for UPID %d", UPID); + clearFSLayer(sLayerInfoForUPID[UPID]); + stopFSIOThreads(); + } + real___PPCExit(u1); +} - REPLACE_FUNCTION(FSOpenDirAsync, LIBRARY_COREINIT, FSOpenDirAsync), - REPLACE_FUNCTION(FSReadDirAsync, LIBRARY_COREINIT, FSReadDirAsync), - REPLACE_FUNCTION(FSCloseDirAsync, LIBRARY_COREINIT, FSCloseDirAsync), - REPLACE_FUNCTION(FSRewindDirAsync, LIBRARY_COREINIT, FSRewindDirAsync), - REPLACE_FUNCTION(FSMakeDirAsync, LIBRARY_COREINIT, FSMakeDirAsync), - REPLACE_FUNCTION(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync), +MochaUtilsStatus MountWrapper(const char *mount, const char *dev, const char *mountTo) { + auto res = Mocha_MountFS(mount, dev, mountTo); + if (res == MOCHA_RESULT_ALREADY_EXISTS) { + res = Mocha_MountFS(mount, nullptr, mountTo); + } + + if (res == MOCHA_RESULT_SUCCESS) { + std::string mountPath = std::string(mount) + ":/"; + DEBUG_FUNCTION_LINE_VERBOSE("Mounted %s", mountPath.c_str()); + } else { + DEBUG_FUNCTION_LINE_ERR("Failed to mount %s: %s [%d]", mount, Mocha_GetStatusStr(res), res); + } + return res; +} + +extern ContentRedirectionApiErrorType CRAddFSLayerEx(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType, uint32_t upid); +DECL_FUNCTION(void, START_HOOK) { + real_START_HOOK(); + auto UPID = OSGetUPID(); + if (UPID != 2 && UPID != 15 && sLayerInfoForUPID.contains(UPID)) { + DEBUG_FUNCTION_LINE_ERR("Clear layer for UPID %d", UPID); + clearFSLayer(sLayerInfoForUPID[UPID]); + DEBUG_FUNCTION_LINE_ERR("Start threads"); + startFSIOThreads(); + } + if (UPID == 8) { + CRLayerHandle handle; + int test = (int) 2; + + Mocha_MountFS("storage_mlc", nullptr, "/vol/storage_mlc01"); + CRAddFSLayerEx(&handle, "browser_test", "storage_mlc:/usr/tmp", FS_LAYER_TYPE_CONTENT_MERGE, UPID); + } +} + +function_replacement_data_t fs_file_function_replacements[] = { + REPLACE_FUNCTION_FOR_PROCESS(__PPCExit, LIBRARY_COREINIT, __PPCExit, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSOpenFileExAsync, LIBRARY_COREINIT, FSOpenFileExAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(FSReadFileGeneric, 0x3201C400 + 0x4ecc0, 0x101C400 + 0x4ecc0, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(FSWriteFileGeneric, 0x3201C400 + 0x4eec0, 0x101C400 + 0x4eec0, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(START_HOOK, 0x3201C400 + 0x3edc0, 0x101C400 + 0x3edc0, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSFlushFileAsync, LIBRARY_COREINIT, FSFlushFileAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync, FP_TARGET_PROCESS_ALL), + + REPLACE_FUNCTION_FOR_PROCESS(FSOpenDirAsync, LIBRARY_COREINIT, FSOpenDirAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSReadDirAsync, LIBRARY_COREINIT, FSReadDirAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSCloseDirAsync, LIBRARY_COREINIT, FSCloseDirAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSRewindDirAsync, LIBRARY_COREINIT, FSRewindDirAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSMakeDirAsync, LIBRARY_COREINIT, FSMakeDirAsync, FP_TARGET_PROCESS_ALL), + REPLACE_FUNCTION_FOR_PROCESS(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync, FP_TARGET_PROCESS_ALL), }; uint32_t fs_file_function_replacements_size = sizeof(fs_file_function_replacements) / sizeof(function_replacement_data_t); diff --git a/src/FSWrapperMergeDirsWithParent.cpp b/src/FSWrapperMergeDirsWithParent.cpp index b781956..291a84c 100644 --- a/src/FSWrapperMergeDirsWithParent.cpp +++ b/src/FSWrapperMergeDirsWithParent.cpp @@ -239,7 +239,7 @@ FSWrapperMergeDirsWithParent::~FSWrapperMergeDirsWithParent() { if (clientHandle) { FSError res; if ((res = FSADelClient(clientHandle)) != FS_ERROR_OK) { - DEBUG_FUNCTION_LINE_ERR("[%s] FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res); + DEBUG_FUNCTION_LINE_ERR("FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res); } clientHandle = 0; } diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index 1165536..5592845 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -11,23 +11,17 @@ #include #include -std::mutex workingDirMutex; -std::map workingDirs; - -std::mutex fsLayerMutex; -std::vector> fsLayers; - -std::string getFullPathGeneric(FSAClientHandle client, const char *path, std::mutex &mutex, std::map &map) { - std::lock_guard workingDirLock(mutex); +std::string getFullPathGeneric(std::shared_ptr &layerInfo, FSAClientHandle client, const char *path) { + std::lock_guard workingDirLock(layerInfo->mutex); std::string res; if (path[0] != '/' && path[0] != '\\') { - if (map.count(client) == 0) { + if (layerInfo->workingDirs.count(client) == 0) { DEBUG_FUNCTION_LINE_WARN("No working dir found for client %08X, fallback to \"/\"", client); - workingDirs[client] = "/"; + layerInfo->workingDirs[client] = "/"; } - res = string_format("%s%s", map.at(client).c_str(), path); + res = string_format("%s%s", layerInfo->workingDirs.at(client).c_str(), path); } else { res = path; } @@ -54,27 +48,33 @@ void setWorkingDirGeneric(FSAClientHandle client, const char *path, std::mutex & } -std::string getFullPath(FSAClientHandle pClient, const char *path) { - return getFullPathGeneric(pClient, path, workingDirMutex, workingDirs); +std::string getFullPath(std::shared_ptr &layerInfo, FSAClientHandle pClient, const char *path) { + return getFullPathGeneric(layerInfo, pClient, path); } -void setWorkingDir(FSAClientHandle client, const char *path) { - setWorkingDirGeneric(client, path, workingDirMutex, workingDirs); +void setWorkingDir(std::shared_ptr &layerInfo, FSAClientHandle client, const char *path) { + setWorkingDirGeneric(client, path, layerInfo->workingDirMutex, layerInfo->workingDirs); } -void clearFSLayer() { +void clearFSLayer(std::shared_ptr &layerInfo) { { - std::lock_guard workingDirLock(workingDirMutex); - workingDirs.clear(); + std::lock_guard workingDirLock(layerInfo->workingDirMutex); + layerInfo->workingDirs.clear(); } { - std::lock_guard layerLock(fsLayerMutex); - fsLayers.clear(); + std::lock_guard layerLock(layerInfo->mutex); + layerInfo->layers.clear(); } } -bool sendMessageToThread(FSShimWrapperMessage *param) { - auto *curThread = &gThreadData[OSGetCoreId()]; +void clearFSLayers() { + for (auto &[upid, layerInfo] : sLayerInfoForUPID) { + clearFSLayer(layerInfo); + } +} + +bool sendMessageToThread(std::shared_ptr &layerInfo, FSShimWrapperMessage *param) { + auto *curThread = &layerInfo->threadData[OSGetCoreId()]; if (curThread->setup) { OSMessage send; send.message = param; @@ -92,12 +92,20 @@ bool sendMessageToThread(FSShimWrapperMessage *param) { return false; } +std::map> sLayerInfoForUPID; + FSError doForLayer(FSShimWrapper *param) { - std::lock_guard lock(fsLayerMutex); - if (!fsLayers.empty()) { - uint32_t startIndex = fsLayers.size(); - for (uint32_t i = fsLayers.size(); i > 0; i--) { - if ((uint32_t) fsLayers[i - 1]->getLayerId() == param->shim->clientHandle) { + if (!sLayerInfoForUPID.contains(param->upid)) { + DEBUG_FUNCTION_LINE_ERR("INVALID UPID IN SHIMWRAPPER: %d", param->upid); + OSFatal("Invalid UPID"); + } + auto &layerInfo = sLayerInfoForUPID[param->upid]; + + std::lock_guard lock(layerInfo->mutex); + if (!layerInfo->layers.empty()) { + uint32_t startIndex = layerInfo->layers.size(); + for (uint32_t i = layerInfo->layers.size(); i > 0; i--) { + if ((uint32_t) layerInfo->layers[i - 1]->getLayerId() == param->shim->clientHandle) { startIndex = i - 1; break; } @@ -105,7 +113,7 @@ FSError doForLayer(FSShimWrapper *param) { if (startIndex > 0) { for (uint32_t i = startIndex; i > 0; i--) { - auto &layer = fsLayers[i - 1]; + auto &layer = layerInfo->layers[i - 1]; if (!layer->isActive()) { continue; } @@ -116,7 +124,7 @@ FSError doForLayer(FSShimWrapper *param) { switch (command) { case FSA_COMMAND_OPEN_DIR: { auto *request = ¶m->shim->request.openDir; - auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); + auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path); DEBUG_FUNCTION_LINE_VERBOSE("[%s] OpenDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); // Hacky solution: auto *hackyBuffer = (uint32_t *) ¶m->shim->response; @@ -154,14 +162,14 @@ FSError doForLayer(FSShimWrapper *param) { } case FSA_COMMAND_MAKE_DIR: { auto *request = ¶m->shim->request.makeDir; - auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); + auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path); DEBUG_FUNCTION_LINE_VERBOSE("[%s] MakeDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); layerResult = layer->FSMakeDirWrapper(fullPath.c_str()); break; } case FSA_COMMAND_OPEN_FILE: { auto *request = ¶m->shim->request.openFile; - auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); + auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path); // Hacky solution: auto *hackyBuffer = (uint32_t *) ¶m->shim->response; auto *handlePtr = (FSFileHandle *) hackyBuffer[1]; @@ -185,7 +193,7 @@ FSError doForLayer(FSShimWrapper *param) { case FSA_COMMAND_GET_INFO_BY_QUERY: { auto *request = ¶m->shim->request.getInfoByQuery; if (request->type == FSA_QUERY_INFO_STAT) { - auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); + auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path); DEBUG_FUNCTION_LINE_VERBOSE("[%s] GetStat: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); // Hacky solution: auto *hackyBuffer = (uint32_t *) ¶m->shim->response; @@ -258,15 +266,15 @@ FSError doForLayer(FSShimWrapper *param) { } case FSA_COMMAND_REMOVE: { auto *request = ¶m->shim->request.remove; - auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); + auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path); DEBUG_FUNCTION_LINE_VERBOSE("[%s] Remove: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); layerResult = layer->FSRemoveWrapper(fullPath.c_str()); break; } case FSA_COMMAND_RENAME: { auto *request = ¶m->shim->request.rename; - auto fullOldPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->oldPath); - auto fullNewPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->newPath); + auto fullOldPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->oldPath); + auto fullNewPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->newPath); DEBUG_FUNCTION_LINE_VERBOSE("[%s] Rename: %s -> %s (full path: %s -> %s)", layer->getName().c_str(), request->oldPath, request->newPath, fullOldPath.c_str(), fullNewPath.c_str()); layerResult = layer->FSRenameWrapper(fullOldPath.c_str(), fullNewPath.c_str()); break; @@ -280,7 +288,7 @@ FSError doForLayer(FSShimWrapper *param) { case FSA_COMMAND_CHANGE_DIR: { auto *request = ¶m->shim->request.changeDir; DEBUG_FUNCTION_LINE_VERBOSE("[%s] ChangeDir: %s", layer->getName().c_str(), request->path); - setWorkingDir((FSAClientHandle) param->shim->clientHandle, request->path); + setWorkingDir(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path); // We still want to call the original function. layerResult = FS_ERROR_FORCE_PARENT_LAYER; break; @@ -458,9 +466,6 @@ int64_t writeFromBuffer(int32_t handle, const void *buffer, size_t size, size_t return totalSize; } -FSIOThreadData gThreadData[3]; -bool gThreadsRunning = false; - static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv) { auto *magic = ((FSIOThreadData *) argv); @@ -512,10 +517,24 @@ static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv) void startFSIOThreads() { int32_t threadAttributes[] = {OS_THREAD_ATTRIB_AFFINITY_CPU0, OS_THREAD_ATTRIB_AFFINITY_CPU1, OS_THREAD_ATTRIB_AFFINITY_CPU2}; auto stackSize = 16 * 1024; + auto upid = OSGetUPID(); + if (!sLayerInfoForUPID.contains(upid)) { + DEBUG_FUNCTION_LINE_ERR("Tried to start threads for invalid UPID %d", upid); + OSFatal("Tried to start threads for invalid UPID."); + } + + auto &layerInfo = sLayerInfoForUPID[upid]; + if (layerInfo->threadsRunning) { + return; + } int coreId = 0; for (int core : threadAttributes) { - auto *threadData = &gThreadData[coreId]; + if (upid != 2 && upid != 15 && core == OS_THREAD_ATTRIB_AFFINITY_CPU2) { + DEBUG_FUNCTION_LINE_ERR("Skip core 2 for non-game UPID %d", upid); + continue; + } + auto *threadData = &layerInfo->threadData[coreId]; memset(threadData, 0, sizeof(*threadData)); threadData->setup = false; threadData->thread = (OSThread *) memalign(8, sizeof(OSThread)); @@ -549,16 +568,24 @@ void startFSIOThreads() { coreId++; } - gThreadsRunning = true; + layerInfo->threadsRunning = true; OSMemoryBarrier(); } void stopFSIOThreads() { - if (!gThreadsRunning) { + auto upid = OSGetUPID(); + if (!sLayerInfoForUPID.contains(upid)) { + DEBUG_FUNCTION_LINE_ERR("Tried to start threads for invalid UPID %d", upid); + OSFatal("Tried to start threads for invalid UPID."); + } + + auto &layerInfo = sLayerInfoForUPID[upid]; + if (!layerInfo->threadsRunning) { return; } - for (auto &gThread : gThreadData) { - auto *thread = &gThread; + + for (auto &curThread : layerInfo->threadData) { + auto *thread = &curThread; if (!thread->setup) { continue; } @@ -581,5 +608,5 @@ void stopFSIOThreads() { } } - gThreadsRunning = false; + layerInfo->threadsRunning = false; } \ No newline at end of file diff --git a/src/FileUtils.h b/src/FileUtils.h index dc3ead2..6f443a1 100644 --- a/src/FileUtils.h +++ b/src/FileUtils.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ struct FSShimWrapper { AsyncParamFS asyncFS; FSShimSyncType sync; FSShimApiType api; + uint32_t upid; }; struct FSShimWrapperMessage { @@ -54,10 +56,16 @@ struct FSShimWrapperMessage { #define FS_IO_QUEUE_COMMAND_PROCESS_FS_COMMAND 0x42424242 #define FS_IO_QUEUE_SYNC_RESULT 0x43434343 -extern bool gThreadsRunning; -extern FSIOThreadData gThreadData[3]; -extern std::mutex fsLayerMutex; -extern std::vector> fsLayers; +struct LayerInfo { + std::mutex mutex{}; + std::vector> layers{}; + std::mutex workingDirMutex{}; + std::map workingDirs{}; + FSIOThreadData threadData[3]{}; + bool threadsRunning{}; +}; + +extern std::map> sLayerInfoForUPID; #define fsaShimPrepareRequestReadFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, uint8_t * buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAReadFlag readFlags))(0x101C400 + 0x436cc)) #define fsaShimPrepareRequestWriteFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAWriteFlag writeFlags))(0x101C400 + 0x437f4)) @@ -87,9 +95,11 @@ extern std::vector> fsLayers; extern "C" FSError __FSAShimDecodeIosErrorToFsaStatus(IOSHandle handle, IOSError err); -bool sendMessageToThread(FSShimWrapperMessage *param); +bool sendMessageToThread(std::shared_ptr &layerInfo, FSShimWrapperMessage *param); -void clearFSLayer(); +void clearFSLayer(std::shared_ptr &layerInfo); + +void clearFSLayers(); FSError doForLayer(FSShimWrapper *param); diff --git a/src/export.cpp b/src/export.cpp index 5e60770..4c206cd 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -16,12 +16,12 @@ struct AOCTitle { WUT_CHECK_SIZE(AOCTitle, 0x68); bool getAOCPath(std::string &outStr) { - int32_t (*AOC_Initialize)() = nullptr; - int32_t (*AOC_Finalize)() = nullptr; - int32_t (*AOC_ListTitle)(uint32_t * titleCountOut, AOCTitle * titleList, uint32_t maxCount, void *workBuffer, uint32_t workBufferSize) = nullptr; - int32_t (*AOC_OpenTitle)(char *pathOut, AOCTitle *aocTitleInfo, void *workBuffer, uint32_t workBufferSize) = nullptr; - int32_t (*AOC_CalculateWorkBufferSize)(uint32_t count) = nullptr; - int32_t (*AOC_CloseTitle)(AOCTitle * aocTitleInfo) = nullptr; + int32_t (*AOC_Initialize)() = nullptr; + int32_t (*AOC_Finalize)() = nullptr; + int32_t (*AOC_ListTitle)(uint32_t *titleCountOut, AOCTitle *titleList, uint32_t maxCount, void *workBuffer, uint32_t workBufferSize) = nullptr; + int32_t (*AOC_OpenTitle)(char *pathOut, AOCTitle *aocTitleInfo, void *workBuffer, uint32_t workBufferSize) = nullptr; + int32_t (*AOC_CalculateWorkBufferSize)(uint32_t count) = nullptr; + int32_t (*AOC_CloseTitle)(AOCTitle *aocTitleInfo) = nullptr; AOCTitle title{}; char aocPath[256]; @@ -88,7 +88,7 @@ end: return result; } -ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType) { +ContentRedirectionApiErrorType CRAddFSLayerEx(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType, uint32_t upid) { if (!handle || layerName == nullptr || replacementDir == nullptr) { DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG"); return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; @@ -121,28 +121,23 @@ ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *l } if (ptr) { DEBUG_FUNCTION_LINE_VERBOSE("Added new layer (%s). Replacement dir: %s Type:%d", layerName, replacementDir, layerType); - std::lock_guard lock(fsLayerMutex); + auto &layerInfo = sLayerInfoForUPID[upid]; + std::lock_guard lock(layerInfo->mutex); *handle = (CRLayerHandle) ptr->getHandle(); - fsLayers.push_back(std::move(ptr)); + layerInfo->layers.push_back(std::move(ptr)); return CONTENT_REDIRECTION_API_ERROR_NONE; } DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory"); return CONTENT_REDIRECTION_API_ERROR_NO_MEMORY; } -ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) { - if (!remove_locked_first_if(fsLayerMutex, fsLayers, [handle](auto &cur) { return (CRLayerHandle) cur->getHandle() == handle; })) { - DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle); - return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND; - } - return CONTENT_REDIRECTION_API_ERROR_NONE; +ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType) { + return CRAddFSLayerEx(handle, layerName, replacementDir, layerType, 2); } -ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) { - std::lock_guard lock(fsLayerMutex); - for (auto &cur : fsLayers) { - if ((CRLayerHandle) cur->getHandle() == handle) { - cur->setActive(active); +ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) { + for (auto &[key, layerInfo] : sLayerInfoForUPID) { + if (remove_locked_first_if(layerInfo->mutex, layerInfo->layers, [handle](auto &cur) { return (CRLayerHandle) cur->getHandle() == handle; })) { return CONTENT_REDIRECTION_API_ERROR_NONE; } } @@ -151,11 +146,27 @@ ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) { return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND; } + +ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) { + for (auto &[key, layerInfo] : sLayerInfoForUPID) { + std::lock_guard lock(layerInfo->mutex); + for (auto &cur : layerInfo->layers) { + if ((CRLayerHandle) cur->getHandle() == handle) { + cur->setActive(active); + return CONTENT_REDIRECTION_API_ERROR_NONE; + } + } + } + + DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle); + return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND; +} + ContentRedirectionApiErrorType CRGetVersion(ContentRedirectionVersion *outVersion) { if (outVersion == nullptr) { return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; } - *outVersion = 1; + *outVersion = 2; return CONTENT_REDIRECTION_API_ERROR_NONE; } @@ -169,6 +180,7 @@ int CRRemoveDevice(const char *name) { WUMS_EXPORT_FUNCTION(CRGetVersion); WUMS_EXPORT_FUNCTION(CRAddFSLayer); +WUMS_EXPORT_FUNCTION(CRAddFSLayerEx); WUMS_EXPORT_FUNCTION(CRRemoveFSLayer); WUMS_EXPORT_FUNCTION(CRSetActive); WUMS_EXPORT_FUNCTION(CRAddDevice); diff --git a/src/main.cpp b/src/main.cpp index 86d2776..d3e2623 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,9 @@ #include "FileUtils.h" #include "utils/StringTools.h" #include "utils/logger.h" +#include "utils/utils.h" #include "version.h" +#include #include WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection"); @@ -13,7 +15,14 @@ WUMS_DEPENDS_ON(homebrew_functionpatcher); #define VERSION "v0.2.5" DECL_FUNCTION(void, OSCancelThread, OSThread *thread) { - if (thread == gThreadData[0].thread || thread == gThreadData[1].thread || thread == gThreadData[2].thread) { + auto upid = OSGetUPID(); + if (!sLayerInfoForUPID.contains(upid)) { + DEBUG_FUNCTION_LINE_ERR("invalid UPID %d", upid); + OSFatal("Invalid UPID."); + } + + auto &layerInfo = sLayerInfoForUPID[upid]; + if (thread == layerInfo->threadData[0].thread || thread == layerInfo->threadData[1].thread || thread == layerInfo->threadData[2].thread) { DEBUG_FUNCTION_LINE_INFO("Prevent calling OSCancelThread for ContentRedirection IO Threads"); return; } @@ -29,6 +38,11 @@ WUMS_INITIALIZE() { OSFatal("homebrew_content_redirection: FunctionPatcher_InitLibrary failed"); } + int mochaInitResult; + if ((mochaInitResult = Mocha_InitLibrary()) != MOCHA_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Mocha_InitLibrary() failed %d", mochaInitResult); + } + bool wasPatched; for (uint32_t i = 0; i < fs_file_function_replacements_size; i++) { wasPatched = false; @@ -46,6 +60,19 @@ WUMS_INITIALIZE() { if (FunctionPatcher_AddFunctionPatch(&OSCancelThreadReplacement, nullptr, &wasPatched) != FUNCTION_PATCHER_RESULT_SUCCESS || !wasPatched) { OSFatal("homebrew_content_redirection: Failed to patch OSCancelThreadReplacement"); } + + // Give UPID 2 (Wii U Menu) and UPID 15 the same layer + auto layerInfoGameMenu = make_shared_nothrow(); + sLayerInfoForUPID[2] = layerInfoGameMenu; + sLayerInfoForUPID[15] = layerInfoGameMenu; + + // Fill in for all other UPIDs + for (int i = 0; i < 16; i++) { + if (i == 2 || i == 15) { + continue; + } + sLayerInfoForUPID[i] = make_shared_nothrow(); + } DEBUG_FUNCTION_LINE("Patch functions finished"); deinitLogging(); } @@ -57,7 +84,10 @@ WUMS_APPLICATION_STARTS() { } WUMS_APPLICATION_ENDS() { - clearFSLayer(); + if (sLayerInfoForUPID.contains(2)) { + DEBUG_FUNCTION_LINE_ERR("Clear layer for UPID %d", 2); + clearFSLayer(sLayerInfoForUPID[2]); + } stopFSIOThreads();