diff --git a/Dockerfile b/Dockerfile index 98ad2a7..f15b2d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM ghcr.io/wiiu-env/devkitppc:20240505 +FROM ghcr.io/wiiu-env/devkitppc:20241128 COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO -COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20240424 /artifacts $DEVKITPRO -COPY --from=ghcr.io/wiiu-env/libcontentredirection:20240428 /artifacts $DEVKITPRO +COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20250208 /artifacts $DEVKITPRO +COPY --from=ghcr.io/wiiu-env/libcontentredirection:20250208 /artifacts $DEVKITPRO WORKDIR project diff --git a/src/DirInfo.h b/src/DirInfo.h index 13fc292..dfaf416 100644 --- a/src/DirInfo.h +++ b/src/DirInfo.h @@ -2,9 +2,13 @@ #include #include -struct DirInfo { - virtual ~DirInfo() = default; +struct DirInfoBase { + virtual ~DirInfoBase() = default; FSDirectoryHandle handle{}; +}; + +struct DirInfo : DirInfoBase { + ~DirInfo() override = default; DIR *dir{}; char path[0x280]{}; }; diff --git a/src/DirInfoEx.h b/src/DirInfoEx.h index b5884cd..648ac7e 100644 --- a/src/DirInfoEx.h +++ b/src/DirInfoEx.h @@ -3,14 +3,20 @@ #include typedef struct FSDirectoryEntryEx { - FSADirectoryEntry realEntry{}; - bool isMarkedAsDeleted = false; + FSADirectoryEntry realEntry = {}; + bool isMarkedAsDeleted = false; } FSDirectoryEntryEx; -struct DirInfoEx : public DirInfo { -public: +struct DirInfoEx final : DirInfo { FSDirectoryEntryEx *readResult = nullptr; int readResultCapacity = 0; int readResultNumberOfEntries = 0; FSDirectoryHandle realDirHandle = 0; -}; \ No newline at end of file +}; + +struct DirInfoExSingleFile final : DirInfoBase { + FSADirectoryEntry directoryEntry = {}; + bool entryRead = false; + bool entryReadSuccess = false; + FSDirectoryHandle realDirHandle = 0; +}; diff --git a/src/FSWrapper.cpp b/src/FSWrapper.cpp index 5a151c1..cc007a9 100644 --- a/src/FSWrapper.cpp +++ b/src/FSWrapper.cpp @@ -3,12 +3,14 @@ #include "utils/StringTools.h" #include "utils/logger.h" #include "utils/utils.h" -#include + #include #include -#include -#include + +#include #include + +#include #include #include #include @@ -28,7 +30,7 @@ FSError FSWrapper::FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle) FSError result = FS_ERROR_OK; - auto dirHandle = getNewDirHandle(); + const auto dirHandle = getNewDirInfoHandle(); if (dirHandle) { DIR *dir; auto newPath = GetNewPath(path); @@ -40,11 +42,7 @@ FSError FSWrapper::FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle) dirHandle->path[0] = '\0'; strncat(dirHandle->path, newPath.c_str(), sizeof(dirHandle->path) - 1); - { - std::lock_guard lock(openDirsMutex); - openDirs.push_back(dirHandle); - OSMemoryBarrier(); - } + addDirHandle(dirHandle); } else { auto err = errno; if (err == ENOENT) { @@ -68,11 +66,11 @@ FSError FSWrapper::FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle) return result; } -FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry) { +FSError FSWrapper::FSReadDirWrapper(const FSDirectoryHandle handle, FSDirectoryEntry *entry) { if (!isValidDirHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } - auto dirHandle = getDirFromHandle(handle); + const auto dirHandle = getDirInfoFromHandle(handle); DIR *dir = dirHandle->dir; @@ -139,11 +137,11 @@ FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry * return result; } -FSError FSWrapper::FSCloseDirWrapper(FSDirectoryHandle handle) { +FSError FSWrapper::FSCloseDirWrapper(const FSDirectoryHandle handle) { if (!isValidDirHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } - auto dirHandle = getDirFromHandle(handle); + const auto dirHandle = getDirInfoFromHandle(handle); DIR *dir = dirHandle->dir; @@ -158,11 +156,11 @@ FSError FSWrapper::FSCloseDirWrapper(FSDirectoryHandle handle) { return result; } -FSError FSWrapper::FSRewindDirWrapper(FSDirectoryHandle handle) { +FSError FSWrapper::FSRewindDirWrapper(const FSDirectoryHandle handle) { if (!isValidDirHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } - auto dirHandle = getDirFromHandle(handle); + const auto dirHandle = getDirInfoFromHandle(handle); DIR *dir = dirHandle->dir; @@ -295,7 +293,7 @@ FSError FSWrapper::FSOpenFileWrapper(const char *path, const char *mode, FSFileH return result; } -FSError FSWrapper::FSCloseFileWrapper(FSFileHandle handle) { +FSError FSWrapper::FSCloseFileWrapper(const FSFileHandle handle) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -360,7 +358,7 @@ FSError FSWrapper::FSGetStatWrapper(const char *path, FSStat *stats) { return result; } -FSError FSWrapper::FSGetStatFileWrapper(FSFileHandle handle, FSStat *stats) { +FSError FSWrapper::FSGetStatFileWrapper(const FSFileHandle handle, FSStat *stats) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -382,7 +380,7 @@ FSError FSWrapper::FSGetStatFileWrapper(FSFileHandle handle, FSStat *stats) { return result; } -FSError FSWrapper::FSReadFileWrapper(void *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) { +FSError FSWrapper::FSReadFileWrapper(void *buffer, const uint32_t size, const uint32_t count, const FSFileHandle handle, [[maybe_unused]] uint32_t unk1) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -417,7 +415,7 @@ FSError FSWrapper::FSReadFileWrapper(void *buffer, uint32_t size, uint32_t count return result; } -FSError FSWrapper::FSReadFileWithPosWrapper(void *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, int32_t unk1) { +FSError FSWrapper::FSReadFileWithPosWrapper(void *buffer, const uint32_t size, const uint32_t count, const uint32_t pos, const FSFileHandle handle, const int32_t unk1) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -432,7 +430,7 @@ FSError FSWrapper::FSReadFileWithPosWrapper(void *buffer, uint32_t size, uint32_ return result; } -FSError FSWrapper::FSSetPosFileWrapper(FSFileHandle handle, uint32_t pos) { +FSError FSWrapper::FSSetPosFileWrapper(const FSFileHandle handle, const uint32_t pos) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -457,7 +455,7 @@ FSError FSWrapper::FSSetPosFileWrapper(FSFileHandle handle, uint32_t pos) { return result; } -FSError FSWrapper::FSGetPosFileWrapper(FSFileHandle handle, uint32_t *pos) { +FSError FSWrapper::FSGetPosFileWrapper(const FSFileHandle handle, uint32_t *pos) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -478,7 +476,7 @@ FSError FSWrapper::FSGetPosFileWrapper(FSFileHandle handle, uint32_t *pos) { return result; } -FSError FSWrapper::FSIsEofWrapper(FSFileHandle handle) { +FSError FSWrapper::FSIsEofWrapper(const FSFileHandle handle) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -509,7 +507,7 @@ FSError FSWrapper::FSIsEofWrapper(FSFileHandle handle) { return result; } -FSError FSWrapper::FSTruncateFileWrapper(FSFileHandle handle) { +FSError FSWrapper::FSTruncateFileWrapper(const FSFileHandle handle) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -542,7 +540,7 @@ FSError FSWrapper::FSTruncateFileWrapper(FSFileHandle handle) { return result; } -FSError FSWrapper::FSWriteFileWrapper(const uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) { +FSError FSWrapper::FSWriteFileWrapper(const uint8_t *buffer, const uint32_t size, const uint32_t count, const FSFileHandle handle, [[maybe_unused]] uint32_t unk1) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -569,7 +567,7 @@ FSError FSWrapper::FSWriteFileWrapper(const uint8_t *buffer, uint32_t size, uint result = FS_ERROR_MEDIA_ERROR; } } else { - result = static_cast(((uint32_t) writeRes) / size); + result = static_cast(static_cast(writeRes) / size); } return result; @@ -630,7 +628,7 @@ FSError FSWrapper::FSRenameWrapper(const char *oldPath, const char *newPath) { return FS_ERROR_OK; } -FSError FSWrapper::FSFlushFileWrapper(FSFileHandle handle) { +FSError FSWrapper::FSFlushFileWrapper(const FSFileHandle handle) { if (!isValidFileHandle(handle)) { return FS_ERROR_FORCE_PARENT_LAYER; } @@ -639,8 +637,8 @@ FSError FSWrapper::FSFlushFileWrapper(FSFileHandle handle) { return FS_ERROR_ACCESS_ERROR; } - auto fileHandle = getFileFromHandle(handle); - int real_fd = fileHandle->fd; + const auto fileHandle = getFileFromHandle(handle); + const int real_fd = fileHandle->fd; DEBUG_FUNCTION_LINE_VERBOSE("[%s] fsync fd %08X (FSFileHandle %08X)", real_fd, handle); FSError result = FS_ERROR_OK; @@ -673,16 +671,15 @@ bool FSWrapper::IsFileModeAllowed(const char *mode) { return false; } - bool FSWrapper::IsPathToReplace(const std::string_view &path) { return starts_with_case_insensitive(path, pPathToReplace); } -std::string FSWrapper::GetNewPath(const std::string_view &path) { - auto subStr = path.substr(this->pPathToReplace.length()); - auto res = string_format("%s%.*s", this->pReplacePathWith.c_str(), int(subStr.length()), subStr.data()); +std::string FSWrapper::GetNewPath(const std::string_view &path) const { + auto res = std::string(path); + SafeReplaceInString(res, this->pPathToReplace, this->pReplacePathWith); - std::replace(res.begin(), res.end(), '\\', '/'); + std::ranges::replace(res, '\\', '/'); uint32_t length = res.size(); @@ -700,25 +697,26 @@ std::string FSWrapper::GetNewPath(const std::string_view &path) { } bool FSWrapper::isValidFileHandle(FSFileHandle handle) { - std::lock_guard lock(openFilesMutex); + std::lock_guard lock(openFilesMutex); return std::ranges::any_of(openFiles, [handle](auto &cur) { return cur->handle == handle; }); } bool FSWrapper::isValidDirHandle(FSDirectoryHandle handle) { - std::lock_guard lock(openDirsMutex); + std::lock_guard lock(openDirsMutex); return std::ranges::any_of(openDirs, [handle](auto &cur) { return cur->handle == handle; }); } + std::shared_ptr FSWrapper::getNewFileHandle() { return make_shared_nothrow(); } -std::shared_ptr FSWrapper::getNewDirHandle() { +std::shared_ptr FSWrapper::getNewDirInfoHandle() { return make_shared_nothrow(); } -std::shared_ptr FSWrapper::getFileFromHandle(FSFileHandle handle) { - std::lock_guard lock(openFilesMutex); +std::shared_ptr FSWrapper::getFileFromHandle(const FSFileHandle handle) { + std::lock_guard lock(openFilesMutex); for (auto &file : openFiles) { if (file->handle == handle) { return file; @@ -729,8 +727,8 @@ std::shared_ptr FSWrapper::getFileFromHandle(FSFileHandle handle) { return nullptr; } -std::shared_ptr FSWrapper::getDirFromHandle(FSDirectoryHandle handle) { - std::lock_guard lock(openDirsMutex); +std::shared_ptr FSWrapper::getDirFromHandle(const FSDirectoryHandle handle) { + std::lock_guard lock(openDirsMutex); for (auto &dir : openDirs) { if (dir->handle == handle) { return dir; @@ -741,14 +739,20 @@ std::shared_ptr FSWrapper::getDirFromHandle(FSDirectoryHandle handle) { return nullptr; } +void FSWrapper::addDirHandle(const std::shared_ptr &dirHandle) { + std::lock_guard lock(openDirsMutex); + openDirs.push_back(dirHandle); + OSMemoryBarrier(); +} + void FSWrapper::deleteDirHandle(FSDirectoryHandle handle) { - if (!remove_locked_first_if(openDirsMutex, openDirs, [handle](auto &cur) { return (FSFileHandle) cur->handle == handle; })) { + if (!remove_locked_first_if(openDirsMutex, openDirs, [handle](auto &cur) { return static_cast(cur->handle) == handle; })) { DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle); } } void FSWrapper::deleteFileHandle(FSFileHandle handle) { - if (!remove_locked_first_if(openFilesMutex, openFiles, [handle](auto &cur) { return (FSFileHandle) cur->handle == handle; })) { + if (!remove_locked_first_if(openFilesMutex, openFiles, [handle](auto &cur) { return static_cast(cur->handle) == handle; })) { DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle); } } @@ -756,3 +760,13 @@ void FSWrapper::deleteFileHandle(FSFileHandle handle) { bool FSWrapper::SkipDeletedFilesInReadDir() { return true; } + +std::shared_ptr FSWrapper::getDirInfoFromHandle(const FSDirectoryHandle handle) { + auto dir = std::dynamic_pointer_cast(getDirFromHandle(handle)); + + if (!dir) { + DEBUG_FUNCTION_LINE_ERR("[%s] dynamic_pointer_cast(%08X) failed", getName().c_str(), handle); + OSFatal("ContentRedirectionModule: dynamic_pointer_cast failed"); + } + return dir; +} diff --git a/src/FSWrapper.h b/src/FSWrapper.h index b9a2eb6..84d5280 100644 --- a/src/FSWrapper.h +++ b/src/FSWrapper.h @@ -1,16 +1,19 @@ #pragma once + #include "DirInfo.h" #include "FileInfo.h" #include "IFSWrapper.h" #include "utils/logger.h" + #include -#include + +#include #include #include class FSWrapper : public IFSWrapper { public: - FSWrapper(const std::string &name, const std::string &pathToReplace, const std::string &replacePathWith, bool fallbackOnError, bool isWriteable) { + FSWrapper(const std::string &name, const std::string &pathToReplace, const std::string &replacePathWith, const bool fallbackOnError, const bool isWriteable) { this->pName = name; this->pPathToReplace = pathToReplace; this->pReplacePathWith = replacePathWith; @@ -18,16 +21,16 @@ class FSWrapper : public IFSWrapper { this->pIsWriteable = isWriteable; this->pCheckIfDeleted = fallbackOnError; - std::replace(pPathToReplace.begin(), pPathToReplace.end(), '\\', '/'); - std::replace(pReplacePathWith.begin(), pReplacePathWith.end(), '\\', '/'); + std::ranges::replace(pPathToReplace, '\\', '/'); + std::ranges::replace(pReplacePathWith, '\\', '/'); } ~FSWrapper() override { { - std::lock_guard lockFiles(openFilesMutex); + std::lock_guard lockFiles(openFilesMutex); openFiles.clear(); } { - std::lock_guard lockDirs(openDirsMutex); + std::lock_guard lockDirs(openDirsMutex); openDirs.clear(); } } @@ -35,7 +38,6 @@ class FSWrapper : public IFSWrapper { FSError FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle) override; - FSError FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry) override; @@ -96,7 +98,7 @@ class FSWrapper : public IFSWrapper { FSError FSFlushFileWrapper(FSFileHandle handle) override; uint32_t getLayerId() override { - return (uint32_t) this; + return getHandle(); } protected: @@ -104,21 +106,22 @@ class FSWrapper : public IFSWrapper { virtual bool IsPathToReplace(const std::string_view &path); - std::string GetNewPath(const std::string_view &path); + [[nodiscard]] virtual std::string GetNewPath(const std::string_view &path) const; - std::shared_ptr getDirFromHandle(FSDirectoryHandle handle); + std::shared_ptr getDirFromHandle(FSDirectoryHandle handle); std::shared_ptr getFileFromHandle(FSFileHandle handle); bool isValidDirHandle(FSDirectoryHandle handle) override; bool isValidFileHandle(FSFileHandle handle) override; + void addDirHandle(const std::shared_ptr &dirHandle); void deleteDirHandle(FSDirectoryHandle handle) override; void deleteFileHandle(FSFileHandle handle) override; virtual bool CheckFileShouldBeIgnored(std::string &path); virtual std::shared_ptr getNewFileHandle(); - virtual std::shared_ptr getNewDirHandle(); + virtual std::shared_ptr getNewDirInfoHandle(); virtual bool SkipDeletedFilesInReadDir(); @@ -127,11 +130,12 @@ class FSWrapper : public IFSWrapper { std::string deletePrefix = ".deleted_"; private: + std::shared_ptr getDirInfoFromHandle(FSDirectoryHandle handle); std::string pPathToReplace; std::string pReplacePathWith; bool pIsWriteable = false; std::mutex openFilesMutex; std::mutex openDirsMutex; std::vector> openFiles; - std::vector> openDirs; + std::vector> openDirs; }; diff --git a/src/FSWrapperMergeDirsWithParent.cpp b/src/FSWrapperMergeDirsWithParent.cpp index df82c5b..9a2bc91 100644 --- a/src/FSWrapperMergeDirsWithParent.cpp +++ b/src/FSWrapperMergeDirsWithParent.cpp @@ -2,9 +2,11 @@ #include "utils/StringTools.h" #include "utils/logger.h" #include "utils/utils.h" + #include #include #include + #include FSError FSWrapperMergeDirsWithParent::FSOpenDirWrapper(const char *path, @@ -21,17 +23,17 @@ FSError FSWrapperMergeDirsWithParent::FSOpenDirWrapper(const char *path, DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), *handle); return FS_ERROR_INVALID_DIRHANDLE; } - auto dirHandle = getDirExFromHandle(*handle); + auto dirHandle = getDirInfoExFromHandle(*handle); if (dirHandle != nullptr) { dirHandle->readResultCapacity = 0; dirHandle->readResultNumberOfEntries = 0; dirHandle->realDirHandle = 0; - if (clientHandle) { + if (mClientHandle) { FSADirectoryHandle realHandle = 0; DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSAOpenDir with %s for parent layer", getName().c_str(), path); FSError err; - if ((err = FSAOpenDir(clientHandle, path, &realHandle)) == FS_ERROR_OK) { + if ((err = FSAOpenDir(mClientHandle, path, &realHandle)) == FS_ERROR_OK) { dirHandle->realDirHandle = realHandle; } else { DEBUG_FUNCTION_LINE_ERR("[%s] Failed to open real dir %s. %s (%d)", getName().c_str(), path, FSAGetStatusStr(err), err); @@ -57,7 +59,11 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSADirectoryHandle handle DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle); return FS_ERROR_INVALID_DIRHANDLE; } - auto dirHandle = getDirExFromHandle(handle); + auto dirHandle = getDirInfoExFromHandle(handle); + if (!dirHandle) { + DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle); + return FS_ERROR_INVALID_DIRHANDLE; + } if (res == FS_ERROR_OK) { if (dirHandle->readResultCapacity == 0) { dirHandle->readResult = (FSDirectoryEntryEx *) malloc(sizeof(FSDirectoryEntryEx)); @@ -92,16 +98,15 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSADirectoryHandle handle } OSMemoryBarrier(); - } else if (res == FS_ERROR_END_OF_DIR) { // Read the real directory. if (dirHandle->realDirHandle != 0) { - if (clientHandle) { + if (mClientHandle) { FSADirectoryEntry realDirEntry; FSError readDirResult; while (true) { DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSReadDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle); - readDirResult = FSAReadDir(clientHandle, dirHandle->realDirHandle, &realDirEntry); + readDirResult = FSAReadDir(mClientHandle, dirHandle->realDirHandle, &realDirEntry); if (readDirResult == FS_ERROR_OK) { bool found = false; auto nameDeleted = deletePrefix + realDirEntry.name; @@ -154,11 +159,11 @@ FSError FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSADirectoryHandle handl DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle); return FS_ERROR_INVALID_DIRHANDLE; } - auto dirHandle = getDirExFromHandle(handle); + auto dirHandle = getDirInfoExFromHandle(handle); if (dirHandle->realDirHandle != 0) { - if (clientHandle) { + if (mClientHandle) { DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSCloseDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle); - auto realResult = FSACloseDir(clientHandle, dirHandle->realDirHandle); + auto realResult = FSACloseDir(mClientHandle, dirHandle->realDirHandle); if (realResult == FS_ERROR_OK) { dirHandle->realDirHandle = 0; } else { @@ -191,7 +196,7 @@ FSError FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSADirectoryHandle hand DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle); return FS_ERROR_INVALID_DIRHANDLE; } - auto dirHandle = getDirExFromHandle(handle); + auto dirHandle = getDirInfoExFromHandle(handle); if (dirHandle->readResult != nullptr) { dirHandle->readResultNumberOfEntries = 0; #pragma GCC diagnostic push @@ -201,12 +206,9 @@ FSError FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSADirectoryHandle hand } if (dirHandle->realDirHandle != 0) { - if (clientHandle) { + if (mClientHandle) { DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSARewindDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle); - FSError err; - if ((err = FSARewindDir(clientHandle, dirHandle->realDirHandle)) == FS_ERROR_OK) { - dirHandle->realDirHandle = 0; - } else { + if (FSError err = FSARewindDir(mClientHandle, dirHandle->realDirHandle); err != FS_ERROR_OK) { DEBUG_FUNCTION_LINE_ERR("[%s] Failed to rewind dir for realDirHandle %08X. %s (%d)", getName().c_str(), dirHandle->realDirHandle, FSAGetStatusStr(err), err); } } else { @@ -229,24 +231,24 @@ FSWrapperMergeDirsWithParent::FSWrapperMergeDirsWithParent(const std::string &na fallbackOnError, false) { FSAInit(); - this->clientHandle = FSAAddClient(nullptr); - if (clientHandle < 0) { - DEBUG_FUNCTION_LINE_ERR("[%s] FSAClientHandle failed: %s (%d)", name.c_str(), FSAGetStatusStr(static_cast(clientHandle)), clientHandle); - clientHandle = 0; + this->mClientHandle = FSAAddClient(nullptr); + if (mClientHandle < 0) { + DEBUG_FUNCTION_LINE_ERR("[%s] FSAClientHandle failed: %s (%d)", name.c_str(), FSAGetStatusStr(static_cast(mClientHandle)), mClientHandle); + mClientHandle = 0; } } FSWrapperMergeDirsWithParent::~FSWrapperMergeDirsWithParent() { - if (clientHandle) { + if (mClientHandle) { FSError res; - if ((res = FSADelClient(clientHandle)) != FS_ERROR_OK) { + if ((res = FSADelClient(mClientHandle)) != FS_ERROR_OK) { DEBUG_FUNCTION_LINE_ERR("[%s] FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res); } - clientHandle = 0; + mClientHandle = 0; } } -std::shared_ptr FSWrapperMergeDirsWithParent::getDirExFromHandle(FSADirectoryHandle handle) { +std::shared_ptr FSWrapperMergeDirsWithParent::getDirInfoExFromHandle(FSADirectoryHandle handle) { auto dir = std::dynamic_pointer_cast(getDirFromHandle(handle)); if (!dir) { @@ -256,6 +258,6 @@ std::shared_ptr FSWrapperMergeDirsWithParent::getDirExFromHandle(FSAD return dir; } -std::shared_ptr FSWrapperMergeDirsWithParent::getNewDirHandle() { +std::shared_ptr FSWrapperMergeDirsWithParent::getNewDirInfoHandle() { return make_shared_nothrow(); } diff --git a/src/FSWrapperMergeDirsWithParent.h b/src/FSWrapperMergeDirsWithParent.h index 24a56f1..6f8c753 100644 --- a/src/FSWrapperMergeDirsWithParent.h +++ b/src/FSWrapperMergeDirsWithParent.h @@ -1,10 +1,10 @@ #pragma once #include "DirInfoEx.h" #include "FSWrapper.h" + #include -#include -class FSWrapperMergeDirsWithParent : public FSWrapper { +class FSWrapperMergeDirsWithParent final : public FSWrapper { public: FSWrapperMergeDirsWithParent(const std::string &name, const std::string &pathToReplace, @@ -23,16 +23,16 @@ class FSWrapperMergeDirsWithParent : public FSWrapper { FSError FSRewindDirWrapper(FSDirectoryHandle handle) override; - std::shared_ptr getNewDirHandle() override; + std::shared_ptr getNewDirInfoHandle() override; bool SkipDeletedFilesInReadDir() override; uint32_t getLayerId() override { - return (uint32_t) clientHandle; + return static_cast(mClientHandle); } private: - FSAClientHandle clientHandle; + FSAClientHandle mClientHandle; - std::shared_ptr getDirExFromHandle(FSDirectoryHandle handle); + std::shared_ptr getDirInfoExFromHandle(FSDirectoryHandle handle); }; diff --git a/src/FSWrapperReplaceSingleFile.cpp b/src/FSWrapperReplaceSingleFile.cpp new file mode 100644 index 0000000..640ce9d --- /dev/null +++ b/src/FSWrapperReplaceSingleFile.cpp @@ -0,0 +1,247 @@ +#include "FSWrapperReplaceSingleFile.h" +#include "utils/StringTools.h" +#include "utils/logger.h" +#include "utils/utils.h" + +#include +#include +#include + +#include + +FSWrapperReplaceSingleFile::FSWrapperReplaceSingleFile(const std::string &name, + const std::string &fileToReplace, + const std::string &replaceWithPath, + const bool fallbackOnError) : FSWrapper(name, + fileToReplace, + replaceWithPath, + fallbackOnError, + false) { + auto strCpy = fileToReplace; + std::ranges::replace(strCpy, '\\', '/'); + auto asPath = std::filesystem::path(strCpy); + mPathToReplace = asPath.parent_path(); + mFileNameToReplace = asPath.filename(); + mFullPathToReplace = fileToReplace; + + strCpy = replaceWithPath; + std::ranges::replace(strCpy, '\\', '/'); + asPath = std::filesystem::path(strCpy); + mReplacedWithPath = asPath.parent_path(); + mReplacedWithFileName = asPath.filename(); + + FSAInit(); + this->mClientHandle = FSAAddClient(nullptr); + if (mClientHandle < 0) { + DEBUG_FUNCTION_LINE_ERR("[%s] FSAClientHandle failed: %s (%d)", name.c_str(), FSAGetStatusStr(static_cast(mClientHandle)), mClientHandle); + mClientHandle = 0; + } +} + +FSWrapperReplaceSingleFile::~FSWrapperReplaceSingleFile() { + if (mClientHandle) { + if (const FSError res = FSADelClient(mClientHandle); res != FS_ERROR_OK) { + DEBUG_FUNCTION_LINE_ERR("[%s] FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res); + } + mClientHandle = 0; + } +} + +FSError FSWrapperReplaceSingleFile::FSOpenDirWrapper(const char *path, + FSADirectoryHandle *handle) { + if (!IsDirPathToReplace(path)) { + return FS_ERROR_FORCE_PARENT_LAYER; + } + if (handle == nullptr) { + DEBUG_FUNCTION_LINE_ERR("[%s] handle was NULL", getName().c_str()); + return FS_ERROR_INVALID_PARAM; + } + if (const auto dirInfo = make_shared_nothrow()) { + dirInfo->handle = (reinterpret_cast(dirInfo.get()) & 0x0FFFFFFF) | 0x30000000; + *handle = dirInfo->handle; + addDirHandle(dirInfo); + if (!isValidDirHandle(*handle)) { + FSWrapper::FSCloseDirWrapper(*handle); + DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), *handle); + return FS_ERROR_INVALID_DIRHANDLE; + } + if (const auto dirHandle = getDirExFromHandle(*handle); dirHandle != nullptr) { + dirHandle->entryRead = false; + dirHandle->entryReadSuccess = false; + dirHandle->directoryEntry = {}; + dirHandle->realDirHandle = 0; + + if (mClientHandle) { + FSADirectoryHandle realHandle = 0; + DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSAOpenDir with %s for parent layer", getName().c_str(), path); + if (const FSError err = FSAOpenDir(mClientHandle, path, &realHandle); err == FS_ERROR_OK) { + dirHandle->realDirHandle = realHandle; + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] Failed to open real dir %s. %s (%d)", getName().c_str(), path, FSAGetStatusStr(err), err); + } + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str()); + } + OSMemoryBarrier(); + } + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc dir handle", getName().c_str()); + return FS_ERROR_MAX_DIRS; + } + return FS_ERROR_OK; +} + +FSError FSWrapperReplaceSingleFile::FSReadDirWrapper(const FSADirectoryHandle handle, FSADirectoryEntry *entry) { + if (!isValidDirHandle(handle)) { + return FS_ERROR_FORCE_PARENT_LAYER; + } + const auto dirHandle = getDirExFromHandle(handle); + if (!dirHandle) { + DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle); + return FS_ERROR_INVALID_DIRHANDLE; + } + FSError res = FS_ERROR_OK; + do { + if (!dirHandle->entryRead) { + dirHandle->entryRead = true; + const auto newPath = GetNewPath(mFullPathToReplace); + + struct stat path_stat {}; + + DEBUG_FUNCTION_LINE_VERBOSE("[%s] dir read of %s (%s)", getName().c_str(), mFullPathToReplace.c_str(), newPath.c_str()); + if (stat(newPath.c_str(), &path_stat) < 0) { + DEBUG_FUNCTION_LINE_WARN("[%s] Path %s (%s) for dir read not found ", getName().c_str(), mFullPathToReplace.c_str(), newPath.c_str()); + dirHandle->entryReadSuccess = false; + continue; + } + translate_stat(&path_stat, &dirHandle->directoryEntry.info); + strncpy(dirHandle->directoryEntry.name, mFileNameToReplace.c_str(), sizeof(dirHandle->directoryEntry.name)); + memcpy(entry, &dirHandle->directoryEntry, sizeof(FSADirectoryEntry)); + + dirHandle->entryReadSuccess = true; + OSMemoryBarrier(); + } else { + // Read the real directory. + if (dirHandle->realDirHandle != 0) { + if (mClientHandle) { + FSADirectoryEntry realDirEntry; + while (true) { + DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSReadDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle); + if (const FSError readDirResult = FSAReadDir(mClientHandle, dirHandle->realDirHandle, &realDirEntry); readDirResult == FS_ERROR_OK) { + // Skip already read new files + if (dirHandle->entryRead && dirHandle->entryReadSuccess && strcmp(dirHandle->directoryEntry.name, realDirEntry.name) == 0) { + continue; + } + + // But use new entries! + memcpy(entry, &realDirEntry, sizeof(FSADirectoryEntry)); + res = FS_ERROR_OK; + break; + } else if (readDirResult == FS_ERROR_END_OF_DIR) { + res = FS_ERROR_END_OF_DIR; + break; + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] real_FSReadDir returned an unexpected error: %s (%d)", getName().c_str(), FSAGetStatusStr(readDirResult), readDirResult); + res = FS_ERROR_END_OF_DIR; + break; + } + } + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str()); + } + } + } + return res; + } while (true); +} + +FSError FSWrapperReplaceSingleFile::FSCloseDirWrapper(const FSADirectoryHandle handle) { + if (!isValidDirHandle(handle)) { + return FS_ERROR_FORCE_PARENT_LAYER; + } + const auto dirHandle = getDirExFromHandle(handle); + if (dirHandle->realDirHandle != 0) { + if (mClientHandle) { + DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSCloseDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle); + auto realResult = FSACloseDir(mClientHandle, dirHandle->realDirHandle); + if (realResult == FS_ERROR_OK) { + dirHandle->realDirHandle = 0; + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close realDirHandle %d: res %s (%d)", getName().c_str(), dirHandle->realDirHandle, FSAGetStatusStr(realResult), realResult); + return realResult == FS_ERROR_CANCELLED ? FS_ERROR_CANCELLED : FS_ERROR_MEDIA_ERROR; + } + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str()); + } + } else { + DEBUG_FUNCTION_LINE_VERBOSE("[%s] dirHandle->realDirHandle was 0", getName().c_str()); + } + dirHandle->entryRead = false; + dirHandle->entryReadSuccess = false; + + OSMemoryBarrier(); + return FS_ERROR_OK; +} + +FSError FSWrapperReplaceSingleFile::FSRewindDirWrapper(const FSADirectoryHandle handle) { + if (!isValidDirHandle(handle)) { + return FS_ERROR_FORCE_PARENT_LAYER; + } + const auto dirHandle = getDirExFromHandle(handle); + dirHandle->entryRead = false; + dirHandle->entryReadSuccess = false; + if (dirHandle->realDirHandle != 0) { + if (mClientHandle) { + DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSARewindDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle); + if (const FSError err = FSARewindDir(mClientHandle, dirHandle->realDirHandle); err != FS_ERROR_OK) { + DEBUG_FUNCTION_LINE_ERR("[%s] Failed to rewind dir for realDirHandle %08X. %s (%d)", getName().c_str(), dirHandle->realDirHandle, FSAGetStatusStr(err), err); + } + } else { + DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str()); + } + } else { + DEBUG_FUNCTION_LINE_VERBOSE("[%s] dirHandle->realDirHandle was 0", getName().c_str()); + } + OSMemoryBarrier(); + + return FS_ERROR_OK; +} + +bool FSWrapperReplaceSingleFile::SkipDeletedFilesInReadDir() { + return false; +} + +bool FSWrapperReplaceSingleFile::IsDirPathToReplace(const std::string_view &path) const { + return starts_with_case_insensitive(path, mPathToReplace); +} + +std::string FSWrapperReplaceSingleFile::GetNewPath(const std::string_view &path) const { + auto pathCpy = std::string(path); + SafeReplaceInString(pathCpy, this->mPathToReplace, this->mReplacedWithPath); + SafeReplaceInString(pathCpy, this->mFileNameToReplace, this->mReplacedWithFileName); + std::ranges::replace(pathCpy, '\\', '/'); + + uint32_t length = pathCpy.size(); + + //! clear path of double slashes + for (uint32_t i = 1; i < length; ++i) { + if (pathCpy[i - 1] == '/' && pathCpy[i] == '/') { + pathCpy.erase(i, 1); + i--; + length--; + } + } + + DEBUG_FUNCTION_LINE_VERBOSE("[%s] Redirect %.*s -> %s", getName().c_str(), int(path.length()), path.data(), pathCpy.c_str()); + return pathCpy; +} + +std::shared_ptr FSWrapperReplaceSingleFile::getDirExFromHandle(FSADirectoryHandle handle) { + auto dir = std::dynamic_pointer_cast(getDirFromHandle(handle)); + + if (!dir) { + DEBUG_FUNCTION_LINE_ERR("[%s] dynamic_pointer_cast(%08X) failed", getName().c_str(), handle); + OSFatal("ContentRedirectionModule: dynamic_pointer_cast failed"); + } + return dir; +} \ No newline at end of file diff --git a/src/FSWrapperReplaceSingleFile.h b/src/FSWrapperReplaceSingleFile.h new file mode 100644 index 0000000..c14dcf5 --- /dev/null +++ b/src/FSWrapperReplaceSingleFile.h @@ -0,0 +1,52 @@ +#pragma once +#include "DirInfoEx.h" +#include "FSWrapper.h" + +#include + +class FSWrapperReplaceSingleFile final : public FSWrapper { +public: + FSWrapperReplaceSingleFile(const std::string &name, + const std::string &fileToReplace, + const std::string &replaceWithPath, + bool fallbackOnError); + + ~FSWrapperReplaceSingleFile() override; + + FSError FSOpenDirWrapper(const char *path, + FSDirectoryHandle *handle) override; + + FSError FSReadDirWrapper(FSDirectoryHandle handle, + FSDirectoryEntry *entry) override; + + FSError FSCloseDirWrapper(FSDirectoryHandle handle) override; + + FSError FSRewindDirWrapper(FSDirectoryHandle handle) override; + + bool SkipDeletedFilesInReadDir() override; + + uint32_t getLayerId() override { + return static_cast(mClientHandle); + } + + +protected: + [[nodiscard]] std::string GetNewPath(const std::string_view &path) const override; + + std::shared_ptr getNewDirInfoHandle() override { + OSFatal("FSWrapperReplaceSingleFile::getNewDirInfoHandle. Not implemented"); + return {}; + } + +private: + bool IsDirPathToReplace(const std::string_view &path) const; + + std::shared_ptr getDirExFromHandle(FSDirectoryHandle handle); + + FSAClientHandle mClientHandle; + std::string mPathToReplace; + std::string mFileNameToReplace; + std::string mFullPathToReplace; + std::string mReplacedWithPath; + std::string mReplacedWithFileName; +}; diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index 1165536..ef23d62 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -11,39 +11,41 @@ #include #include -std::mutex workingDirMutex; -std::map workingDirs; +namespace { + std::mutex sWorkingDirMutex; + std::map sWorkingDirs; +} // namespace -std::mutex fsLayerMutex; -std::vector> fsLayers; +std::mutex gFSLayerMutex; +std::vector> gFSLayers; -std::string getFullPathGeneric(FSAClientHandle client, const char *path, std::mutex &mutex, std::map &map) { - std::lock_guard workingDirLock(mutex); +std::string getFullPathGeneric(const FSAClientHandle client, const char *path, std::mutex &mutex, const std::map &map) { + std::lock_guard workingDirLock(mutex); std::string res; if (path[0] != '/' && path[0] != '\\') { if (map.count(client) == 0) { DEBUG_FUNCTION_LINE_WARN("No working dir found for client %08X, fallback to \"/\"", client); - workingDirs[client] = "/"; + sWorkingDirs[client] = "/"; } res = string_format("%s%s", map.at(client).c_str(), path); } else { res = path; } - std::replace(res.begin(), res.end(), '\\', '/'); + std::ranges::replace(res, '\\', '/'); return res; } -void setWorkingDirGeneric(FSAClientHandle client, const char *path, std::mutex &mutex, std::map &map) { +void setWorkingDirGeneric(const FSAClientHandle client, const char *path, std::mutex &mutex, std::map &map) { if (!path) { DEBUG_FUNCTION_LINE_WARN("Path was NULL"); return; } - std::lock_guard workingDirLock(mutex); + std::lock_guard workingDirLock(mutex); std::string cwd(path); if (cwd.empty() || cwd.back() != '/') { @@ -54,22 +56,22 @@ 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(const FSAClientHandle pClient, const char *path) { + return getFullPathGeneric(pClient, path, sWorkingDirMutex, sWorkingDirs); } -void setWorkingDir(FSAClientHandle client, const char *path) { - setWorkingDirGeneric(client, path, workingDirMutex, workingDirs); +void setWorkingDir(const FSAClientHandle client, const char *path) { + setWorkingDirGeneric(client, path, sWorkingDirMutex, sWorkingDirs); } void clearFSLayer() { { - std::lock_guard workingDirLock(workingDirMutex); - workingDirs.clear(); + std::lock_guard workingDirLock(sWorkingDirMutex); + sWorkingDirs.clear(); } { - std::lock_guard layerLock(fsLayerMutex); - fsLayers.clear(); + std::lock_guard layerLock(gFSLayerMutex); + gFSLayers.clear(); } } @@ -93,11 +95,11 @@ bool sendMessageToThread(FSShimWrapperMessage *param) { } 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) { + std::lock_guard lock(gFSLayerMutex); + if (!gFSLayers.empty()) { + uint32_t startIndex = gFSLayers.size(); + for (uint32_t i = gFSLayers.size(); i > 0; i--) { + if (gFSLayers[i - 1]->getLayerId() == param->shim->clientHandle) { startIndex = i - 1; break; } @@ -105,7 +107,7 @@ FSError doForLayer(FSShimWrapper *param) { if (startIndex > 0) { for (uint32_t i = startIndex; i > 0; i--) { - auto &layer = fsLayers[i - 1]; + auto &layer = gFSLayers[i - 1]; if (!layer->isActive()) { continue; } diff --git a/src/FileUtils.h b/src/FileUtils.h index f31f5df..5095d4a 100644 --- a/src/FileUtils.h +++ b/src/FileUtils.h @@ -55,8 +55,8 @@ struct FSShimWrapperMessage { extern bool gThreadsRunning; extern FSIOThreadData gThreadData[3]; -extern std::mutex fsLayerMutex; -extern std::vector> fsLayers; +extern std::mutex gFSLayerMutex; +extern std::vector> gFSLayers; #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)) diff --git a/src/IFSWrapper.h b/src/IFSWrapper.h index 528709d..6d2faa7 100644 --- a/src/IFSWrapper.h +++ b/src/IFSWrapper.h @@ -1,6 +1,7 @@ #pragma once #include -#include +#include + #include #define FS_ERROR_EXTRA_MASK 0xFFF00000 @@ -53,7 +54,6 @@ class IFSWrapper { return FS_ERROR_FORCE_PARENT_LAYER; } - virtual FSError FSGetStatFileWrapper(FSAFileHandle handle, FSAStat *stats) { return FS_ERROR_FORCE_PARENT_LAYER; @@ -151,11 +151,17 @@ class IFSWrapper { virtual uint32_t getLayerId() = 0; virtual uint32_t getHandle() { - return (uint32_t) this; + return reinterpret_cast(this); + } + + IFSWrapper() { + // Abuse this as a stable handle that references itself and survives std::move + *mHandle = reinterpret_cast(mHandle.get()); } private: - bool pIsActive = true; + bool pIsActive = true; + std::unique_ptr mHandle = std::make_unique(); protected: bool pFallbackOnError = false; diff --git a/src/export.cpp b/src/export.cpp index a2d6c24..7bab400 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -6,6 +6,8 @@ #include "utils/StringTools.h" #include "utils/logger.h" #include "utils/utils.h" + +#include #include #include #include @@ -15,6 +17,7 @@ struct AOCTitle { WUT_UNKNOWN_BYTES(0x68); }; + WUT_CHECK_SIZE(AOCTitle, 0x68); bool getAOCPath(std::string &outStr) { @@ -90,7 +93,7 @@ bool getAOCPath(std::string &outStr) { return result; } -ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType) { +ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, const FSLayerType layerType) { if (!handle || layerName == nullptr || replacementDir == nullptr) { DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG"); return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; @@ -132,17 +135,57 @@ 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); + std::lock_guard lock(gFSLayerMutex); *handle = (CRLayerHandle) ptr->getHandle(); - fsLayers.push_back(std::move(ptr)); + gFSLayers.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 CRAddFSLayerEx(CRLayerHandle *handle, const char *layerName, const char *targetPath, const char *replacementPath, const FSLayerTypeEx layerType) { + if (!handle || layerName == nullptr || replacementPath == nullptr || targetPath == nullptr) { + DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG"); + return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; + } + std::unique_ptr ptr; + switch (layerType) { + case FS_LAYER_TYPE_EX_REPLACE_DIRECTORY: { + DEBUG_FUNCTION_LINE_INFO("[AddFSLayerEx] Redirecting \"%s\" to \"%s\", mode: \"replace\"", targetPath, replacementPath); + ptr = make_unique_nothrow(layerName, targetPath, replacementPath, false, false); + break; + } + case FS_LAYER_TYPE_EX_MERGE_DIRECTORY: { + DEBUG_FUNCTION_LINE_INFO("[AddFSLayerEx] Redirecting \"%s\" to \"%s\", mode: \"merge\"", targetPath, replacementPath); + ptr = make_unique_nothrow(layerName, targetPath, replacementPath, true); + break; + } + case FS_LAYER_TYPE_EX_REPLACE_FILE: { + DEBUG_FUNCTION_LINE_INFO("[AddFSLayerEx] Redirecting file \"%s\" to \"%s\"", targetPath, replacementPath); + ptr = make_unique_nothrow(layerName, targetPath, replacementPath, true); + break; + } + default: { + DEBUG_FUNCTION_LINE_ERR("[AddFSLayerEx] CONTENT_REDIRECTION_API_ERROR_UNKNOWN_LAYER_DIR_TYPE: %s %s %d", layerName, replacementPath, layerType); + return CONTENT_REDIRECTION_API_ERROR_UNKNOWN_FS_LAYER_TYPE; + } + } + + if (ptr) { + DEBUG_FUNCTION_LINE_VERBOSE("[AddFSLayerEx] Added new layer (%s). Target path: %s Replacement dir: %s Type:%d", layerName, targetPath, replacementPath, layerType); + std::lock_guard lock(gFSLayerMutex); + *handle = ptr->getHandle(); + gFSLayers.emplace_back(std::move(ptr)); + return CONTENT_REDIRECTION_API_ERROR_NONE; + } + DEBUG_FUNCTION_LINE_ERR("[AddFSLayerEx] 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; })) { + if (!remove_locked_first_if(gFSLayerMutex, gFSLayers, [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; } @@ -150,8 +193,8 @@ ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) { } ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) { - std::lock_guard lock(fsLayerMutex); - for (auto &cur : fsLayers) { + std::lock_guard lock(gFSLayerMutex); + for (auto &cur : gFSLayers) { if ((CRLayerHandle) cur->getHandle() == handle) { cur->setActive(active); return CONTENT_REDIRECTION_API_ERROR_NONE; @@ -166,7 +209,7 @@ ContentRedirectionApiErrorType CRGetVersion(ContentRedirectionVersion *outVersio if (outVersion == nullptr) { return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; } - *outVersion = 1; + *outVersion = 2; return CONTENT_REDIRECTION_API_ERROR_NONE; } @@ -179,6 +222,7 @@ int CRRemoveDevice(const char *name) { } WUMS_EXPORT_FUNCTION(CRGetVersion); +WUMS_EXPORT_FUNCTION(CRAddFSLayerEx); WUMS_EXPORT_FUNCTION(CRAddFSLayer); WUMS_EXPORT_FUNCTION(CRRemoveFSLayer); WUMS_EXPORT_FUNCTION(CRSetActive); diff --git a/src/main.cpp b/src/main.cpp index 224fb7f..ae44993 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,7 @@ WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection"); WUMS_USE_WUT_DEVOPTAB(); WUMS_DEPENDS_ON(homebrew_functionpatcher); -#define VERSION "v0.2.7" +#define VERSION "v0.2.8" DECL_FUNCTION(void, OSCancelThread, OSThread *thread) { if (thread == gThreadData[0].thread || thread == gThreadData[1].thread || thread == gThreadData[2].thread) { diff --git a/src/utils/StringTools.cpp b/src/utils/StringTools.cpp new file mode 100644 index 0000000..fd551a2 --- /dev/null +++ b/src/utils/StringTools.cpp @@ -0,0 +1,16 @@ +#include "StringTools.h" +#include + +void SafeReplaceInString(std::string &subject, std::string search, const std::string &replace) { + if (search.empty() || search == replace) { + return; // Avoid infinite loops and invalid input + } + std::string lowerSubject = subject; + + std::ranges::transform(subject, lowerSubject.begin(), ::tolower); + std::ranges::transform(search, search.begin(), ::tolower); + + if (const size_t pos = lowerSubject.find(search); pos != std::string::npos) { + subject.replace(pos, search.length(), replace); + } +} \ No newline at end of file diff --git a/src/utils/StringTools.h b/src/utils/StringTools.h index 78ec91a..08d3b47 100644 --- a/src/utils/StringTools.h +++ b/src/utils/StringTools.h @@ -2,7 +2,6 @@ #include #include -#include template std::string string_format(const std::string &format, Args... args) { @@ -13,12 +12,14 @@ std::string string_format(const std::string &format, Args... args) { return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside } -static inline bool starts_with_case_insensitive(std::string_view str, std::string_view prefix) { +void SafeReplaceInString(std::string &subject, std::string search, const std::string &replace); + +static inline bool starts_with_case_insensitive(const std::string_view str, const std::string_view prefix) { if (str.size() < prefix.size()) return false; return std::equal(prefix.begin(), prefix.end(), str.begin(), - [](char a, char b) { + [](const char a, const char b) { return std::tolower(a) == std::tolower(b); }); } diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 5d338bf..b141d9f 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -54,7 +54,7 @@ FSTime translate_time(time_t timeValue) { return adjustedTimeValue * 1000000; } -void translate_stat(struct stat *posStat, FSStat *fsStat) { +void translate_stat(const struct stat *posStat, FSStat *fsStat) { memset(fsStat, 0, sizeof(FSStat)); fsStat->size = posStat->st_size; diff --git a/src/utils/utils.h b/src/utils/utils.h index a9d7a6f..ef974ce 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -32,4 +32,4 @@ bool remove_locked_first_if(std::mutex &mutex, std::vector &list, #define ROUNDDOWN(val, align) ((val) & ~(align - 1)) #define ROUNDUP(val, align) ROUNDDOWN(((val) + (align - 1)), align) -void translate_stat(struct stat *posStat, FSStat *fsStat); \ No newline at end of file +void translate_stat(const struct stat *posStat, FSStat *fsStat); \ No newline at end of file