Compare commits

...

36 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
Maschell
9fc60b3189 Bump version to 0.2.4 2023-01-10 18:15:59 +01:00
Maschell
488df227eb Use the latest libfunctionpatcher version 2023-01-07 10:38:40 +01:00
Maschell
716deafb13 Update the CI to use a non-deprecated release action 2023-01-07 10:26:17 +01:00
Maschell
9f664abc6e Update CI to use actions/checkout@v3 2023-01-07 10:26:17 +01:00
Maschell
8a1c0bdb80 Update Dockerfile to use the latest WUMS version, add dependency to FunctionPatcherModule 2023-01-07 10:26:17 +01:00
Maschell
4300168ea4 Update version string 2022-10-11 15:12:28 +02:00
Maschell
eb0ff82425 Fix CRRemoveFSLayer return value 2022-10-11 14:44:10 +02:00
Maschell
f5266db613 Add support for /vol/aoc redirection 2022-10-10 20:11:57 +02:00
Maschell
8811d7ae0a Improve and fix logging 2022-10-10 20:11:57 +02:00
Maschell
ecd3cc03b2 Add missing FSAInit() call to fix directory merge with parents 2022-10-10 20:11:57 +02:00
Maschell
e46cf2c765 Update version to v0.2.2 2022-10-09 15:45:43 +02:00
Maschell
31045019c1 Improve commit messages for nightlies 2022-10-04 18:49:23 +02:00
Maschell
89c7fc26d8 Prevent calling OSCancelThread for ContentRedirection IO Threads 2022-10-04 18:49:23 +02:00
17 changed files with 210 additions and 62 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@v2
- 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@v2
- uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
@ -48,25 +48,12 @@ jobs:
- name: zip artifact
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: "softprops/action-gh-release@v2"
with:
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
draft: false
prerelease: true
body: |
Not a stable release:
${{ github.event.head_commit.message }}
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
asset_content_type: application/zip
generate_release_notes: true
name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
files: |
./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip

View File

@ -6,15 +6,15 @@ jobs:
clang-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- 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@v2
- 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@v2
- 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:20220917
FROM ghcr.io/wiiu-env/devkitppc:20240505
COPY --from=wiiuenv/libfunctionpatcher:20220904 /artifacts $DEVKITPRO
COPY --from=wiiuenv/wiiumodulesystem:20220904 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libromfs_wiiu:20220904 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libcontentredirection:20220916 /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

@ -37,7 +37,7 @@ CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld $(WUMSSPECS)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUMSSPECS)
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -g

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>
@ -28,12 +29,12 @@ FSError FSWrapperMergeDirsWithParent::FSOpenDirWrapper(const char *path,
if (clientHandle) {
FSADirectoryHandle realHandle = 0;
DEBUG_FUNCTION_LINE_ERR("[%s] Call FSAOpenDir with %s for parent layer", getName().c_str(), path);
// Call FSOpen with "this" as errorFlag call FSOpen for "parent" layers only.
if (FSAOpenDir(clientHandle, path, &realHandle) == FS_ERROR_OK) {
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) {
dirHandle->realDirHandle = realHandle;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Failed to open real dir %s", getName().c_str(), path);
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());
@ -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();
@ -99,7 +100,7 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSADirectoryHandle handle
FSADirectoryEntry realDirEntry;
FSError readDirResult;
while (true) {
DEBUG_FUNCTION_LINE_ERR("[%s] Call FSReadDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSReadDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
readDirResult = FSAReadDir(clientHandle, dirHandle->realDirHandle, &realDirEntry);
if (readDirResult == FS_ERROR_OK) {
bool found = false;
@ -128,7 +129,7 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSADirectoryHandle handle
res = FS_ERROR_END_OF_DIR;
break;
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] real_FSReadDir returned an unexpected error: %08X", getName().c_str(), readDirResult);
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;
}
@ -156,17 +157,19 @@ FSError FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSADirectoryHandle handl
auto dirHandle = getDirExFromHandle(handle);
if (dirHandle->realDirHandle != 0) {
if (clientHandle) {
DEBUG_FUNCTION_LINE_ERR("[%s] Call FSCloseDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSCloseDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
auto realResult = FSACloseDir(clientHandle, dirHandle->realDirHandle);
if (realResult == FS_ERROR_OK) {
dirHandle->realDirHandle = 0;
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close realDirHandle %d: res %d", getName().c_str(), dirHandle->realDirHandle, -1);
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());
}
if (dirHandle->readResult != nullptr) {
@ -199,15 +202,18 @@ FSError FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSADirectoryHandle hand
if (dirHandle->realDirHandle != 0) {
if (clientHandle) {
DEBUG_FUNCTION_LINE_ERR("[%s] Call FSARewindDir with %08X for parent layer", getName().c_str(), dirHandle->realDirHandle);
if (FSARewindDir(clientHandle, dirHandle->realDirHandle) == FS_ERROR_OK) {
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 {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to rewind dir for realDirHandle %08X", getName().c_str(), dirHandle->realDirHandle);
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();
}
@ -222,6 +228,7 @@ FSWrapperMergeDirsWithParent::FSWrapperMergeDirsWithParent(const std::string &na
replaceWithPath,
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<FSError>(clientHandle)), clientHandle);

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

@ -2,12 +2,94 @@
#include "FSWrapperMergeDirsWithParent.h"
#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 {
WUT_UNKNOWN_BYTES(0x68);
};
WUT_CHECK_SIZE(AOCTitle, 0x68);
bool getAOCPath(std::string &outStr) {
int32_t (*AOC_Initialize)() = nullptr;
int32_t (*AOC_Finalize)() = nullptr;
int32_t (*AOC_ListTitle)(uint32_t * titleCountOut, AOCTitle * titleList, uint32_t maxCount, void *workBuffer, uint32_t workBufferSize) = nullptr;
int32_t (*AOC_OpenTitle)(char *pathOut, AOCTitle *aocTitleInfo, void *workBuffer, uint32_t workBufferSize) = nullptr;
int32_t (*AOC_CalculateWorkBufferSize)(uint32_t count) = nullptr;
int32_t (*AOC_CloseTitle)(AOCTitle * aocTitleInfo) = nullptr;
AOCTitle title{};
char aocPath[256];
aocPath[0] = '\0';
uint32_t outCount = 0;
uint32_t workBufferSize = 0;
void *workBuffer = nullptr;
bool result = false;
OSDynLoad_Module aoc_handle;
if (OSDynLoad_Acquire("nn_aoc.rpl", &aoc_handle) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_Acquire failed");
return false;
}
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, 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, 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, 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, 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, OS_DYNLOAD_EXPORT_FUNC, "AOC_CloseTitle", reinterpret_cast<void **>(&AOC_CloseTitle)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_FindExport failed");
goto end;
}
AOC_Initialize();
workBufferSize = AOC_CalculateWorkBufferSize(1);
workBuffer = memalign(0x40, workBufferSize);
if (!workBuffer) {
DEBUG_FUNCTION_LINE_WARN("Failed to alloc workBuffer");
goto end;
}
if (AOC_ListTitle(&outCount, &title, 1, workBuffer, workBufferSize) < 0) {
DEBUG_FUNCTION_LINE_WARN("AOC_ListTitle failed");
goto end;
}
if (AOC_OpenTitle(aocPath, &title, workBuffer, workBufferSize) < 0) {
DEBUG_FUNCTION_LINE_WARN("AOC_OpenTitle failed");
goto end;
}
result = true;
outStr = aocPath;
AOC_CloseTitle(&title);
end:
free(workBuffer);
AOC_Finalize();
OSDynLoad_Release(aoc_handle);
return result;
}
ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType) {
if (!handle || layerName == nullptr || replacementDir == nullptr) {
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG");
@ -15,11 +97,35 @@ ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *l
}
std::unique_ptr<IFSWrapper> ptr;
if (layerType == FS_LAYER_TYPE_CONTENT_REPLACE) {
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/content\" to \"%s\", mode: \"replace\"", replacementDir);
ptr = make_unique_nothrow<FSWrapper>(layerName, "/vol/content", replacementDir, false, false);
} else if (layerType == FS_LAYER_TYPE_CONTENT_MERGE) {
DEBUG_FUNCTION_LINE_INFO("Redirecting \"/vol/content\" to \"%s\", mode: \"merge\"", replacementDir);
ptr = make_unique_nothrow<FSWrapperMergeDirsWithParent>(layerName, "/vol/content", replacementDir, true);
} else if (layerType == FS_LAYER_TYPE_AOC_MERGE || layerType == FS_LAYER_TYPE_AOC_REPLACE) {
std::string targetPath;
if (!getAOCPath(targetPath)) {
DEBUG_FUNCTION_LINE_ERR("(%s) Failed to get the AOC path. Not redirecting /vol/aoc", layerName);
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
}
DEBUG_FUNCTION_LINE_INFO("Redirecting \"%s\" to \"%s\", mode: \"%s\"", targetPath.c_str(), replacementDir, layerType == FS_LAYER_TYPE_AOC_MERGE ? "merge" : "replace");
if (layerType == FS_LAYER_TYPE_AOC_MERGE) {
ptr = make_unique_nothrow<FSWrapperMergeDirsWithParent>(layerName, targetPath.c_str(), replacementDir, true);
} else {
ptr = make_unique_nothrow<FSWrapper>(layerName, targetPath.c_str(), replacementDir, false, false);
}
} 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;
@ -36,7 +142,7 @@ ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *l
}
ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) {
if (remove_locked_first_if(fsLayerMutex, fsLayers, [handle](auto &cur) { return (CRLayerHandle) cur->getHandle() == handle; })) {
if (!remove_locked_first_if(fsLayerMutex, fsLayers, [handle](auto &cur) { return (CRLayerHandle) cur->getHandle() == handle; })) {
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
}

View File

@ -4,30 +4,48 @@
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "version.h"
#include <coreinit/cache.h>
#include <coreinit/core.h>
#include <malloc.h>
#include <wums.h>
WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection");
WUMS_USE_WUT_DEVOPTAB();
WUMS_DEPENDS_ON(homebrew_functionpatcher);
#define VERSION "v0.2.1"
#define VERSION "v0.2.7"
DECL_FUNCTION(void, OSCancelThread, OSThread *thread) {
if (thread == gThreadData[0].thread || thread == gThreadData[1].thread || thread == gThreadData[2].thread) {
DEBUG_FUNCTION_LINE_INFO("Prevent calling OSCancelThread for ContentRedirection IO Threads");
return;
}
real_OSCancelThread(thread);
}
function_replacement_data_t OSCancelThreadReplacement = REPLACE_FUNCTION(OSCancelThread, LIBRARY_COREINIT, OSCancelThread);
WUMS_INITIALIZE() {
initLogging();
DEBUG_FUNCTION_LINE("Patch functions");
if (FunctionPatcher_InitLibrary() != FUNCTION_PATCHER_RESULT_SUCCESS) {
OSFatal("homebrew_content_redirection: FunctionPatcher_InitLibrary failed");
}
bool wasPatched;
for (uint32_t i = 0; i < fs_file_function_replacements_size; i++) {
if (!FunctionPatcherPatchFunction(&fs_file_function_replacements[i], nullptr)) {
wasPatched = false;
if (FunctionPatcher_AddFunctionPatch(&fs_file_function_replacements[i], nullptr, &wasPatched) != FUNCTION_PATCHER_RESULT_SUCCESS || !wasPatched) {
OSFatal("homebrew_content_redirection: Failed to patch function");
}
}
for (uint32_t i = 0; i < fsa_file_function_replacements_size; i++) {
if (!FunctionPatcherPatchFunction(&fsa_file_function_replacements[i], nullptr)) {
wasPatched = false;
if (FunctionPatcher_AddFunctionPatch(&fsa_file_function_replacements[i], nullptr, &wasPatched) != FUNCTION_PATCHER_RESULT_SUCCESS || !wasPatched) {
OSFatal("homebrew_content_redirection: Failed to patch function");
}
}
wasPatched = false;
if (FunctionPatcher_AddFunctionPatch(&OSCancelThreadReplacement, nullptr, &wasPatched) != FUNCTION_PATCHER_RESULT_SUCCESS || !wasPatched) {
OSFatal("homebrew_content_redirection: Failed to patch OSCancelThreadReplacement");
}
DEBUG_FUNCTION_LINE("Patch functions finished");
deinitLogging();
}

View File

@ -12,3 +12,13 @@ std::string string_format(const std::string &format, Args... 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
}
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);
});
}

View File

@ -39,6 +39,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
@ -54,6 +55,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##INFO ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);