Compare commits

...

8 Commits

Author SHA1 Message Date
Maschell
03ea0aceae Bump version to 0.2.7 2024-05-08 11:22:20 +02:00
Maschell
45efb02d27 Make sure all path checks are case-insensitive 2024-05-08 11:22:20 +02:00
Maschell
60b711cf81 Update Dockerfile 2024-05-06 16:34:44 +02:00
Maschell
884a3561d2 Replace FSWriteFile and FSReadFile only for the game/menu process 2024-04-28 18:20:19 +02:00
Maschell
f9a4b537c8 Skip stat if _DIRENT_HAVE_D_STAT is set 2024-04-28 15:31:48 +02:00
Maschell
835881abfa Add support for FS_LAYER_TYPE_SAVE_REPLACE_FOR_CURRENT_USER 2024-04-28 15:31:48 +02:00
Maschell
d0dfbd6d4e Hotfix for readdir bug in latest newlib version 2024-04-28 15:31:48 +02:00
Maschell
a2c8863cbe Revert "Add support for FS_LAYER_TYPE_SAVE_REPLACE_IGNORE_VOL_SAVE_COMMON"
This reverts commit 71406e2044.
2024-04-28 15:31:48 +02:00
8 changed files with 39 additions and 29 deletions

View File

@ -1,7 +1,7 @@
FROM ghcr.io/wiiu-env/devkitppc:20240423 FROM ghcr.io/wiiu-env/devkitppc:20240505
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:20240424 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libcontentredirection:20240428 /artifacts $DEVKITPRO
WORKDIR project WORKDIR project

View File

@ -560,11 +560,11 @@ function_replacement_data_t fs_file_function_replacements[] = {
REPLACE_FUNCTION(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync), REPLACE_FUNCTION(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync),
REPLACE_FUNCTION(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync), REPLACE_FUNCTION(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync),
REPLACE_FUNCTION(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync), REPLACE_FUNCTION(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync),
REPLACE_FUNCTION_VIA_ADDRESS(FSReadFileGeneric, 0x3201C400 + 0x4ecc0, 0x101C400 + 0x4ecc0), REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(FSReadFileGeneric, 0x3201C400 + 0x4ecc0, 0x101C400 + 0x4ecc0, FP_TARGET_PROCESS_GAME_AND_MENU),
REPLACE_FUNCTION(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync), REPLACE_FUNCTION(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync),
REPLACE_FUNCTION(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync), REPLACE_FUNCTION(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync),
REPLACE_FUNCTION(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync), REPLACE_FUNCTION(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync),
REPLACE_FUNCTION_VIA_ADDRESS(FSWriteFileGeneric, 0x3201C400 + 0x4eec0, 0x101C400 + 0x4eec0), REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(FSWriteFileGeneric, 0x3201C400 + 0x4eec0, 0x101C400 + 0x4eec0, FP_TARGET_PROCESS_GAME_AND_MENU),
REPLACE_FUNCTION(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync), REPLACE_FUNCTION(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync),
REPLACE_FUNCTION(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync), REPLACE_FUNCTION(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync),
REPLACE_FUNCTION(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync), REPLACE_FUNCTION(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync),

View File

@ -83,7 +83,7 @@ FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *
struct dirent *entry_ = readdir(dir); struct dirent *entry_ = readdir(dir);
if (entry_) { if (entry_) {
if (SkipDeletedFilesInReadDir() && std::string_view(entry_->d_name).starts_with(deletePrefix)) { if (SkipDeletedFilesInReadDir() && starts_with_case_insensitive(entry_->d_name, deletePrefix)) {
DEBUG_FUNCTION_LINE_ERR("Skip file file name %s because of the prefix", entry_->d_name); DEBUG_FUNCTION_LINE_ERR("Skip file file name %s because of the prefix", entry_->d_name);
continue; continue;
} }
@ -98,6 +98,9 @@ FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *
if (strcmp(entry_->d_name, ".") == 0 || strcmp(entry_->d_name, "..") == 0) { if (strcmp(entry_->d_name, ".") == 0 || strcmp(entry_->d_name, "..") == 0) {
entry->info.size = 0; entry->info.size = 0;
} else { } else {
#ifdef _DIRENT_HAVE_D_STAT
translate_stat(&entry_->d_stat, &entry->info);
#else
struct stat sb {}; struct stat sb {};
auto path = string_format("%s/%s", dirHandle->path, entry_->d_name); auto path = string_format("%s/%s", dirHandle->path, entry_->d_name);
std::replace(path.begin(), path.end(), '\\', '/'); std::replace(path.begin(), path.end(), '\\', '/');
@ -120,13 +123,14 @@ FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *
result = FS_ERROR_MEDIA_ERROR; result = FS_ERROR_MEDIA_ERROR;
break; break;
} }
#endif
} }
} }
result = FS_ERROR_OK; result = FS_ERROR_OK;
} else { } else {
auto err = errno; auto err = errno;
if (err != 0) { if (err != 0) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to read dir %08X (handle %08X)", getName().c_str(), dir, handle); DEBUG_FUNCTION_LINE_ERR("[%s] Failed to read dir %08X (handle %08X). errno %d (%s)", getName().c_str(), dir, handle, err, strerror(err));
result = FS_ERROR_MEDIA_ERROR; result = FS_ERROR_MEDIA_ERROR;
} }
} }
@ -313,7 +317,7 @@ FSError FSWrapper::FSCloseFileWrapper(FSFileHandle handle) {
bool FSWrapper::CheckFileShouldBeIgnored(std::string &path) { bool FSWrapper::CheckFileShouldBeIgnored(std::string &path) {
auto asPath = std::filesystem::path(path); auto asPath = std::filesystem::path(path);
if (std::string(asPath.filename().c_str()).starts_with(deletePrefix)) { if (starts_with_case_insensitive(asPath.filename().c_str(), deletePrefix)) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Ignore %s, filename starts with %s", getName().c_str(), path.c_str(), deletePrefix.c_str()); DEBUG_FUNCTION_LINE_VERBOSE("[%s] Ignore %s, filename starts with %s", getName().c_str(), path.c_str(), deletePrefix.c_str());
return true; return true;
} }
@ -669,16 +673,9 @@ bool FSWrapper::IsFileModeAllowed(const char *mode) {
return false; return false;
} }
bool FSWrapper::IsPathToReplace(const std::string_view &path) { bool FSWrapper::IsPathToReplace(const std::string_view &path) {
if (!path.starts_with(pPathToReplace)) { return starts_with_case_insensitive(path, pPathToReplace);
return false;
}
if (std::ranges::any_of(pIgnorePaths.cbegin(), pIgnorePaths.cend(), [&path](auto &ignorePath) { return path.starts_with(ignorePath); })) {
return false;
}
return true;
} }
std::string FSWrapper::GetNewPath(const std::string_view &path) { std::string FSWrapper::GetNewPath(const std::string_view &path) {

View File

@ -10,20 +10,16 @@
class FSWrapper : public IFSWrapper { class FSWrapper : public IFSWrapper {
public: public:
FSWrapper(const std::string &name, const std::string &pathToReplace, const std::string &replacePathWith, bool fallbackOnError, bool isWriteable, std::vector<std::string> ignorePaths = {}) { FSWrapper(const std::string &name, const std::string &pathToReplace, const std::string &replacePathWith, bool fallbackOnError, bool isWriteable) {
this->pName = name; this->pName = name;
this->pPathToReplace = pathToReplace; this->pPathToReplace = pathToReplace;
this->pReplacePathWith = replacePathWith; this->pReplacePathWith = replacePathWith;
this->pFallbackOnError = fallbackOnError; this->pFallbackOnError = fallbackOnError;
this->pIsWriteable = isWriteable; this->pIsWriteable = isWriteable;
this->pCheckIfDeleted = fallbackOnError; this->pCheckIfDeleted = fallbackOnError;
this->pIgnorePaths = std::move(ignorePaths);
std::replace(pPathToReplace.begin(), pPathToReplace.end(), '\\', '/'); std::replace(pPathToReplace.begin(), pPathToReplace.end(), '\\', '/');
std::replace(pReplacePathWith.begin(), pReplacePathWith.end(), '\\', '/'); std::replace(pReplacePathWith.begin(), pReplacePathWith.end(), '\\', '/');
for (auto &ignorePath : pIgnorePaths) {
std::replace(ignorePath.begin(), ignorePath.end(), '\\', '/');
}
} }
~FSWrapper() override { ~FSWrapper() override {
{ {
@ -133,7 +129,6 @@ protected:
private: private:
std::string pPathToReplace; std::string pPathToReplace;
std::string pReplacePathWith; std::string pReplacePathWith;
std::vector<std::string> pIgnorePaths;
bool pIsWriteable = false; bool pIsWriteable = false;
std::mutex openFilesMutex; std::mutex openFilesMutex;
std::mutex openDirsMutex; std::mutex openDirsMutex;

View File

@ -1,4 +1,5 @@
#include "FSWrapperMergeDirsWithParent.h" #include "FSWrapperMergeDirsWithParent.h"
#include "utils/StringTools.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "utils/utils.h" #include "utils/utils.h"
#include <coreinit/cache.h> #include <coreinit/cache.h>
@ -83,7 +84,7 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSADirectoryHandle handle
/** /**
* Read the next entry if this entry starts with deletePrefix. We keep the entry but mark it as deleted. * Read the next entry if this entry starts with deletePrefix. We keep the entry but mark it as deleted.
*/ */
if (std::string_view(entry->name).starts_with(deletePrefix)) { if (starts_with_case_insensitive(entry->name, deletePrefix)) {
dirHandle->readResult[dirHandle->readResultNumberOfEntries].isMarkedAsDeleted = true; dirHandle->readResult[dirHandle->readResultNumberOfEntries].isMarkedAsDeleted = true;
OSMemoryBarrier(); OSMemoryBarrier();

View File

@ -3,11 +3,13 @@
#include "FileUtils.h" #include "FileUtils.h"
#include "IFSWrapper.h" #include "IFSWrapper.h"
#include "malloc.h" #include "malloc.h"
#include "utils/StringTools.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "utils/utils.h" #include "utils/utils.h"
#include <content_redirection/redirection.h> #include <content_redirection/redirection.h>
#include <coreinit/dynload.h> #include <coreinit/dynload.h>
#include <mutex> #include <mutex>
#include <nn/act.h>
#include <wums/exports.h> #include <wums/exports.h>
struct AOCTitle { struct AOCTitle {
@ -115,10 +117,15 @@ ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *l
} else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE) { } else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE) {
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/save\" to \"%s\", mode: \"replace\"", replacementDir); DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/save\" to \"%s\", mode: \"replace\"", replacementDir);
ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/save", replacementDir, false, true); ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/save", replacementDir, false, true);
} else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE_IGNORE_VOL_SAVE_COMMON) { } else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE_FOR_CURRENT_USER) {
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/save\" to \"%s\", mode: \"replace\", ignore: (\"/vol/save/common\")", replacementDir); nn::act::Initialize();
std::vector<std::string> ignorePaths({"/vol/save/common"}); nn::act::PersistentId persistentId = nn::act::GetPersistentId();
ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/save", replacementDir, false, true, ignorePaths); nn::act::Finalize();
std::string user = string_format("/vol/save/%08X", 0x80000000 | persistentId);
DEBUG_FUNCTION_LINE_INFO("Redirecting \"%s\" to \"%s\", mode: \"replace\"", user.c_str(), replacementDir);
ptr = make_unique_nothrow<FSWrapper>(layerName, user, replacementDir, false, true);
} else { } else {
DEBUG_FUNCTION_LINE_ERR("CONTENT_REDIRECTION_API_ERROR_UNKNOWN_LAYER_DIR_TYPE: %s %s %d", layerName, replacementDir, layerType); DEBUG_FUNCTION_LINE_ERR("CONTENT_REDIRECTION_API_ERROR_UNKNOWN_LAYER_DIR_TYPE: %s %s %d", layerName, replacementDir, layerType);
return CONTENT_REDIRECTION_API_ERROR_UNKNOWN_FS_LAYER_TYPE; return CONTENT_REDIRECTION_API_ERROR_UNKNOWN_FS_LAYER_TYPE;

View File

@ -10,7 +10,7 @@ WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection");
WUMS_USE_WUT_DEVOPTAB(); WUMS_USE_WUT_DEVOPTAB();
WUMS_DEPENDS_ON(homebrew_functionpatcher); WUMS_DEPENDS_ON(homebrew_functionpatcher);
#define VERSION "v0.2.6" #define VERSION "v0.2.7"
DECL_FUNCTION(void, OSCancelThread, OSThread *thread) { DECL_FUNCTION(void, OSCancelThread, OSThread *thread) {
if (thread == gThreadData[0].thread || thread == gThreadData[1].thread || thread == gThreadData[2].thread) { if (thread == gThreadData[0].thread || thread == gThreadData[1].thread || thread == gThreadData[2].thread) {

View File

@ -11,4 +11,14 @@ std::string string_format(const std::string &format, Args... args) {
auto buf = std::make_unique<char[]>(size); auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, format.c_str(), args...); std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside 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) {
if (str.size() < prefix.size())
return false;
return std::equal(prefix.begin(), prefix.end(), str.begin(),
[](char a, char b) {
return std::tolower(a) == std::tolower(b);
});
}