mirror of
https://github.com/wiiu-env/ContentRedirectionModule.git
synced 2024-11-10 21:55:15 +01:00
Compare commits
36 Commits
ContentRed
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
03ea0aceae | ||
|
45efb02d27 | ||
|
60b711cf81 | ||
|
884a3561d2 | ||
|
f9a4b537c8 | ||
|
835881abfa | ||
|
d0dfbd6d4e | ||
|
a2c8863cbe | ||
|
0d5bf8c78f | ||
|
9d0dbda1db | ||
|
75854a05e5 | ||
|
6c2748569d | ||
|
71406e2044 | ||
|
12e102a4a7 | ||
|
0df67fc7d9 | ||
|
f4f50748d7 | ||
|
98a137c91f | ||
|
ef29402e54 | ||
|
473b3bfb94 | ||
|
b719da914d | ||
|
9e7299b65a | ||
|
96e8ddf97e | ||
|
0bfa5be464 | ||
|
9fc60b3189 | ||
|
488df227eb | ||
|
716deafb13 | ||
|
9f664abc6e | ||
|
8a1c0bdb80 | ||
|
4300168ea4 | ||
|
eb0ff82425 | ||
|
f5266db613 | ||
|
8811d7ae0a | ||
|
ecd3cc03b2 | ||
|
e46cf2c765 | ||
|
31045019c1 | ||
|
89c7fc26d8 |
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
@ -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
|
8
.github/workflows/pr.yml
vendored
8
.github/workflows/pr.yml
vendored
@ -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
1
.gitignore
vendored
@ -8,3 +8,4 @@ build/
|
||||
cmake-build-debug/
|
||||
CMakeLists.txt
|
||||
*.wms
|
||||
*.zip
|
||||
|
@ -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
|
||||
|
2
Makefile
2
Makefile
@ -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
|
||||
|
@ -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`
|
@ -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) {
|
||||
|
@ -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),
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <romfs_dev.h>
|
||||
#include <string>
|
||||
|
||||
struct FSIOThreadData {
|
||||
|
108
src/export.cpp
108
src/export.cpp
@ -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;
|
||||
}
|
||||
|
32
src/main.cpp
32
src/main.cpp
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user