Compare commits

...

23 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
Maschell 0d5bf8c78f Update Dockerfiles to use latest devkitPPC and wut 2024-04-24 18:38:31 +02:00
Maschell 9d0dbda1db Bump version to 0.2.6 2024-04-24 18:38:31 +02:00
Maschell 75854a05e5 Update .gitignore to ignore .zip files 2024-04-24 18:38:31 +02:00
dependabot[bot] 6c2748569d Bump softprops/action-gh-release from 1 to 2
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-24 18:38:31 +02:00
Maschell 71406e2044 Add support for FS_LAYER_TYPE_SAVE_REPLACE_IGNORE_VOL_SAVE_COMMON 2024-04-24 18:38:31 +02:00
dependabot[bot] 12e102a4a7 Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-17 11:15:45 +01:00
Callum Parsey 0df67fc7d9 Support "wb" mode for opening files
Hachihachi (the Nintendo DS emulator used in Virtual Console) crashes
when its save data is redirected to the SD card. It tries to open the
save-state files in "wb" mode which is currently rejected by the
ContentRedirectionModule. This causes the emulator's `SAVEOpenFile()`
call to fail, and the emulator immediately panics.

I am operating under the assumption that the Wii U's I/O system
doesn't differentiate between text and binary modes, like most other
operating systems. This is evidenced by the fact that the module
already supports "rb" mode and treats it in the same way as "r".
2023-11-17 11:15:25 +01:00
Maschell f4f50748d7 Remove romfs usage 2023-11-17 11:15:05 +01:00
Maschell 98a137c91f Create dependabot.yml 2023-07-23 10:11:56 +02:00
Maschell ef29402e54 Bump version 2023-07-19 19:22:17 +02:00
Maschell 473b3bfb94 Update Dockerfile 2023-07-19 16:18:27 +02:00
Maschell b719da914d Update Dockerfile 2023-07-14 20:41:40 +02:00
Maschell 9e7299b65a Add missing imports 2023-07-14 20:41:40 +02:00
Maschell 96e8ddf97e Fix compiling with latest wut 2023-04-17 14:19:49 +02:00
Maschell 0bfa5be464 Change docker registry to ghcr.io 2023-03-18 16:25:22 +01:00
15 changed files with 71 additions and 31 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@ -9,15 +9,15 @@ jobs:
clang-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./src
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src
build-binary:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
@ -48,7 +48,7 @@ jobs:
- name: zip artifact
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms
- name: Create Release
uses: "softprops/action-gh-release@v1"
uses: "softprops/action-gh-release@v2"
with:
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
draft: false

View File

@ -6,15 +6,15 @@ jobs:
clang-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./src
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src
check-build-with-logging:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: build binary with logging
run: |
docker build . -t builder
@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ build/
cmake-build-debug/
CMakeLists.txt
*.wms
*.zip

View File

@ -1,8 +1,7 @@
FROM wiiuenv/devkitppc:20221228
FROM ghcr.io/wiiu-env/devkitppc:20240505
COPY --from=wiiuenv/libfunctionpatcher:20230106 /artifacts $DEVKITPRO
COPY --from=wiiuenv/wiiumodulesystem:20230106 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libromfs_wiiu:20220904 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libcontentredirection:20221010 /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/libcontentredirection:20240428 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -35,4 +35,4 @@ docker run -it --rm -v ${PWD}:/project contentredirectionmodule-builder make cle
## Format the code via docker
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./src -i`
`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src -i`

View File

@ -2,6 +2,7 @@
#include "FileUtils.h"
#include "utils/logger.h"
#include <coreinit/core.h>
#include <coreinit/thread.h>
#include <malloc.h>
static FSError processFSAShimInThread(FSAShimBuffer *shimBuffer) {

View File

@ -3,6 +3,7 @@
#include "utils/StringTools.h"
#include "utils/logger.h"
#include <coreinit/core.h>
#include <coreinit/thread.h>
FSStatus processFSError(FSError fsError, FSClient *client, FSErrorFlag errorMask) {
auto result = fsError >= 0 ? static_cast<FSStatus>(fsError) : fsaDecodeFsaStatusToFsStatus(fsError);
@ -559,11 +560,11 @@ function_replacement_data_t fs_file_function_replacements[] = {
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_VIA_ADDRESS_FOR_PROCESS(FSReadFileGeneric, 0x3201C400 + 0x4ecc0, 0x101C400 + 0x4ecc0, FP_TARGET_PROCESS_GAME_AND_MENU),
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_VIA_ADDRESS_FOR_PROCESS(FSWriteFileGeneric, 0x3201C400 + 0x4eec0, 0x101C400 + 0x4eec0, FP_TARGET_PROCESS_GAME_AND_MENU),
REPLACE_FUNCTION(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync),
REPLACE_FUNCTION(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync),
REPLACE_FUNCTION(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync),

View File

@ -83,7 +83,7 @@ FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *
struct dirent *entry_ = readdir(dir);
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);
continue;
}
@ -98,6 +98,9 @@ FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *
if (strcmp(entry_->d_name, ".") == 0 || strcmp(entry_->d_name, "..") == 0) {
entry->info.size = 0;
} else {
#ifdef _DIRENT_HAVE_D_STAT
translate_stat(&entry_->d_stat, &entry->info);
#else
struct stat sb {};
auto path = string_format("%s/%s", dirHandle->path, entry_->d_name);
std::replace(path.begin(), path.end(), '\\', '/');
@ -120,13 +123,14 @@ FSError FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *
result = FS_ERROR_MEDIA_ERROR;
break;
}
#endif
}
}
result = FS_ERROR_OK;
} else {
auto err = errno;
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;
}
}
@ -229,7 +233,7 @@ FSError FSWrapper::FSOpenFileWrapper(const char *path, const char *mode, FSFileH
_mode = O_RDONLY;
} else if (strcmp(mode, "r+") == 0) {
_mode = O_RDWR;
} else if (strcmp(mode, "w") == 0) {
} else if (strcmp(mode, "w") == 0 || strcmp(mode, "wb") == 0) {
_mode = O_WRONLY | O_CREAT | O_TRUNC;
} else if (strcmp(mode, "w+") == 0) {
_mode = O_RDWR | O_CREAT | O_TRUNC;
@ -313,7 +317,7 @@ FSError FSWrapper::FSCloseFileWrapper(FSFileHandle handle) {
bool FSWrapper::CheckFileShouldBeIgnored(std::string &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());
return true;
}
@ -659,6 +663,7 @@ bool FSWrapper::IsFileModeAllowed(const char *mode) {
if (pIsWriteable && (strcmp(mode, "r+") == 0 ||
strcmp(mode, "w") == 0 ||
strcmp(mode, "wb") == 0 ||
strcmp(mode, "w+") == 0 ||
strcmp(mode, "a") == 0 ||
strcmp(mode, "a+") == 0)) {
@ -668,8 +673,9 @@ bool FSWrapper::IsFileModeAllowed(const char *mode) {
return false;
}
bool FSWrapper::IsPathToReplace(const std::string_view &path) {
return path.starts_with(pPathToReplace);
return starts_with_case_insensitive(path, pPathToReplace);
}
std::string FSWrapper::GetNewPath(const std::string_view &path) {

View File

@ -1,4 +1,5 @@
#include "FSWrapperMergeDirsWithParent.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "utils/utils.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.
*/
if (std::string_view(entry->name).starts_with(deletePrefix)) {
if (starts_with_case_insensitive(entry->name, deletePrefix)) {
dirHandle->readResult[dirHandle->readResultNumberOfEntries].isMarkedAsDeleted = true;
OSMemoryBarrier();

View File

@ -6,6 +6,7 @@
#include "utils/utils.h"
#include <coreinit/cache.h>
#include <coreinit/filesystem_fsa.h>
#include <coreinit/thread.h>
#include <malloc.h>
#include <map>
#include <unistd.h>

View File

@ -7,7 +7,6 @@
#include <coreinit/filesystem_fsa.h>
#include <functional>
#include <mutex>
#include <romfs_dev.h>
#include <string>
struct FSIOThreadData {

View File

@ -3,11 +3,13 @@
#include "FileUtils.h"
#include "IFSWrapper.h"
#include "malloc.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <content_redirection/redirection.h>
#include <coreinit/dynload.h>
#include <mutex>
#include <nn/act.h>
#include <wums/exports.h>
struct AOCTitle {
@ -36,27 +38,27 @@ bool getAOCPath(std::string &outStr) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_Acquire failed");
return false;
}
if (OSDynLoad_FindExport(aoc_handle, false, "AOC_Initialize", reinterpret_cast<void **>(&AOC_Initialize)) != OS_DYNLOAD_OK) {
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_Initialize", reinterpret_cast<void **>(&AOC_Initialize)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
goto end;
}
if (OSDynLoad_FindExport(aoc_handle, false, "AOC_Finalize", reinterpret_cast<void **>(&AOC_Finalize)) != OS_DYNLOAD_OK) {
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_Finalize", reinterpret_cast<void **>(&AOC_Finalize)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
goto end;
}
if (OSDynLoad_FindExport(aoc_handle, false, "AOC_OpenTitle", reinterpret_cast<void **>(&AOC_OpenTitle)) != OS_DYNLOAD_OK) {
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_OpenTitle", reinterpret_cast<void **>(&AOC_OpenTitle)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
goto end;
}
if (OSDynLoad_FindExport(aoc_handle, false, "AOC_ListTitle", reinterpret_cast<void **>(&AOC_ListTitle)) != OS_DYNLOAD_OK) {
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_ListTitle", reinterpret_cast<void **>(&AOC_ListTitle)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
goto end;
}
if (OSDynLoad_FindExport(aoc_handle, false, "AOC_CalculateWorkBufferSize", reinterpret_cast<void **>(&AOC_CalculateWorkBufferSize)) != OS_DYNLOAD_OK) {
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_CalculateWorkBufferSize", reinterpret_cast<void **>(&AOC_CalculateWorkBufferSize)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
goto end;
}
if (OSDynLoad_FindExport(aoc_handle, false, "AOC_CloseTitle", reinterpret_cast<void **>(&AOC_CloseTitle)) != OS_DYNLOAD_OK) {
if (OSDynLoad_FindExport(aoc_handle, OS_DYNLOAD_EXPORT_FUNC, "AOC_CloseTitle", reinterpret_cast<void **>(&AOC_CloseTitle)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
goto end;
}
@ -115,6 +117,15 @@ ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *l
} else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE) {
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/save\" to \"%s\", mode: \"replace\"", replacementDir);
ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/save", replacementDir, false, true);
} else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE_FOR_CURRENT_USER) {
nn::act::Initialize();
nn::act::PersistentId persistentId = nn::act::GetPersistentId();
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 {
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;

View File

@ -10,7 +10,7 @@ WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection");
WUMS_USE_WUT_DEVOPTAB();
WUMS_DEPENDS_ON(homebrew_functionpatcher);
#define VERSION "v0.2.4"
#define VERSION "v0.2.7"
DECL_FUNCTION(void, OSCancelThread, OSThread *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);
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
}
}
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);
});
}