Implement FSWrapperReplaceSingleFile

This commit is contained in:
Maschell 2025-02-07 18:25:00 +01:00
parent bee512fba6
commit 7416bc340b
7 changed files with 363 additions and 11 deletions

View File

@ -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/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20240424 /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/libcontentredirection:1.2-dev-20250207-b11c3c8 /artifacts $DEVKITPRO
WORKDIR project WORKDIR project

View File

@ -3,8 +3,8 @@
#include <coreinit/filesystem_fsa.h> #include <coreinit/filesystem_fsa.h>
typedef struct FSDirectoryEntryEx { typedef struct FSDirectoryEntryEx {
FSADirectoryEntry realEntry{}; FSADirectoryEntry realEntry = {};
bool isMarkedAsDeleted = false; bool isMarkedAsDeleted = false;
} FSDirectoryEntryEx; } FSDirectoryEntryEx;
struct DirInfoEx final : DirInfo { struct DirInfoEx final : DirInfo {
@ -13,3 +13,10 @@ struct DirInfoEx final : DirInfo {
int readResultNumberOfEntries = 0; int readResultNumberOfEntries = 0;
FSDirectoryHandle realDirHandle = 0; FSDirectoryHandle realDirHandle = 0;
}; };
struct DirInfoExSingleFile final : DirInfoBase {
FSADirectoryEntry directoryEntry = {};
bool entryRead = false;
bool entryReadSuccess = false;
FSDirectoryHandle realDirHandle = 0;
};

View File

@ -42,11 +42,7 @@ FSError FSWrapper::FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle)
dirHandle->path[0] = '\0'; dirHandle->path[0] = '\0';
strncat(dirHandle->path, newPath.c_str(), sizeof(dirHandle->path) - 1); strncat(dirHandle->path, newPath.c_str(), sizeof(dirHandle->path) - 1);
{ addDirHandle(dirHandle);
std::lock_guard<std::mutex> lock(openDirsMutex);
openDirs.push_back(dirHandle);
OSMemoryBarrier();
}
} else { } else {
auto err = errno; auto err = errno;
if (err == ENOENT) { if (err == ENOENT) {
@ -743,6 +739,12 @@ std::shared_ptr<DirInfoBase> FSWrapper::getDirFromHandle(const FSDirectoryHandle
return nullptr; return nullptr;
} }
void FSWrapper::addDirHandle(const std::shared_ptr<DirInfoBase> &dirHandle) {
std::lock_guard lock(openDirsMutex);
openDirs.push_back(dirHandle);
OSMemoryBarrier();
}
void FSWrapper::deleteDirHandle(FSDirectoryHandle handle) { void FSWrapper::deleteDirHandle(FSDirectoryHandle handle) {
if (!remove_locked_first_if(openDirsMutex, openDirs, [handle](auto &cur) { return static_cast<FSDirectoryHandle>(cur->handle) == handle; })) { if (!remove_locked_first_if(openDirsMutex, openDirs, [handle](auto &cur) { return static_cast<FSDirectoryHandle>(cur->handle) == handle; })) {
DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle); DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle);

View File

@ -114,6 +114,7 @@ protected:
bool isValidDirHandle(FSDirectoryHandle handle) override; bool isValidDirHandle(FSDirectoryHandle handle) override;
bool isValidFileHandle(FSFileHandle handle) override; bool isValidFileHandle(FSFileHandle handle) override;
void addDirHandle(const std::shared_ptr<DirInfoBase> &dirHandle);
void deleteDirHandle(FSDirectoryHandle handle) override; void deleteDirHandle(FSDirectoryHandle handle) override;
void deleteFileHandle(FSFileHandle handle) override; void deleteFileHandle(FSFileHandle handle) override;

View File

@ -0,0 +1,247 @@
#include "FSWrapperReplaceSingleFile.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/filesystem.h>
#include <filesystem>
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<FSError>(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<DirInfoExSingleFile>()) {
dirInfo->handle = (reinterpret_cast<uint32_t>(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<DirInfoExSingleFile> FSWrapperReplaceSingleFile::getDirExFromHandle(FSADirectoryHandle handle) {
auto dir = std::dynamic_pointer_cast<DirInfoExSingleFile>(getDirFromHandle(handle));
if (!dir) {
DEBUG_FUNCTION_LINE_ERR("[%s] dynamic_pointer_cast<DirInfoExSingleFile *>(%08X) failed", getName().c_str(), handle);
OSFatal("ContentRedirectionModule: dynamic_pointer_cast<DirInfoExSingleFile *> failed");
}
return dir;
}

View File

@ -0,0 +1,52 @@
#pragma once
#include "DirInfoEx.h"
#include "FSWrapper.h"
#include <coreinit/filesystem.h>
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<uint32_t>(mClientHandle);
}
protected:
[[nodiscard]] std::string GetNewPath(const std::string_view &path) const override;
std::shared_ptr<DirInfo> getNewDirInfoHandle() override {
OSFatal("FSWrapperReplaceSingleFile::getNewDirInfoHandle. Not implemented");
return {};
}
private:
bool IsDirPathToReplace(const std::string_view &path) const;
std::shared_ptr<DirInfoExSingleFile> getDirExFromHandle(FSDirectoryHandle handle);
FSAClientHandle mClientHandle;
std::string mPathToReplace;
std::string mFileNameToReplace;
std::string mFullPathToReplace;
std::string mReplacedWithPath;
std::string mReplacedWithFileName;
};

View File

@ -6,6 +6,8 @@
#include "utils/StringTools.h" #include "utils/StringTools.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "utils/utils.h" #include "utils/utils.h"
#include <FSWrapperReplaceSingleFile.h>
#include <content_redirection/redirection.h> #include <content_redirection/redirection.h>
#include <coreinit/dynload.h> #include <coreinit/dynload.h>
#include <mutex> #include <mutex>
@ -142,6 +144,46 @@ ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *l
return CONTENT_REDIRECTION_API_ERROR_NO_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<IFSWrapper> 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<FSWrapper>(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<FSWrapperMergeDirsWithParent>(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<FSWrapperReplaceSingleFile>(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) { ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) {
if (!remove_locked_first_if(gFSLayerMutex, gFSLayers, [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); DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
@ -167,7 +209,7 @@ ContentRedirectionApiErrorType CRGetVersion(ContentRedirectionVersion *outVersio
if (outVersion == nullptr) { if (outVersion == nullptr) {
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
} }
*outVersion = 1; *outVersion = 2;
return CONTENT_REDIRECTION_API_ERROR_NONE; return CONTENT_REDIRECTION_API_ERROR_NONE;
} }
@ -180,6 +222,7 @@ int CRRemoveDevice(const char *name) {
} }
WUMS_EXPORT_FUNCTION(CRGetVersion); WUMS_EXPORT_FUNCTION(CRGetVersion);
WUMS_EXPORT_FUNCTION(CRAddFSLayerEx);
WUMS_EXPORT_FUNCTION(CRAddFSLayer); WUMS_EXPORT_FUNCTION(CRAddFSLayer);
WUMS_EXPORT_FUNCTION(CRRemoveFSLayer); WUMS_EXPORT_FUNCTION(CRRemoveFSLayer);
WUMS_EXPORT_FUNCTION(CRSetActive); WUMS_EXPORT_FUNCTION(CRSetActive);