Compare commits

...

39 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
Maschell
f06f8ecc11 Bump the version 2022-09-19 22:21:40 +02:00
Maschell
ba76c15d1e Log the module version on each application start 2022-09-19 13:19:04 +02:00
Maschell
857461b735 Rewrite of almost everything to fix crashes and lags 2022-09-17 22:38:25 +02:00
30 changed files with 2353 additions and 1532 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,22 @@ 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")
cat <<EOF > ./src/version.h
#pragma once
#define VERSION_EXTRA " (nightly-$git_hash)"
EOF
- name: build binary
run: |
docker build . -t builder
@ -41,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,14 @@ 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 }}")
cat <<EOF > ./src/version.h
#pragma once
#define VERSION_EXTRA " (nightly-$git_hash)"
EOF
- name: build binary
run: |
docker build . -t builder

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:20220806
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:20220903 /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
@ -108,7 +108,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#-------------------------------------------------------------------------------

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

@ -1,9 +1,9 @@
#pragma once
#include "DirInfo.h"
#include <coreinit/filesystem.h>
#include <coreinit/filesystem_fsa.h>
typedef struct FSDirectoryEntryEx {
FSDirectoryEntry realEntry{};
FSADirectoryEntry realEntry{};
bool isMarkedAsDeleted = false;
} FSDirectoryEntryEx;

View File

@ -1,82 +0,0 @@
#include "FSDirReplacements.h"
#include "FileUtils.h"
#include "IFSWrapper.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <coreinit/filesystem.h>
DECL_FUNCTION(FSError, FSAOpenDir, FSAClientHandle client, const char *path, FSADirectoryHandle *dirHandle) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
return doForLayerFSA(
[c = client, p = path, h = dirHandle]() -> FSError {
return real_FSAOpenDir(c, p, h);
},
[p = path, h = dirHandle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSOpenDirWrapper(p, h);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAReadDir, FSAClientHandle client, FSADirectoryHandle dirHandle, FSADirectoryEntry *directoryEntry) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", dirHandle);
return doForLayerFSA(
[c = client, h = dirHandle, de = directoryEntry]() -> FSError {
return real_FSAReadDir(c, h, de);
},
[h = dirHandle, de = directoryEntry](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadDirWrapper(h, de);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSARewindDir, FSAClientHandle client, FSADirectoryHandle dirHandle) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", dirHandle);
return doForLayerFSA(
[c = client, h = dirHandle]() -> FSError {
return real_FSARewindDir(c, h);
},
[h = dirHandle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRewindDirWrapper(h);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSACloseDir, FSAClientHandle client, FSADirectoryHandle dirHandle) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", dirHandle);
return doForLayerFSA(
[c = client, h = dirHandle]() -> FSError {
return real_FSACloseDir(c, h);
},
[h = dirHandle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSCloseDirWrapper(h);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAMakeDir, FSAClientHandle client, const char *path, FSMode mode) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
return doForLayerFSA(
[c = client, p = path, m = mode]() -> FSError {
return real_FSAMakeDir(c, p, m);
},
[p = path, m = mode](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSMakeDirWrapper(p);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAChangeDir, FSAClientHandle client, const char *path) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
setWorkingDirForFSAClient(client, path);
return real_FSAChangeDir(client, path);
}
function_replacement_data_t fsa_dir_function_replacements[] = {
REPLACE_FUNCTION(FSAOpenDir, LIBRARY_COREINIT, FSAOpenDir),
REPLACE_FUNCTION(FSAReadDir, LIBRARY_COREINIT, FSAReadDir),
REPLACE_FUNCTION(FSARewindDir, LIBRARY_COREINIT, FSARewindDir),
REPLACE_FUNCTION(FSACloseDir, LIBRARY_COREINIT, FSACloseDir),
REPLACE_FUNCTION(FSAMakeDir, LIBRARY_COREINIT, FSAMakeDir),
REPLACE_FUNCTION(FSAChangeDir, LIBRARY_COREINIT, FSAChangeDir),
};
uint32_t fsa_dir_function_replacements_size = sizeof(fsa_dir_function_replacements) / sizeof(function_replacement_data_t);

View File

@ -1,10 +0,0 @@
#pragma once
#include "IFSWrapper.h"
#include <coreinit/filesystem.h>
#include <function_patcher/function_patching.h>
#include <functional>
#include <string>
extern function_replacement_data_t fsa_dir_function_replacements[];
extern uint32_t fsa_dir_function_replacements_size;

View File

@ -1,238 +0,0 @@
#include "FSFileReplacements.h"
#include "FileUtils.h"
#include "utils/logger.h"
#include <coreinit/filesystem_fsa.h>
DECL_FUNCTION(FSError, FSAOpenFileEx, FSAClientHandle client, const char *path, const char *mode, FSMode createMode, FSOpenFileFlags openFlag, uint32_t preallocSize, FSAFileHandle *handle) {
DEBUG_FUNCTION_LINE_VERBOSE("path: %s mode: %d", path, mode);
return doForLayerFSA(
[c = client, p = path, m = mode, cm = createMode, of = openFlag, pa = preallocSize, h = handle]() -> FSError {
return real_FSAOpenFileEx(c, p, m, cm, of, pa, h);
},
[f = getFullPathForFSAClient(client, path), m = mode, h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
// FSAFileHandle is just an alias for FSFileHandle
return layer->FSOpenFileWrapper(f.c_str(), m, h);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSACloseFile, FSAClientHandle client, FSAFileHandle fileHandle) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", fileHandle);
return doForLayerFSA(
[c = client, h = fileHandle]() -> FSError {
return real_FSACloseFile(c, h);
},
[h = fileHandle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
// FSAFileHandle is just an alias for FSFileHandle
return layer->FSCloseFileWrapper(h);
},
[h = fileHandle, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](std::unique_ptr<IFSWrapper> &layer, FSError res) -> FSError {
if (layer->isValidFileHandle(h)) {
layer->deleteFileHandle(h);
} else {
DEBUG_FUNCTION_LINE_ERR_LAMBDA(filename, func, line, "Expected to delete fileHandle by handle %08X but it was not found", h);
}
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Sync result %d", res);
return res;
});
}
DECL_FUNCTION(FSError, FSAFlushFile, FSAClientHandle client, FSAFileHandle fileHandle) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", fileHandle);
return doForLayerFSA(
[c = client, h = fileHandle]() -> FSError {
return real_FSAFlushFile(c, h);
},
[h = fileHandle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
// FSAFileHandle is just an alias for FSFileHandle
return layer->FSFlushFileWrapper(h);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAGetStat, FSAClientHandle client, const char *path, FSAStat *stat) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
return doForLayerFSA(
[c = client, p = path, s = stat]() -> FSError {
return real_FSAGetStat(c, p, s);
},
[f = getFullPathForFSAClient(client, path), s = stat](std::unique_ptr<IFSWrapper> &layer) -> FSError {
// FSAStat is just an alias for FSStat
return layer->FSGetStatWrapper(f.c_str(), s);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAGetStatFile, FSAClientHandle client, FSAFileHandle fileHandle, FSAStat *stat) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", fileHandle);
return doForLayerFSA(
[c = client, h = fileHandle, s = stat]() -> FSError {
return real_FSAGetStatFile(c, h, s);
},
[h = fileHandle, s = stat](std::unique_ptr<IFSWrapper> &layer) -> FSError {
// FSAFileHandle is just an alias for FSFileHandle
// FSAStat is just an alias for FSStat
return layer->FSGetStatFileWrapper(h, s);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSARemove, FSAClientHandle client, const char *path) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
return doForLayerFSA(
[c = client, p = path]() -> FSError {
return real_FSARemove(c, p);
},
[f = getFullPathForFSAClient(client, path)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRemoveWrapper(f.c_str());
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSARename, FSAClientHandle client, const char *oldPath, const char *newPath) {
DEBUG_FUNCTION_LINE_VERBOSE("%s %s", oldPath, newPath);
return doForLayerFSA(
[c = client, op = oldPath, np = newPath]() -> FSError {
return real_FSARename(c, op, np);
},
[op = getFullPathForFSAClient(client, oldPath), np = getFullPathForFSAClient(client, newPath)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRenameWrapper(op.c_str(), np.c_str());
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSASetPosFile, FSAClientHandle client, FSAFileHandle fileHandle, uint32_t pos) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X pos: %08X", fileHandle, pos);
return doForLayerFSA(
[c = client, h = fileHandle, p = pos]() -> FSError {
return real_FSASetPosFile(c, h, p);
},
[h = fileHandle, p = pos](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSSetPosFileWrapper(h, p);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSATruncateFile, FSAClientHandle client, FSAFileHandle fileHandle) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", fileHandle);
return doForLayerFSA(
[c = client, h = fileHandle]() -> FSError {
return real_FSATruncateFile(c, h);
},
[h = fileHandle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSTruncateFileWrapper(h);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAReadFile, FSAClientHandle client, void *buffer, uint32_t size, uint32_t count, FSAFileHandle handle, uint32_t flags) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X", handle, buffer, size * count);
return doForLayerFSA(
[c = client, b = buffer, s = size, co = count, h = handle, f = flags]() -> FSError {
return real_FSAReadFile(c, b, s, co, h, f);
},
[b = buffer, s = size, co = count, h = handle, f = flags](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadFileWrapper(b, s, co, h, f);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAWriteFile, FSAClientHandle client, void *buffer, uint32_t size, uint32_t count, FSAFileHandle handle, uint32_t flags) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X", handle, buffer, size * count);
return doForLayerFSA(
[c = client, b = buffer, s = size, co = count, h = handle, f = flags]() -> FSError {
return real_FSAWriteFile(c, b, s, co, h, f);
},
[b = buffer, s = size, co = count, h = handle, f = flags](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSWriteFileWrapper((uint8_t *) b, s, co, h, f);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAGetPosFile, FSAClientHandle client, FSAFileHandle handle, uint32_t *outPos) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", handle);
return doForLayerFSA(
[c = client, h = handle, o = outPos]() -> FSError {
return real_FSAGetPosFile(c, h, o);
},
[h = handle, o = outPos](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSGetPosFileWrapper(h, o);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAIsEof, FSAClientHandle client, FSAFileHandle handle) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", handle);
return doForLayerFSA(
[c = client, h = handle]() -> FSError {
return real_FSAIsEof(c, h);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSIsEofWrapper(h);
},
SYNC_RESULT_HANDLER_FSA);
}
DECL_FUNCTION(FSError, FSAFlushMultiQuota, FSAClientHandle client, const char *path) {
DEBUG_FUNCTION_LINE("NOT IMPLEMENTED. path %s", path);
return real_FSAFlushMultiQuota(client, path);
}
DECL_FUNCTION(FSError, FSAFlushQuota, FSAClientHandle client, const char *path) {
DEBUG_FUNCTION_LINE("NOT IMPLEMENTED. path %s", path);
return real_FSAFlushQuota(client, path);
}
DECL_FUNCTION(FSError, FSAChangeMode, FSAClientHandle client, const char *path, FSMode permission) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED path %s permission: %08X", path, permission);
return real_FSAChangeMode(client, path, permission);
}
DECL_FUNCTION(FSError, FSAOpenFileByStat, FSAClientHandle client, FSAStat *stat, const char *mode, const char *path, FSAFileHandle *outFileHandle) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return real_FSAOpenFileByStat(client, stat, mode, path, outFileHandle);
}
DECL_FUNCTION(FSError, FSAAppendFile, FSAClientHandle client, FSAFileHandle fileHandle, uint32_t size, uint32_t count) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return real_FSAAppendFile(client, fileHandle, size, count);
}
DECL_FUNCTION(FSError, FSAAppendFileEx, FSAClientHandle client, FSAFileHandle fileHandle, uint32_t size, uint32_t count, uint32_t flags) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return real_FSAAppendFileEx(client, fileHandle, size, count, flags);
}
DECL_FUNCTION(FSStatus, FSAWriteFileWithPos, FSAClientHandle client, uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, uint32_t flags) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED. handle %08X size %08X", handle, size * count);
return real_FSAWriteFileWithPos(client, buffer, size, count, pos, handle, flags);
}
function_replacement_data_t fsa_file_function_replacements[] = {
REPLACE_FUNCTION(FSAOpenFileEx, LIBRARY_COREINIT, FSAOpenFileEx),
REPLACE_FUNCTION(FSACloseFile, LIBRARY_COREINIT, FSACloseFile),
REPLACE_FUNCTION(FSAFlushFile, LIBRARY_COREINIT, FSAFlushFile),
REPLACE_FUNCTION(FSAGetStat, LIBRARY_COREINIT, FSAGetStat),
REPLACE_FUNCTION(FSAGetStatFile, LIBRARY_COREINIT, FSAGetStatFile),
REPLACE_FUNCTION(FSARemove, LIBRARY_COREINIT, FSARemove),
REPLACE_FUNCTION(FSARename, LIBRARY_COREINIT, FSARename),
REPLACE_FUNCTION(FSASetPosFile, LIBRARY_COREINIT, FSASetPosFile),
REPLACE_FUNCTION(FSATruncateFile, LIBRARY_COREINIT, FSATruncateFile),
REPLACE_FUNCTION(FSAReadFile, LIBRARY_COREINIT, FSAReadFile),
REPLACE_FUNCTION(FSAWriteFile, LIBRARY_COREINIT, FSAWriteFile),
REPLACE_FUNCTION(FSAGetPosFile, LIBRARY_COREINIT, FSAGetPosFile),
REPLACE_FUNCTION(FSAIsEof, LIBRARY_COREINIT, FSAIsEof),
REPLACE_FUNCTION(FSAFlushMultiQuota, LIBRARY_COREINIT, FSAFlushMultiQuota),
REPLACE_FUNCTION(FSAFlushQuota, LIBRARY_COREINIT, FSAFlushQuota),
REPLACE_FUNCTION(FSAChangeMode, LIBRARY_COREINIT, FSAChangeMode),
REPLACE_FUNCTION(FSAOpenFileByStat, LIBRARY_COREINIT, FSAOpenFileByStat),
REPLACE_FUNCTION(FSAAppendFile, LIBRARY_COREINIT, FSAAppendFile),
REPLACE_FUNCTION(FSAAppendFileEx, LIBRARY_COREINIT, FSAAppendFileEx),
REPLACE_FUNCTION(FSAWriteFileWithPos, LIBRARY_COREINIT, FSAWriteFileWithPos),
};
uint32_t fsa_file_function_replacements_size = sizeof(fsa_file_function_replacements) / sizeof(function_replacement_data_t);

732
src/FSAReplacements.cpp Normal file
View File

@ -0,0 +1,732 @@
#include "FSAReplacements.h"
#include "FileUtils.h"
#include "utils/logger.h"
#include <coreinit/core.h>
#include <coreinit/thread.h>
#include <malloc.h>
static FSError processFSAShimInThread(FSAShimBuffer *shimBuffer) {
FSError res;
if (gThreadsRunning) {
auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper));
if (param == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for FSShimWrapper");
OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper");
}
param->api = FS_SHIM_API_FSA;
param->sync = FS_SHIM_TYPE_SYNC;
param->shim = shimBuffer;
if (OSGetCurrentThread() == gThreadData[OSGetCoreId()].thread) {
res = processShimBufferForFSA(param);
//No need to clean "param", it has been already free'd in processFSAShimBuffer.
} else {
auto message = (FSShimWrapperMessage *) malloc(sizeof(FSShimWrapperMessage));
if (message == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for FSShimWrapperMessage");
OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapperMessage");
}
message->param = param;
constexpr int32_t messageSize = sizeof(message->messages) / sizeof(message->messages[0]);
OSInitMessageQueue(&message->messageQueue, message->messages, messageSize);
if (!sendMessageToThread(message)) {
DEBUG_FUNCTION_LINE_ERR("Failed to send message to thread");
OSFatal("ContentRedirectionModule: Failed send message to thread");
}
OSMessage recv;
if (!OSReceiveMessage(&message->messageQueue, &recv, OS_MESSAGE_FLAGS_BLOCKING)) {
DEBUG_FUNCTION_LINE_ERR("Failed to receive message");
OSFatal("ContentRedirectionModule: Failed to receive message");
}
if (recv.args[0] != FS_IO_QUEUE_SYNC_RESULT) {
DEBUG_FUNCTION_LINE_ERR("ContentRedirection: Unexpected message in message queue.");
OSFatal("ContentRedirection: Unexpected message in message queue.");
}
res = (FSError) recv.args[1];
// We only need to clean up "message". "param" has already been free'd by the other thread.
free(message);
}
} else {
res = FS_ERROR_FORCE_REAL_FUNCTION;
DEBUG_FUNCTION_LINE_WARN("Threads are not running yet, skip replacement");
}
return res;
}
DECL_FUNCTION(FSError, FSAOpenFileEx, FSAClientHandle client, const char *path, const char *mode, FSMode createMode, FSOpenFileFlags openFlag, uint32_t preallocSize, FSAFileHandle *handle) {
if (handle == nullptr) {
DEBUG_FUNCTION_LINE_WARN("handle is null.");
return FS_ERROR_INVALID_BUFFER;
}
*handle = -1;
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestOpenFile(shimBuffer, client, path, mode, createMode, openFlag, preallocSize);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestOpenFile failed");
return res;
}
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) handle;
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAOpenFileEx" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAOpenFileEx(client, path, mode, createMode, openFlag, preallocSize, handle);
}
DECL_FUNCTION(FSError, FSAOpenFile, FSAClientHandle client, const char *path, const char *mode, FSAFileHandle *handle) {
if (handle == nullptr) {
DEBUG_FUNCTION_LINE_WARN("handle is null.");
return FS_ERROR_INVALID_BUFFER;
}
*handle = -1;
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestOpenFile(shimBuffer, client, path, mode, static_cast<FSMode>(0x660), static_cast<FSOpenFileFlags>(0), 0);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestOpenFile failed");
return res;
}
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) handle;
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAOpenFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAOpenFile(client, path, mode, handle);
}
DECL_FUNCTION(FSError, FSACloseFile, FSAClientHandle client, FSAFileHandle handle) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestCloseFile(shimBuffer, client, handle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestCloseFile failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSACloseFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSACloseFile(client, handle);
}
DECL_FUNCTION(FSError, FSAFlushFile, FSAClientHandle client, FSAFileHandle handle) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestFlushFile(shimBuffer, client, handle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestFlushFile failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAFlushFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAFlushFile(client, handle);
}
DECL_FUNCTION(FSError, FSAGetStat, FSAClientHandle client, const char *path, FSAStat *stat) {
if (stat == nullptr) {
DEBUG_FUNCTION_LINE_WARN("stat is null.");
return FS_ERROR_INVALID_BUFFER;
}
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestQueryInfo(shimBuffer, client, path, FSA_QUERY_INFO_STAT);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) stat;
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAGetStat" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAGetStat(client, path, stat);
}
DECL_FUNCTION(FSError, FSAGetStatFile, FSAClientHandle client, FSAFileHandle handle, FSAStat *stat) {
if (stat == nullptr) {
DEBUG_FUNCTION_LINE_WARN("stat is null.");
return FS_ERROR_INVALID_BUFFER;
}
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestStatFile(shimBuffer, client, handle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) stat;
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAGetStatFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAGetStatFile(client, handle, stat);
}
DECL_FUNCTION(FSError, FSARemove, FSAClientHandle client, const char *path) {
if (path == nullptr) {
DEBUG_FUNCTION_LINE_WARN("path is null.");
return FS_ERROR_INVALID_PARAM;
}
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestRemove(shimBuffer, client, path);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSARemove" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSARemove(client, path);
}
DECL_FUNCTION(FSError, FSARename, FSAClientHandle client, const char *oldPath, const char *newPath) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestRename(shimBuffer, client, oldPath, newPath);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSARename" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSARename(client, oldPath, newPath);
}
DECL_FUNCTION(FSError, FSASetPosFile, FSAClientHandle client, FSAFileHandle handle, FSAFilePosition pos) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestSetPos(shimBuffer, client, handle, pos);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSASetPosFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSASetPosFile(client, handle, pos);
}
DECL_FUNCTION(FSError, FSATruncateFile, FSAClientHandle client, FSAFileHandle handle) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestTruncate(shimBuffer, client, handle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSATruncateFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSATruncateFile(client, handle);
}
DECL_FUNCTION(FSError, FSAReadFile, FSAClientHandle client, uint8_t *buffer, uint32_t size, uint32_t count, FSAFileHandle handle, uint32_t flags) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestReadFile(shimBuffer, client, buffer, size, count, 0, handle, static_cast<FSAReadFlag>(flags & 0xfffffffe));
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAReadFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAReadFile(client, buffer, size, count, handle, flags);
}
DECL_FUNCTION(FSError, FSAReadFileWithPos, FSAClientHandle client, uint8_t *buffer, uint32_t size, uint32_t count, FSAFilePosition pos, FSAFileHandle handle, uint32_t flags) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestReadFile(shimBuffer, client, buffer, size, count, pos, handle, static_cast<FSAReadFlag>(flags | FSA_READ_FLAG_READ_WITH_POS));
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAReadFileWithPos" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAReadFileWithPos(client, buffer, size, count, pos, handle, flags);
}
DECL_FUNCTION(FSError, FSAWriteFile, FSAClientHandle client, const uint8_t *buffer, uint32_t size, uint32_t count, FSAFileHandle handle, uint32_t flags) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestWriteFile(shimBuffer, client, buffer, size, count, 0, handle, static_cast<FSAWriteFlag>(flags & 0xfffffffe));
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAWriteFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAWriteFile(client, buffer, size, count, handle, flags);
}
DECL_FUNCTION(FSError, FSAWriteFileWithPos, FSAClientHandle client, uint8_t *buffer, uint32_t size, uint32_t count, FSAFilePosition pos, FSAFileHandle handle, uint32_t flags) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestWriteFile(shimBuffer, client, buffer, size, count, pos, handle, static_cast<FSAWriteFlag>(flags | FSA_WRITE_FLAG_READ_WITH_POS));
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAWriteFileWithPos" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAWriteFileWithPos(client, buffer, size, count, pos, handle, flags);
}
DECL_FUNCTION(FSError, FSAGetPosFile, FSAClientHandle client, FSAFileHandle handle, FSAFilePosition *outPos) {
if (outPos == nullptr) {
DEBUG_FUNCTION_LINE_WARN("outPos is null.");
return FS_ERROR_INVALID_BUFFER;
}
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestGetPos(shimBuffer, client, handle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) outPos;
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAGetPosFile" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAGetPosFile(client, handle, outPos);
}
DECL_FUNCTION(FSError, FSAIsEof, FSAClientHandle client, FSAFileHandle handle) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestIsEof(shimBuffer, client, handle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAIsEof" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAIsEof(client, handle);
}
DECL_FUNCTION(FSError, FSAFlushMultiQuota, FSAClientHandle client, const char *path) {
DEBUG_FUNCTION_LINE("NOT IMPLEMENTED. path %s", path);
return real_FSAFlushMultiQuota(client, path);
}
DECL_FUNCTION(FSError, FSAFlushQuota, FSAClientHandle client, const char *path) {
DEBUG_FUNCTION_LINE("NOT IMPLEMENTED. path %s", path);
return real_FSAFlushQuota(client, path);
}
DECL_FUNCTION(FSError, FSAChangeMode, FSAClientHandle client, const char *path, FSMode permission) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED path %s permission: %08X", path, permission);
return real_FSAChangeMode(client, path, permission);
}
DECL_FUNCTION(FSError, FSAOpenFileByStat, FSAClientHandle client, FSAStat *stat, const char *mode, const char *path, FSAFileHandle *outFileHandle) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return real_FSAOpenFileByStat(client, stat, mode, path, outFileHandle);
}
DECL_FUNCTION(FSError, FSAAppendFile, FSAClientHandle client, FSAFileHandle fileHandle, uint32_t size, uint32_t count) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return real_FSAAppendFile(client, fileHandle, size, count);
}
DECL_FUNCTION(FSError, FSAAppendFileEx, FSAClientHandle client, FSAFileHandle fileHandle, uint32_t size, uint32_t count, uint32_t flags) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED");
return real_FSAAppendFileEx(client, fileHandle, size, count, flags);
}
DECL_FUNCTION(FSError, FSAOpenDir, FSAClientHandle client, const char *path, FSADirectoryHandle *dirHandle) {
if (dirHandle == nullptr) {
DEBUG_FUNCTION_LINE_WARN("handle is null.");
return FS_ERROR_INVALID_BUFFER;
}
*dirHandle = -1;
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestOpenDir(shimBuffer, client, path);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) dirHandle;
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAOpenDir" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAOpenDir(client, path, dirHandle);
}
DECL_FUNCTION(FSError, FSAReadDir, FSAClientHandle client, FSADirectoryHandle dirHandle, FSADirectoryEntry *directoryEntry) {
if (directoryEntry == nullptr) {
DEBUG_FUNCTION_LINE_WARN("handle is null.");
return FS_ERROR_INVALID_BUFFER;
}
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestReadDir(shimBuffer, client, dirHandle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) directoryEntry;
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAOpenDir" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAReadDir(client, dirHandle, directoryEntry);
}
DECL_FUNCTION(FSError, FSARewindDir, FSAClientHandle client, FSADirectoryHandle dirHandle) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestRewindDir(shimBuffer, client, dirHandle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAOpenDir" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSARewindDir(client, dirHandle);
}
DECL_FUNCTION(FSError, FSACloseDir, FSAClientHandle client, FSADirectoryHandle dirHandle) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestCloseDir(shimBuffer, client, dirHandle);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSACloseDir" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSACloseDir(client, dirHandle);
}
DECL_FUNCTION(FSError, FSAMakeDir, FSAClientHandle client, const char *path, FSMode mode) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestMakeDir(shimBuffer, client, path, mode);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAMakeDir" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAMakeDir(client, path, mode);
}
DECL_FUNCTION(FSError, FSAChangeDir, FSAClientHandle client, const char *path) {
auto *shimBuffer = (FSAShimBuffer *) memalign(0x20, sizeof(FSAShimBuffer));
if (!shimBuffer) {
DEBUG_FUNCTION_LINE_WARN("FS_ERROR_OUT_OF_RESOURCES");
return FS_ERROR_OUT_OF_RESOURCES;
}
auto res = fsaShimPrepareRequestChangeDir(shimBuffer, client, path);
if (res != FS_ERROR_OK) {
free(shimBuffer);
DEBUG_FUNCTION_LINE_WARN("fsaShimPrepareRequestQueryInfo failed");
return res;
}
res = processFSAShimInThread(shimBuffer);
free(shimBuffer);
if (res != FS_ERROR_FORCE_REAL_FUNCTION) {
return res;
}
// Other plugins/modules may override this function as well, so we need to call "real_FSAChangeDir" instead of using
// the existing shimBuffer (which would be more efficient).
return real_FSAChangeDir(client, path);
}
function_replacement_data_t fsa_file_function_replacements[] = {
REPLACE_FUNCTION(FSAOpenFile, LIBRARY_COREINIT, FSAOpenFile),
REPLACE_FUNCTION(FSAOpenFileEx, LIBRARY_COREINIT, FSAOpenFileEx),
REPLACE_FUNCTION(FSACloseFile, LIBRARY_COREINIT, FSACloseFile),
REPLACE_FUNCTION(FSAFlushFile, LIBRARY_COREINIT, FSAFlushFile),
REPLACE_FUNCTION(FSAGetStat, LIBRARY_COREINIT, FSAGetStat),
REPLACE_FUNCTION(FSAGetStatFile, LIBRARY_COREINIT, FSAGetStatFile),
REPLACE_FUNCTION(FSARemove, LIBRARY_COREINIT, FSARemove),
REPLACE_FUNCTION(FSARename, LIBRARY_COREINIT, FSARename),
REPLACE_FUNCTION(FSASetPosFile, LIBRARY_COREINIT, FSASetPosFile),
REPLACE_FUNCTION(FSATruncateFile, LIBRARY_COREINIT, FSATruncateFile),
REPLACE_FUNCTION(FSAReadFile, LIBRARY_COREINIT, FSAReadFile),
REPLACE_FUNCTION(FSAReadFileWithPos, LIBRARY_COREINIT, FSAReadFileWithPos),
REPLACE_FUNCTION(FSAWriteFile, LIBRARY_COREINIT, FSAWriteFile),
REPLACE_FUNCTION(FSAWriteFileWithPos, LIBRARY_COREINIT, FSAWriteFileWithPos),
REPLACE_FUNCTION(FSAGetPosFile, LIBRARY_COREINIT, FSAGetPosFile),
REPLACE_FUNCTION(FSAIsEof, LIBRARY_COREINIT, FSAIsEof),
REPLACE_FUNCTION(FSAFlushMultiQuota, LIBRARY_COREINIT, FSAFlushMultiQuota),
REPLACE_FUNCTION(FSAFlushQuota, LIBRARY_COREINIT, FSAFlushQuota),
REPLACE_FUNCTION(FSAChangeMode, LIBRARY_COREINIT, FSAChangeMode),
REPLACE_FUNCTION(FSAOpenFileByStat, LIBRARY_COREINIT, FSAOpenFileByStat),
REPLACE_FUNCTION(FSAAppendFile, LIBRARY_COREINIT, FSAAppendFile),
REPLACE_FUNCTION(FSAAppendFileEx, LIBRARY_COREINIT, FSAAppendFileEx),
REPLACE_FUNCTION(FSAOpenDir, LIBRARY_COREINIT, FSAOpenDir),
REPLACE_FUNCTION(FSAReadDir, LIBRARY_COREINIT, FSAReadDir),
REPLACE_FUNCTION(FSARewindDir, LIBRARY_COREINIT, FSARewindDir),
REPLACE_FUNCTION(FSACloseDir, LIBRARY_COREINIT, FSACloseDir),
REPLACE_FUNCTION(FSAMakeDir, LIBRARY_COREINIT, FSAMakeDir),
REPLACE_FUNCTION(FSAChangeDir, LIBRARY_COREINIT, FSAChangeDir),
};
uint32_t fsa_file_function_replacements_size = sizeof(fsa_file_function_replacements) / sizeof(function_replacement_data_t);
FSError processShimBufferForFSA(FSShimWrapper *param) {
auto res = doForLayer(param);
if (res == FS_ERROR_FORCE_REAL_FUNCTION) {
if (param->sync == FS_SHIM_TYPE_ASYNC) {
DEBUG_FUNCTION_LINE_ERR("ASYNC FSA API is not supported");
OSFatal("ContentRedirectionModule: ASYNC FSA API is not supported");
}
}
free(param);
return res;
}

View File

@ -1,219 +0,0 @@
#include "FSDirReplacements.h"
#include "FileUtils.h"
#include "IFSWrapper.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <coreinit/filesystem.h>
DECL_FUNCTION(FSStatus, FSOpenDir, FSClient *client, FSCmdBlock *block, const char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSOpenDir(client, block, path, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, p = path](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSOpenDir(c, b, p, h, realErrorMask);
},
[f = getFullPathForFSClient(client, path), h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSOpenDirWrapper(f.c_str(), h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSOpenDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSOpenDirAsync(client, block, path, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, p = path, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSOpenDirAsync(c, b, p, h, realErrorMask, a);
},
[f = getFullPathForFSClient(client, path), h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSOpenDirWrapper(f.c_str(), h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSReadDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, [[maybe_unused]] FSDirectoryEntry *entry, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("", errorMask);
if (isForceRealFunction(errorMask)) {
return real_FSReadDir(client, block, handle, entry, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, e = entry](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSReadDir(c, b, h, e, realErrorMask);
},
[h = handle, e = entry](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadDirWrapper(h, e);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSReadDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE();
if (isForceRealFunction(errorMask)) {
return real_FSReadDirAsync(client, block, handle, entry, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, e = entry, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSReadDirAsync(c, b, h, e, realErrorMask, a);
},
[h = handle, e = entry](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadDirWrapper(h, e);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSCloseDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("");
if (isForceRealFunction(errorMask)) {
return real_FSCloseDir(client, block, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSCloseDir(c, b, h, realErrorMask);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSCloseDirWrapper(h);
},
[h = handle, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](std::unique_ptr<IFSWrapper> &layer, FSStatus res) -> FSStatus {
if (layer->isValidDirHandle(h)) {
layer->deleteDirHandle(h);
} else {
DEBUG_FUNCTION_LINE_ERR_LAMBDA(filename, func, line, "Expected to delete dirHandle by %08X but it was not found", h);
}
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Sync result %d", res);
return res;
});
}
DECL_FUNCTION(FSStatus, FSCloseDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE();
if (isForceRealFunction(errorMask)) {
return real_FSCloseDirAsync(client, block, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSCloseDirAsync(c, b, h, realErrorMask, a);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSCloseDirWrapper(h);
},
[c = client, b = block, h = handle, a = asyncData, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](std::unique_ptr<IFSWrapper> &layer, FSStatus res) -> FSStatus {
if (layer->isValidDirHandle(h)) {
layer->deleteDirHandle(h);
} else {
DEBUG_FUNCTION_LINE_ERR_LAMBDA(filename, func, line, "Expected to delete dirHandle by %08X but it was not found", h);
}
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Async result %d", res);
return send_result_async(c, b, a, res);
});
}
DECL_FUNCTION(FSStatus, FSRewindDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("");
if (isForceRealFunction(errorMask)) {
return real_FSRewindDir(client, block, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSRewindDir(c, b, h, realErrorMask);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRewindDirWrapper(h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRewindDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE();
if (isForceRealFunction(errorMask)) {
return real_FSRewindDirAsync(client, block, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSRewindDirAsync(c, b, h, realErrorMask, a);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRewindDirWrapper(h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSMakeDir, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSMakeDir(client, block, path, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSMakeDir(c, b, p, realErrorMask);
},
[f = getFullPathForFSClient(client, path)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSMakeDirWrapper(f.c_str());
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSMakeDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSMakeDirAsync(client, block, path, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSMakeDirAsync(c, b, p, realErrorMask, a);
},
[f = getFullPathForFSClient(client, path)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSMakeDirWrapper(f.c_str());
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSChangeDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("FSChangeDirAsync %s", path);
setWorkingDirForFSClient(client, path);
return real_FSChangeDirAsync(client, block, path, errorMask, asyncData);
}
function_replacement_data_t fs_dir_function_replacements[] = {
REPLACE_FUNCTION(FSOpenDir, LIBRARY_COREINIT, FSOpenDir),
REPLACE_FUNCTION(FSOpenDirAsync, LIBRARY_COREINIT, FSOpenDirAsync),
REPLACE_FUNCTION(FSReadDir, LIBRARY_COREINIT, FSReadDir),
REPLACE_FUNCTION(FSReadDirAsync, LIBRARY_COREINIT, FSReadDirAsync),
REPLACE_FUNCTION(FSCloseDir, LIBRARY_COREINIT, FSCloseDir),
REPLACE_FUNCTION(FSCloseDirAsync, LIBRARY_COREINIT, FSCloseDirAsync),
REPLACE_FUNCTION(FSRewindDir, LIBRARY_COREINIT, FSRewindDir),
REPLACE_FUNCTION(FSRewindDirAsync, LIBRARY_COREINIT, FSRewindDirAsync),
REPLACE_FUNCTION(FSMakeDir, LIBRARY_COREINIT, FSMakeDir),
REPLACE_FUNCTION(FSMakeDirAsync, LIBRARY_COREINIT, FSMakeDirAsync),
REPLACE_FUNCTION(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync),
};
uint32_t fs_dir_function_replacements_size = sizeof(fs_dir_function_replacements) / sizeof(function_replacement_data_t);

View File

@ -1,10 +0,0 @@
#pragma once
#include "IFSWrapper.h"
#include <coreinit/filesystem.h>
#include <function_patcher/function_patching.h>
#include <functional>
#include <string>
extern function_replacement_data_t fs_dir_function_replacements[];
extern uint32_t fs_dir_function_replacements_size;

View File

@ -1,616 +0,0 @@
#include "FSFileReplacements.h"
#include "FileUtils.h"
#include "utils/logger.h"
DECL_FUNCTION(FSStatus, FSOpenFileEx, FSClient *client, FSCmdBlock *block, const char *path, const char *mode, FSMode createMode, FSOpenFileFlags openFlag, uint32_t preallocSize, FSFileHandle *handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSOpenFileEx(client, block, path, mode, createMode, openFlag, preallocSize, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, m = mode, cm = createMode, of = openFlag, pa = preallocSize, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSOpenFileEx(c, b, p, m, cm, of, pa, h, realErrorMask);
},
[f = getFullPathForFSClient(client, path), m = mode, h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSOpenFileWrapper(f.c_str(), m, h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSOpenFileExAsync, FSClient *client, FSCmdBlock *block, const char *path, const char *mode, FSMode createMode, FSOpenFileFlags openFlag, uint32_t preallocSize, FSFileHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSOpenFileExAsync(client, block, path, mode, createMode, openFlag, preallocSize, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, m = mode, cm = createMode, of = openFlag, pa = preallocSize, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSOpenFileExAsync(c, b, p, m, cm, of, pa, h, realErrorMask, a);
},
[p = getFullPathForFSClient(client, path), m = mode, h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSOpenFileWrapper(p.c_str(), m, h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, FSFileHandle *handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSOpenFile(client, block, path, mode, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, m = mode, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSOpenFile(c, b, p, m, h, realErrorMask);
},
[f = getFullPathForFSClient(client, path), m = mode, h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSOpenFileWrapper(f.c_str(), m, h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSOpenFileAsync, FSClient *client, FSCmdBlock *block, const char *path, const char *mode, FSFileHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSOpenFileAsync(client, block, path, mode, handle, errorMask, asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, m = mode, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSOpenFileAsync(c, b, p, m, h, realErrorMask, a);
},
[p = getFullPathForFSClient(client, path), m = mode, h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSOpenFileWrapper(p.c_str(), m, h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSCloseFile(client, block, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSCloseFile(c, b, h, realErrorMask);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSCloseFileWrapper(h);
},
[h = handle, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](std::unique_ptr<IFSWrapper> &layer, FSStatus res) -> FSStatus {
if (layer->isValidFileHandle(h)) {
layer->deleteFileHandle(h);
} else {
DEBUG_FUNCTION_LINE_ERR_LAMBDA(filename, func, line, "Expected to delete fileHandle by handle %08X but it was not found", h);
}
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Sync result %d", res);
return res;
});
}
DECL_FUNCTION(FSStatus, FSCloseFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSCloseFileAsync(client, block, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSCloseFileAsync(c, b, h, realErrorMask, a);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSCloseFileWrapper(h);
},
[c = client, b = block, h = handle, a = asyncData, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](std::unique_ptr<IFSWrapper> &layer, FSStatus res) -> FSStatus {
if (layer->isValidFileHandle(h)) {
layer->deleteFileHandle(h);
} else {
DEBUG_FUNCTION_LINE_ERR_LAMBDA(filename, func, line, "Expected to delete fileHandle by handle %08X but it was not found", h);
}
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Async result %d", res);
return send_result_async(c, b, a, res);
});
}
DECL_FUNCTION(FSStatus, FSGetStat, FSClient *client, FSCmdBlock *block, const char *path, FSStat *stats, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSGetStat(client, block, path, stats, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, s = stats](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSGetStat(c, b, p, s, realErrorMask);
},
[p = getFullPathForFSClient(client, path), s = stats](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSGetStatWrapper(p.c_str(), s);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSGetStatAsync, FSClient *client, FSCmdBlock *block, const char *path, FSStat *stats, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSGetStatAsync(client, block, path, stats, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, s = stats, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSGetStatAsync(c, b, p, s, realErrorMask, a);
},
[p = getFullPathForFSClient(client, path), s = stats](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSGetStatWrapper(p.c_str(), s);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSGetStatFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSStat *stats, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSGetStatFile(client, block, handle, stats, getRealErrorFlag(errorMask));
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, s = stats](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSGetStatFile(c, b, h, s, realErrorMask);
},
[h = handle, s = stats](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSGetStatFileWrapper(h, s);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSGetStatFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSStat *stats, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSGetStatFileAsync(client, block, handle, stats, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, s = stats, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSGetStatFileAsync(c, b, h, s, realErrorMask, a);
},
[h = handle, s = stats](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSGetStatFileWrapper(h, s);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSReadFile, FSClient *client, FSCmdBlock *block, void *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X", handle, buffer, size * count);
if (isForceRealFunction(errorMask)) {
return real_FSReadFile(client, block, buffer, size, count, handle, unk1, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, s = size, co = count, bu = buffer, u = unk1](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSReadFile(c, b, bu, s, co, h, u, realErrorMask);
},
[b = buffer, s = size, c = count, h = handle, u = unk1](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadFileWrapper(b, s, c, h, u);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSReadFileAsync, FSClient *client, FSCmdBlock *block, void *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, FSErrorFlag errorMask,
FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X", handle, buffer, size * count);
if (isForceRealFunction(errorMask)) {
return real_FSReadFileAsync(client, block, buffer, size, count, handle, unk1, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, s = size, co = count, bu = buffer, u = unk1, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSReadFileAsync(c, b, bu, s, co, h, u, realErrorMask, a);
},
[b = buffer, s = size, c = count, h = handle, u = unk1](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadFileWrapper(b, s, c, h, u);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSReadFileWithPos, FSClient *client, FSCmdBlock *block, void *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, uint32_t unk1, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X pos: %08X", handle, buffer, size * count, pos);
if (isForceRealFunction(errorMask)) {
return real_FSReadFileWithPos(client, block, buffer, size, count, pos, handle, unk1, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, s = size, co = count, p = pos, bu = buffer, u = unk1](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSReadFileWithPos(c, b, bu, s, co, p, h, u, realErrorMask);
},
[b = buffer, s = size, c = count, p = pos, h = handle, u = unk1](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadFileWithPosWrapper(b, s, c, p, h, u);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSReadFileWithPosAsync, FSClient *client, FSCmdBlock *block, void *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, int32_t unk1, FSErrorFlag errorMask,
FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X pos: %08X", handle, buffer, size * count, pos);
if (isForceRealFunction(errorMask)) {
return real_FSReadFileWithPosAsync(client, block, buffer, size, count, pos, handle, unk1, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, s = size, co = count, p = pos, bu = buffer, u = unk1, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSReadFileWithPosAsync(c, b, bu, s, co, p, h, u, realErrorMask, a);
},
[b = buffer, s = size, c = count, p = pos, h = handle, u = unk1](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSReadFileWithPosWrapper(b, s, c, p, h, u);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSSetPosFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t pos, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X pos: %08X", handle, pos);
if (isForceRealFunction(errorMask)) {
return real_FSSetPosFile(client, block, handle, pos, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, p = pos](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSSetPosFile(c, b, h, p, realErrorMask);
},
[h = handle, p = pos](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSSetPosFileWrapper(h, p);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSSetPosFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t pos, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X pos: %08X", handle, pos);
if (isForceRealFunction(errorMask)) {
return real_FSSetPosFileAsync(client, block, handle, pos, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, p = pos, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSSetPosFileAsync(c, b, h, p, realErrorMask, a);
},
[h = handle, p = pos](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSSetPosFileWrapper(h, p);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSGetPosFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t *pos, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSGetPosFile(client, block, handle, pos, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, p = pos](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSGetPosFile(c, b, h, p, realErrorMask);
},
[h = handle, p = pos](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSGetPosFileWrapper(h, p);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSGetPosFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t *pos, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSGetPosFileAsync(client, block, handle, pos, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, p = pos, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSGetPosFileAsync(c, b, h, p, realErrorMask, a);
},
[h = handle, p = pos](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSGetPosFileWrapper(h, p);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSIsEof, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSIsEof(client, block, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSIsEof(c, b, h, realErrorMask);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSIsEofWrapper(h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSIsEofAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSIsEofAsync(client, block, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSIsEofAsync(c, b, h, realErrorMask, a);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSIsEofWrapper(h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSWriteFile, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X", handle, buffer, size * count);
if (isForceRealFunction(errorMask)) {
return real_FSWriteFile(client, block, buffer, size, count, handle, unk1, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, bu = buffer, s = size, co = count, h = handle, u = unk1](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSWriteFile(c, b, bu, s, co, h, u, realErrorMask);
},
[b = buffer, s = size, c = count, h = handle, u = unk1](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSWriteFileWrapper(b, s, c, h, u);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSWriteFileAsync, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, FSErrorFlag errorMask,
FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X buffer: %08X size: %08X", handle, buffer, size * count);
if (isForceRealFunction(errorMask)) {
return real_FSWriteFileAsync(client, block, buffer, size, count, handle, unk1, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, bu = buffer, s = size, co = count, h = handle, u = unk1, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSWriteFileAsync(c, b, bu, s, co, h, u, realErrorMask, a);
},
[b = buffer, s = size, c = count, h = handle, u = unk1](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSWriteFileWrapper(b, s, c, h, u);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSTruncateFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSTruncateFile(client, block, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSTruncateFile(c, b, h, realErrorMask);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSTruncateFileWrapper(h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSTruncateFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSTruncateFileAsync(client, block, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSTruncateFileAsync(c, b, h, realErrorMask, a);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSTruncateFileWrapper(h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRemove, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSRemove(client, block, path, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSRemove(c, b, p, realErrorMask);
},
[p = getFullPathForFSClient(client, path)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRemoveWrapper(p.c_str());
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRemoveAsync, FSClient *client, FSCmdBlock *block, const char *path, [[maybe_unused]] FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
if (isForceRealFunction(errorMask)) {
return real_FSRemoveAsync(client, block, path, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, p = path, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSRemoveAsync(c, b, p, realErrorMask, a);
},
[p = getFullPathForFSClient(client, path)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRemoveWrapper(p.c_str());
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRename, FSClient *client, FSCmdBlock *block, const char *oldPath, const char *newPath, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%s -> %s", oldPath, newPath);
if (isForceRealFunction(errorMask)) {
return real_FSRename(client, block, oldPath, newPath, getRealErrorFlag(errorMask));
}
return doForLayer(
client,
errorMask,
[c = client, b = block, oP = oldPath, nP = newPath](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSRename(c, b, oP, nP, realErrorMask);
},
[oP = getFullPathForFSClient(client, oldPath), nP = getFullPathForFSClient(client, newPath)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRenameWrapper(oP.c_str(), nP.c_str());
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRenameAsync,
FSClient *client,
FSCmdBlock *block,
const char *oldPath,
const char *newPath,
FSErrorFlag errorMask,
FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s -> %s", oldPath, newPath);
if (isForceRealFunction(errorMask)) {
return real_FSRenameAsync(client, block, oldPath, newPath, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, oP = oldPath, nP = newPath, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSRenameAsync(c, b, oP, nP, realErrorMask, a);
},
[oP = getFullPathForFSClient(client, oldPath), nP = getFullPathForFSClient(client, newPath)](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSRenameWrapper(oP.c_str(), nP.c_str());
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSFlushFile, FSClient *client, FSCmdBlock *block, [[maybe_unused]] FSFileHandle handle, [[maybe_unused]] FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSFlushFile(client, block, handle, errorMask);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSFlushFile(c, b, h, realErrorMask);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSFlushFileWrapper(h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSFlushFileAsync, FSClient *client, FSCmdBlock *block, [[maybe_unused]] FSFileHandle handle, [[maybe_unused]] FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%08X", handle);
if (isForceRealFunction(errorMask)) {
return real_FSFlushFileAsync(client, block, handle, getRealErrorFlag(errorMask), asyncData);
}
return doForLayer(
client,
errorMask,
[c = client, b = block, h = handle, a = asyncData](FSErrorFlag realErrorMask) -> FSStatus {
return real_FSFlushFileAsync(c, b, h, realErrorMask, a);
},
[h = handle](std::unique_ptr<IFSWrapper> &layer) -> FSError {
return layer->FSFlushFileWrapper(h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSChangeModeAsync,
FSClient *client,
FSCmdBlock *block,
const char *path,
[[maybe_unused]] FSMode mode,
[[maybe_unused]] FSMode modeMask,
[[maybe_unused]] FSErrorFlag errorMask,
FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED path %s mode: %08X", path, mode);
return real_FSChangeModeAsync(client, block, path, mode, modeMask, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSGetFreeSpaceSizeAsync, FSClient *client, FSCmdBlock *block, const char *path, [[maybe_unused]] uint64_t *outSize, [[maybe_unused]] FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED. Path: %s", path);
return real_FSGetFreeSpaceSizeAsync(client, block, path, outSize, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSWriteFileWithPosAsync, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, uint32_t unk1, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED. handle %08X size %08X", handle, size * count);
return real_FSWriteFileWithPosAsync(client, block, buffer, size, count, pos, handle, unk1, errorMask, asyncData);
}
function_replacement_data_t fs_file_function_replacements[] = {
REPLACE_FUNCTION(FSOpenFileEx, LIBRARY_COREINIT, FSOpenFileEx),
REPLACE_FUNCTION(FSOpenFileExAsync, LIBRARY_COREINIT, FSOpenFileExAsync),
REPLACE_FUNCTION(FSOpenFile, LIBRARY_COREINIT, FSOpenFile),
REPLACE_FUNCTION(FSOpenFileAsync, LIBRARY_COREINIT, FSOpenFileAsync),
REPLACE_FUNCTION(FSCloseFile, LIBRARY_COREINIT, FSCloseFile),
REPLACE_FUNCTION(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync),
REPLACE_FUNCTION(FSGetStat, LIBRARY_COREINIT, FSGetStat),
REPLACE_FUNCTION(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync),
REPLACE_FUNCTION(FSGetStatFile, LIBRARY_COREINIT, FSGetStatFile),
REPLACE_FUNCTION(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync),
REPLACE_FUNCTION(FSReadFile, LIBRARY_COREINIT, FSReadFile),
REPLACE_FUNCTION(FSReadFileAsync, LIBRARY_COREINIT, FSReadFileAsync),
REPLACE_FUNCTION(FSReadFileWithPos, LIBRARY_COREINIT, FSReadFileWithPos),
REPLACE_FUNCTION(FSReadFileWithPosAsync, LIBRARY_COREINIT, FSReadFileWithPosAsync),
REPLACE_FUNCTION(FSSetPosFile, LIBRARY_COREINIT, FSSetPosFile),
REPLACE_FUNCTION(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync),
REPLACE_FUNCTION(FSGetPosFile, LIBRARY_COREINIT, FSGetPosFile),
REPLACE_FUNCTION(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync),
REPLACE_FUNCTION(FSIsEof, LIBRARY_COREINIT, FSIsEof),
REPLACE_FUNCTION(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync),
REPLACE_FUNCTION(FSWriteFile, LIBRARY_COREINIT, FSWriteFile),
REPLACE_FUNCTION(FSWriteFileAsync, LIBRARY_COREINIT, FSWriteFileAsync),
REPLACE_FUNCTION(FSTruncateFile, LIBRARY_COREINIT, FSTruncateFile),
REPLACE_FUNCTION(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync),
REPLACE_FUNCTION(FSRemove, LIBRARY_COREINIT, FSRemove),
REPLACE_FUNCTION(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync),
REPLACE_FUNCTION(FSRename, LIBRARY_COREINIT, FSRename),
REPLACE_FUNCTION(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync),
REPLACE_FUNCTION(FSFlushFile, LIBRARY_COREINIT, FSFlushFile),
REPLACE_FUNCTION(FSFlushFileAsync, LIBRARY_COREINIT, FSFlushFileAsync),
REPLACE_FUNCTION(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync),
REPLACE_FUNCTION(FSWriteFileWithPosAsync, LIBRARY_COREINIT, FSWriteFileWithPosAsync),
//REPLACE_FUNCTION(FSGetFreeSpaceSizeAsync, LIBRARY_COREINIT, FSGetFreeSpaceSizeAsync),
//REPLACE_FUNCTION_VIA_ADDRESS(FSGetFreeSpaceSizeAsync, LIBRARY_COREINIT, 0x0A000000 + 0x0256079c),
};
uint32_t fs_file_function_replacements_size = sizeof(fs_file_function_replacements) / sizeof(function_replacement_data_t);

811
src/FSReplacements.cpp Normal file
View File

@ -0,0 +1,811 @@
#include "FSReplacements.h"
#include "FileUtils.h"
#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);
if (result >= FS_STATUS_OK || result == FS_STATUS_END || result == FS_STATUS_CANCELLED) {
return result;
}
FSErrorFlag errorFlags = FS_ERROR_FLAG_NONE;
bool forceError = false;
switch ((int32_t) result) {
case FS_STATUS_MAX:
errorFlags = FS_ERROR_FLAG_MAX;
break;
case FS_STATUS_ALREADY_OPEN:
errorFlags = FS_ERROR_FLAG_ALREADY_OPEN;
break;
case FS_STATUS_EXISTS:
errorFlags = FS_ERROR_FLAG_EXISTS;
break;
case FS_STATUS_NOT_FOUND:
errorFlags = FS_ERROR_FLAG_NOT_FOUND;
break;
case FS_STATUS_NOT_FILE:
errorFlags = FS_ERROR_FLAG_NOT_FILE;
break;
case FS_STATUS_NOT_DIR:
errorFlags = FS_ERROR_FLAG_NOT_DIR;
break;
case FS_STATUS_ACCESS_ERROR:
errorFlags = FS_ERROR_FLAG_ACCESS_ERROR;
break;
case FS_STATUS_PERMISSION_ERROR:
errorFlags = FS_ERROR_FLAG_PERMISSION_ERROR;
break;
case FS_STATUS_FILE_TOO_BIG:
errorFlags = FS_ERROR_FLAG_FILE_TOO_BIG;
break;
case FS_STATUS_STORAGE_FULL:
errorFlags = FS_ERROR_FLAG_STORAGE_FULL;
break;
case FS_STATUS_JOURNAL_FULL:
errorFlags = FS_ERROR_FLAG_JOURNAL_FULL;
break;
case FS_STATUS_UNSUPPORTED_CMD:
errorFlags = FS_ERROR_FLAG_UNSUPPORTED_CMD;
break;
case FS_STATUS_MEDIA_NOT_READY:
case FS_STATUS_MEDIA_ERROR:
case FS_STATUS_CORRUPTED:
case FS_STATUS_FATAL_ERROR:
forceError = true;
break;
case FS_STATUS_OK:
break;
}
if (forceError || (errorMask != FS_ERROR_FLAG_NONE && (errorFlags & errorMask) == 0)) {
DEBUG_FUNCTION_LINE_ERR("Transit to Fatal Error. Error %s (%d)", FSAGetStatusStr(fsError), fsError);
auto clientBody = fsClientGetBody(client);
fsClientHandleFatalErrorAndBlock(clientBody, clientBody->lastError);
return FS_STATUS_FATAL_ERROR;
}
return result;
}
void handleAsyncRequestsCallback(IOSError err, void *context) {
auto *param = (AsyncParamFS *) context;
auto *client = param->client;
auto *clientBody = fsClientGetBody(client);
auto *block = param->block;
auto *asyncData = &param->asyncData;
auto errorMask = param->errorMask;
auto fsError = __FSAShimDecodeIosErrorToFsaStatus(clientBody->clientHandle, err);
auto fsStatus = processFSError(fsError, client, errorMask);
handleAsyncResult(client, block, asyncData, fsStatus);
}
bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBlock *block, FSErrorFlag errorMask, FSAsyncData *asyncData) {
bool res = false;
if (gThreadsRunning) {
// we **don't** need to free this in this function.
auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper));
if (param == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for FSShimWrapper");
OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper");
}
param->api = FS_SHIM_API_FS;
param->sync = FS_SHIM_TYPE_ASYNC;
param->shim = shimBuffer;
param->asyncFS.callback = handleAsyncRequestsCallback;
// The client and block have to valid during the whole fs operation
param->asyncFS.client = client;
param->asyncFS.block = block;
// But we need to copy the asyncData as it might be on the stack.
param->asyncFS.asyncData.param = asyncData->param;
param->asyncFS.asyncData.callback = asyncData->callback;
param->asyncFS.asyncData.ioMsgQueue = asyncData->ioMsgQueue;
// Copy by value
param->asyncFS.errorMask = errorMask;
if (OSGetCurrentThread() == gThreadData[OSGetCoreId()].thread) {
processShimBufferForFS(param);
// because we're doing this in sync, free(param) has already been called at this point.
res = true;
} else {
auto message = (FSShimWrapperMessage *) malloc(sizeof(FSShimWrapperMessage));
if (message == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for FSShimWrapperMessage");
OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapperMessage");
}
message->param = param;
res = sendMessageToThread(message);
// the other thread is call free for us, so we can return early!
}
} else {
DEBUG_FUNCTION_LINE_WARN("Threads are not running yet, skip replacement");
}
return res;
}
DECL_FUNCTION(FSStatus, FSReadFileGeneric, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAReadFlag readFlag, FSErrorFlag errorMask,
FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
// Ensure size * count is not > 32 bit.
auto bytes = uint64_t{size} * uint64_t{count};
if (bytes > 0xFFFFFFFFull) {
DEBUG_FUNCTION_LINE_ERR("FS doesn't support transaction size >= 4GB.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PARAM);
return FS_STATUS_FATAL_ERROR;
}
if (((uint32_t) buffer & 0x3f) != 0) {
DEBUG_FUNCTION_LINE_ERR("buffer must be aligned by FS_IO_BUFFER_ALIGN");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_ALIGNMENT);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestReadFile(shimBuffer, clientBody->clientHandle, buffer, size, count, pos, handle, readFlag) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSReadFileGeneric(client, block, buffer, size, count, pos, handle, readFlag, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSWriteFileGeneric, FSClient *client, FSCmdBlock *block, const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAWriteFlag writeFlag, FSErrorFlag errorMask,
FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
// Ensure size * count is not > 32 bit.
auto bytes = uint64_t{size} * uint64_t{count};
if (bytes > 0xFFFFFFFFull) {
DEBUG_FUNCTION_LINE_ERR("FS doesn't support transaction size >= 4GB.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PARAM);
return FS_STATUS_FATAL_ERROR;
}
if (((uint32_t) buffer & 0x3f) != 0) {
DEBUG_FUNCTION_LINE_ERR("buffer must be aligned by FS_IO_BUFFER_ALIGN");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_ALIGNMENT);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestWriteFile(shimBuffer, clientBody->clientHandle, buffer, size, count, pos, handle, writeFlag) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSWriteFileGeneric(client, block, buffer, size, count, pos, handle, writeFlag, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSOpenFileExAsync, FSClient *client, FSCmdBlock *block, const char *path, const char *mode, FSMode createMode, FSOpenFileFlags openFlag, uint32_t preallocSize, FSFileHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (handle == nullptr) {
DEBUG_FUNCTION_LINE_ERR("handle is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_BUFFER);
return FS_STATUS_FATAL_ERROR;
}
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("path is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (mode == nullptr) {
DEBUG_FUNCTION_LINE_ERR("mode is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestOpenFile(shimBuffer, clientBody->clientHandle, path, mode, createMode, openFlag, preallocSize) == 0) {
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) handle;
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSOpenFileExAsync(client, block, path, mode, createMode, openFlag, preallocSize, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSCloseFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (fsaShimPrepareRequestCloseFile(shimBuffer, clientBody->clientHandle, handle) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSCloseFileAsync(client, block, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSGetStatAsync, FSClient *client, FSCmdBlock *block, const char *path, FSStat *stat, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("path is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (stat == nullptr) {
DEBUG_FUNCTION_LINE_ERR("stat is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_BUFFER);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestQueryInfo(shimBuffer, clientBody->clientHandle, path, FSA_QUERY_INFO_STAT) == 0) {
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) stat;
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSGetStatAsync(client, block, path, stat, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSGetStatFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSStat *stat, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (stat == nullptr) {
DEBUG_FUNCTION_LINE_ERR("stat is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_BUFFER);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestStatFile(shimBuffer, clientBody->clientHandle, handle) == 0) {
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) stat;
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSGetStatFileAsync(client, block, handle, stat, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSSetPosFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSAFilePosition pos, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (fsaShimPrepareRequestSetPos(shimBuffer, clientBody->clientHandle, handle, pos) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSSetPosFileAsync(client, block, handle, pos, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSGetPosFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, const FSAFilePosition *pos, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (pos == nullptr) {
DEBUG_FUNCTION_LINE_ERR("pos is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_BUFFER);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestGetPos(shimBuffer, clientBody->clientHandle, handle) == 0) {
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) pos;
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSGetPosFileAsync(client, block, handle, pos, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSIsEofAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (fsaShimPrepareRequestIsEof(shimBuffer, clientBody->clientHandle, handle) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSIsEofAsync(client, block, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSTruncateFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (fsaShimPrepareRequestTruncate(shimBuffer, clientBody->clientHandle, handle) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSTruncateFileAsync(client, block, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSRemoveAsync, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("path is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestRemove(shimBuffer, clientBody->clientHandle, path) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSRemoveAsync(client, block, path, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSRenameAsync, FSClient *client, FSCmdBlock *block, const char *oldPath, const char *newPath, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (oldPath == nullptr || newPath == nullptr) {
DEBUG_FUNCTION_LINE_ERR("oldPath or newPath is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestRename(shimBuffer, clientBody->clientHandle, oldPath, newPath) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSRenameAsync(client, block, oldPath, newPath, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSFlushFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (fsaShimPrepareRequestFlushFile(shimBuffer, clientBody->clientHandle, handle) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSFlushFileAsync(client, block, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSChangeModeAsync, FSClient *client, FSCmdBlock *block, const char *path, FSMode mode, FSMode modeMask, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("path is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestChangeMode(shimBuffer, clientBody->clientHandle, path, mode, modeMask) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSChangeModeAsync(client, block, path, mode, modeMask, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSOpenDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("path is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (handle == nullptr) {
DEBUG_FUNCTION_LINE_ERR("handle is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_BUFFER);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestOpenDir(shimBuffer, clientBody->clientHandle, path) == 0) {
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) handle;
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSOpenDirAsync(client, block, path, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSReadDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (entry == nullptr) {
DEBUG_FUNCTION_LINE_ERR("entry is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_BUFFER);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestReadDir(shimBuffer, clientBody->clientHandle, handle) == 0) {
// Hacky solution to pass the pointer into the other thread.
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) entry;
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSReadDirAsync(client, block, handle, entry, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSCloseDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (fsaShimPrepareRequestCloseDir(shimBuffer, clientBody->clientHandle, handle) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSCloseDirAsync(client, block, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSRewindDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (fsaShimPrepareRequestRewindDir(shimBuffer, clientBody->clientHandle, handle) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSRewindDirAsync(client, block, handle, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSMakeDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("path is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestMakeDir(shimBuffer, clientBody->clientHandle, path, static_cast<FSMode>(0x660)) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSMakeDirAsync(client, block, path, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSChangeDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
auto *clientBody = fsClientGetBody(client);
auto *shimBuffer = (FSAShimBuffer *) fsCmdBlockGetBody(block);
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("path is null.");
fsClientHandleFatalError(clientBody, FS_ERROR_INVALID_PATH);
return FS_STATUS_FATAL_ERROR;
}
if (fsaShimPrepareRequestChangeDir(shimBuffer, clientBody->clientHandle, path) == 0) {
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK;
}
}
return real_FSChangeDirAsync(client, block, path, errorMask, asyncData);
}
function_replacement_data_t fs_file_function_replacements[] = {
REPLACE_FUNCTION(FSOpenFileExAsync, LIBRARY_COREINIT, FSOpenFileExAsync),
REPLACE_FUNCTION(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync),
REPLACE_FUNCTION(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync),
REPLACE_FUNCTION(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync),
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_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),
REPLACE_FUNCTION(FSFlushFileAsync, LIBRARY_COREINIT, FSFlushFileAsync),
REPLACE_FUNCTION(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync),
REPLACE_FUNCTION(FSOpenDirAsync, LIBRARY_COREINIT, FSOpenDirAsync),
REPLACE_FUNCTION(FSReadDirAsync, LIBRARY_COREINIT, FSReadDirAsync),
REPLACE_FUNCTION(FSCloseDirAsync, LIBRARY_COREINIT, FSCloseDirAsync),
REPLACE_FUNCTION(FSRewindDirAsync, LIBRARY_COREINIT, FSRewindDirAsync),
REPLACE_FUNCTION(FSMakeDirAsync, LIBRARY_COREINIT, FSMakeDirAsync),
REPLACE_FUNCTION(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync),
};
uint32_t fs_file_function_replacements_size = sizeof(fs_file_function_replacements) / sizeof(function_replacement_data_t);
FSError processShimBufferForFS(FSShimWrapper *param) {
FSError result = doForLayer(param);
FSStatus fsResult = FS_STATUS_MEDIA_ERROR;
if (result == FS_ERROR_FORCE_REAL_FUNCTION) {
if (param->sync == FS_SHIM_TYPE_SYNC) {
fsResult = FS_STATUS_MEDIA_ERROR;
DEBUG_FUNCTION_LINE_ERR("SYNC FS API is not supported");
OSFatal("ContentRedirectionModule: SYNC FS API is not supported");
} else if (param->sync == FS_SHIM_TYPE_ASYNC) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
switch ((FSACommandEnum) param->shim->command) {
case FSA_COMMAND_READ_FILE: {
auto *request = &param->shim->request.readFile;
fsResult = real_FSReadFileGeneric(param->asyncFS.client, param->asyncFS.block, request->buffer, request->size, request->count, request->pos, request->handle, request->readFlags, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSReadFileGeneric. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_WRITE_FILE: {
auto *request = &param->shim->request.writeFile;
fsResult = real_FSWriteFileGeneric(param->asyncFS.client, param->asyncFS.block, request->buffer, request->size, request->count, request->pos, request->handle, request->writeFlags, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSWriteFileGeneric. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_OPEN_FILE: {
auto *request = &param->shim->request.openFile;
// Hacky solution. We stored the pointer from the user in the response to use it at this point.
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *handlePtr = (FSFileHandle *) hackyBuffer[1];
fsResult = real_FSOpenFileExAsync(param->asyncFS.client, param->asyncFS.block, request->path, request->mode, static_cast<FSMode>(request->unk0x290), static_cast<FSOpenFileFlags>(request->unk0x294), request->unk0x298, handlePtr, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSOpenFileExAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_CLOSE_FILE: {
auto *request = &param->shim->request.closeFile;
fsResult = real_FSCloseFileAsync(param->asyncFS.client, param->asyncFS.block, request->handle, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSCloseFileAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_SET_POS_FILE: {
auto *request = &param->shim->request.setPosFile;
fsResult = real_FSSetPosFileAsync(param->asyncFS.client, param->asyncFS.block, request->handle, request->pos, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSSetPosFileAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_GET_POS_FILE: {
auto *request = &param->shim->request.getPosFile;
// Hacky solution. We stored the pointer from the user in the response to use it at this point.
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *posPtr = (FSAFilePosition *) hackyBuffer[1];
fsResult = real_FSGetPosFileAsync(param->asyncFS.client, param->asyncFS.block, request->handle, posPtr, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSGetPosFileAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_STAT_FILE: {
auto *request = &param->shim->request.statFile;
// Hacky solution. We stored the pointer from the user in the response to use it at this point.
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *statPtr = (FSStat *) hackyBuffer[1];
fsResult = real_FSGetStatFileAsync(param->asyncFS.client, param->asyncFS.block, request->handle, statPtr, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSGetStatFileAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_GET_INFO_BY_QUERY: {
auto *request = &param->shim->request.getInfoByQuery;
if (request->type == FSA_QUERY_INFO_STAT) {
// Hacky solution. We stored the pointer from the user in the response to use it at this point.
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *statPtr = (FSStat *) hackyBuffer[1];
fsResult = real_FSGetStatAsync(param->asyncFS.client, param->asyncFS.block, request->path, statPtr, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSGetStatAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
} else {
DEBUG_FUNCTION_LINE_ERR("Missing real implementation for FSA_COMMAND_GET_INFO_BY_QUERY type %08X", request->type);
}
break;
}
case FSA_COMMAND_IS_EOF: {
auto *request = &param->shim->request.isEof;
fsResult = real_FSIsEofAsync(param->asyncFS.client, param->asyncFS.block, request->handle, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSIsEofAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_TRUNCATE_FILE: {
auto *request = &param->shim->request.truncateFile;
fsResult = real_FSTruncateFileAsync(param->asyncFS.client, param->asyncFS.block, request->handle, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSTruncateFileAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_REMOVE: {
auto *request = &param->shim->request.remove;
fsResult = real_FSRemoveAsync(param->asyncFS.client, param->asyncFS.block, request->path, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSRemoveAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_RENAME: {
auto *request = &param->shim->request.rename;
fsResult = real_FSRenameAsync(param->asyncFS.client, param->asyncFS.block, request->oldPath, request->newPath, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSRenameAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_FLUSH_FILE: {
auto *request = &param->shim->request.flushFile;
fsResult = real_FSFlushFileAsync(param->asyncFS.client, param->asyncFS.block, request->handle, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSFlushFileAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_CHANGE_MODE: {
auto *request = &param->shim->request.changeMode;
fsResult = real_FSChangeModeAsync(param->asyncFS.client, param->asyncFS.block, request->path, (FSMode) request->mode1, (FSMode) request->mode2, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSChangeModeAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_OPEN_DIR: {
auto *request = &param->shim->request.openDir;
// Hacky solution. We stored the pointer from the user in the response to use it at this point.
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *handlePtr = (FSDirectoryHandle *) hackyBuffer[1];
fsResult = real_FSOpenDirAsync(param->asyncFS.client, param->asyncFS.block, request->path, handlePtr, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSOpenDirAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_READ_DIR: {
auto *request = &param->shim->request.readDir;
// Hacky solution. We stored the pointer from the user in the response to use it at this point.
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *entryPtr = (FSDirectoryEntry *) hackyBuffer[1];
fsResult = real_FSReadDirAsync(param->asyncFS.client, param->asyncFS.block, request->handle, entryPtr, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSReadDirAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_CLOSE_DIR: {
auto *request = &param->shim->request.closeDir;
fsResult = real_FSCloseDirAsync(param->asyncFS.client, param->asyncFS.block, request->handle, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSCloseDirAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_REWIND_DIR: {
auto *request = &param->shim->request.rewindDir;
fsResult = real_FSRewindDirAsync(param->asyncFS.client, param->asyncFS.block, request->handle, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSRewindDirAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_MAKE_DIR: {
auto *request = &param->shim->request.makeDir;
fsResult = real_FSMakeDirAsync(param->asyncFS.client, param->asyncFS.block, request->path, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSMakeDirAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
case FSA_COMMAND_CHANGE_DIR: {
auto *request = &param->shim->request.changeDir;
fsResult = real_FSChangeDirAsync(param->asyncFS.client, param->asyncFS.block, request->path, param->asyncFS.errorMask, &param->asyncFS.asyncData);
if (fsResult != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to submit real_FSChangeDirAsync. Return was %d. Fake actual async result to FS_STATUS_MEDIA_ERROR instead", fsResult);
handleAsyncResult(param->asyncFS.client, param->asyncFS.block, &param->asyncFS.asyncData, FS_STATUS_MEDIA_ERROR);
}
break;
}
default: {
DEBUG_FUNCTION_LINE_ERR("Missing real implementation for command %08X", param->shim->command);
fsResult = FS_STATUS_FATAL_ERROR;
}
}
#pragma GCC diagnostic pop
}
}
free(param);
if (fsResult != FS_STATUS_OK) {
result = FS_ERROR_MEDIA_ERROR;
}
return result;
}

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,15 +317,15 @@ 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)) {
DEBUG_FUNCTION_LINE_VERBOSE("Ignore %s, filename starts with %s", path.c_str(), deletePrefix.c_str());
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;
}
auto newDelPath = asPath.replace_filename(deletePrefix + asPath.filename().c_str());
struct stat buf {};
if (stat(newDelPath.c_str(), &buf) == 0) {
DEBUG_FUNCTION_LINE_VERBOSE("Ignore %s, file %s exists", path.c_str(), newDelPath.c_str());
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Ignore %s, file %s exists", getName().c_str(), path.c_str(), newDelPath.c_str());
return true;
}
return false;
@ -400,7 +404,7 @@ FSError FSWrapper::FSReadFileWrapper(void *buffer, uint32_t size, uint32_t count
FSError result;
if (read < 0) {
DEBUG_FUNCTION_LINE_ERR("[%s] read %u bytes of fd %d (FSFileHandle %08X) failed", getName().c_str(), size * count, real_fd, handle);
DEBUG_FUNCTION_LINE_ERR("[%s] Read %u bytes of fd %d (FSFileHandle %08X) failed", getName().c_str(), size * count, real_fd, handle);
auto err = errno;
if (err == EBADF || err == EROFS) {
return FS_ERROR_ACCESS_ERROR;
@ -438,10 +442,10 @@ FSError FSWrapper::FSSetPosFileWrapper(FSFileHandle handle, uint32_t pos) {
int real_fd = fileHandle->fd;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %d (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %d (FSFileHandle %08X) SEEK_SET to position %08X", getName().c_str(), real_fd, handle, pos);
off_t newPos;
if ((newPos = lseek(real_fd, (off_t) pos, SEEK_SET)) != pos) {
DEBUG_FUNCTION_LINE_ERR("[%s] lseek fd %d (FSFileHandle %08X) to position %u failed", getName().c_str(), real_fd, handle);
DEBUG_FUNCTION_LINE_ERR("[%s] lseek fd %d (FSFileHandle %08X) to position %08X failed", getName().c_str(), real_fd, handle, pos);
if (newPos < 0) {
// TODO: read errno
}
@ -538,7 +542,7 @@ FSError FSWrapper::FSTruncateFileWrapper(FSFileHandle handle) {
return result;
}
FSError FSWrapper::FSWriteFileWrapper(uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) {
FSError FSWrapper::FSWriteFileWrapper(const uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) {
if (!isValidFileHandle(handle)) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
@ -583,7 +587,7 @@ FSError FSWrapper::FSRemoveWrapper(const char *path) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Remove %s (%s)", getName().c_str(), path, newPath.c_str());
if (remove(newPath.c_str()) < 0) {
auto err = errno;
DEBUG_FUNCTION_LINE_ERR("[%s] rename failed %s (%s) errno %d", getName().c_str(), path, newPath.c_str(), err);
DEBUG_FUNCTION_LINE_ERR("[%s] Rename failed %s (%s) errno %d", getName().c_str(), path, newPath.c_str(), err);
if (err == ENOTDIR) {
return FS_ERROR_NOT_DIR;
} else if (err == EACCES) {
@ -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) {
@ -719,7 +725,7 @@ std::shared_ptr<FileInfo> FSWrapper::getFileFromHandle(FSFileHandle handle) {
}
}
DEBUG_FUNCTION_LINE_ERR("[%s] FileInfo for handle %08X was not found. isValidFileHandle check missing?", getName().c_str(), handle);
OSFatal("Failed to find file handle");
OSFatal("ContentRedirectionModule: Failed to find file handle");
return nullptr;
}
@ -731,7 +737,7 @@ std::shared_ptr<DirInfo> FSWrapper::getDirFromHandle(FSDirectoryHandle handle) {
}
}
DEBUG_FUNCTION_LINE_ERR("[%s] DirInfo for handle %08X was not found. isValidDirHandle check missing?", getName().c_str(), handle);
OSFatal("Failed to find dir handle");
OSFatal("ContentRedirectionModule: Failed to find dir handle");
return nullptr;
}

View File

@ -82,7 +82,7 @@ public:
FSError FSTruncateFileWrapper(FSFileHandle handle) override;
FSError FSWriteFileWrapper(uint8_t *buffer,
FSError FSWriteFileWrapper(const uint8_t *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
@ -95,6 +95,10 @@ public:
FSError FSFlushFileWrapper(FSFileHandle handle) override;
uint32_t getLayerId() override {
return (uint32_t) this;
}
protected:
virtual bool IsFileModeAllowed(const char *mode);

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>
@ -7,7 +8,7 @@
#include <filesystem>
FSError FSWrapperMergeDirsWithParent::FSOpenDirWrapper(const char *path,
FSDirectoryHandle *handle) {
FSADirectoryHandle *handle) {
if (handle == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] handle was NULL", getName().c_str());
return FS_ERROR_INVALID_PARAM;
@ -26,17 +27,17 @@ FSError FSWrapperMergeDirsWithParent::FSOpenDirWrapper(const char *path,
dirHandle->readResultNumberOfEntries = 0;
dirHandle->realDirHandle = 0;
if (pFSClient && pCmdBlock) {
FSDirectoryHandle realHandle = 0;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call real_FSOpenDir for %s with error_flag %08X", getName().c_str(), path, this->getHandle());
// Call FSOpen with "this" as errorFlag call FSOpen for "parent" layers only.
if (FSOpenDir(pFSClient, pCmdBlock, path, &realHandle, (FSErrorFlag) this->getHandle()) == FS_STATUS_OK) {
if (clientHandle) {
FSADirectoryHandle realHandle = 0;
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] Global FSClient or FSCmdBlock were null", getName().c_str());
DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str());
}
OSMemoryBarrier();
}
@ -48,7 +49,7 @@ bool FSWrapperMergeDirsWithParent::SkipDeletedFilesInReadDir() {
return false;
}
FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry) {
FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSADirectoryHandle handle, FSADirectoryEntry *entry) {
do {
auto res = FSWrapper::FSReadDirWrapper(handle, entry);
if (res == FS_ERROR_OK || res == FS_ERROR_END_OF_DIR) {
@ -62,7 +63,7 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle,
dirHandle->readResult = (FSDirectoryEntryEx *) malloc(sizeof(FSDirectoryEntryEx));
if (dirHandle->readResult == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc memory for %08X (handle %08X)", getName().c_str(), dirHandle.get(), handle);
OSFatal("Failed to alloc memory for read result");
OSFatal("ContentRedirectionModule: Failed to alloc memory for read result");
}
dirHandle->readResultCapacity = 1;
}
@ -73,17 +74,17 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle,
dirHandle->readResultCapacity = newCapacity;
if (dirHandle->readResult == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to realloc memory for %08X (handle %08X)", getName().c_str(), dirHandle.get(), handle);
OSFatal("Failed to alloc memory for read result");
OSFatal("ContentRedirectionModule: Failed to alloc memory for read result");
}
}
memcpy(&dirHandle->readResult[dirHandle->readResultNumberOfEntries].realEntry, entry, sizeof(FSDirectoryEntry));
memcpy(&dirHandle->readResult[dirHandle->readResultNumberOfEntries].realEntry, entry, sizeof(FSADirectoryEntry));
dirHandle->readResultNumberOfEntries++;
/**
* 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();
@ -95,19 +96,19 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle,
} else if (res == FS_ERROR_END_OF_DIR) {
// Read the real directory.
if (dirHandle->realDirHandle != 0) {
if (pFSClient && pCmdBlock) {
FSDirectoryEntry realDirEntry;
FSStatus readDirResult;
if (clientHandle) {
FSADirectoryEntry realDirEntry;
FSError readDirResult;
while (true) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call real_FSReadDir for %08X with error_flag %08X", getName().c_str(), dirHandle->realDirHandle, (uint32_t) this->getHandle());
readDirResult = FSReadDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, &realDirEntry, (FSErrorFlag) (uint32_t) this->getHandle());
if (readDirResult == FS_STATUS_OK) {
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;
auto nameDeleted = deletePrefix + realDirEntry.name;
for (int i = 0; i < dirHandle->readResultNumberOfEntries; i++) {
auto curResult = &dirHandle->readResult[i];
// Don't return file that are "deleted"
// Don't return files that are "deleted"
if (strcmp(curResult->realEntry.name, nameDeleted.c_str()) == 0) {
found = true;
break;
@ -120,21 +121,21 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle,
}
// If it's new we can use it :)
if (!found) {
memcpy(entry, &realDirEntry, sizeof(FSDirectoryEntry));
memcpy(entry, &realDirEntry, sizeof(FSADirectoryEntry));
res = FS_ERROR_OK;
break;
}
} else if (readDirResult == FS_STATUS_END) {
} 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: %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;
}
}
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
DEBUG_FUNCTION_LINE_ERR("[%s] clientHandle was null", getName().c_str());
}
}
} else {
@ -145,7 +146,7 @@ FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle,
} while (true);
}
FSError FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSDirectoryHandle handle) {
FSError FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSADirectoryHandle handle) {
auto res = FSWrapper::FSCloseDirWrapper(handle);
if (res == FS_ERROR_OK) {
@ -155,18 +156,20 @@ FSError FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSDirectoryHandle handle
}
auto dirHandle = getDirExFromHandle(handle);
if (dirHandle->realDirHandle != 0) {
if (pFSClient && pCmdBlock) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSCloseDir for %08X with error_flag %08X (this)", getName().c_str(), dirHandle->realDirHandle, (uint32_t) this->getHandle());
auto realResult = FSCloseDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, (FSErrorFlag) (uint32_t) this->getHandle());
if (realResult == FS_STATUS_OK) {
if (clientHandle) {
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, realResult);
return realResult == FS_STATUS_CANCELLED ? FS_ERROR_CANCELLED : FS_ERROR_MEDIA_ERROR;
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] Global FSClient or FSCmdBlock were null", getName().c_str());
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) {
@ -181,7 +184,7 @@ FSError FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSDirectoryHandle handle
return res;
}
FSError FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSDirectoryHandle handle) {
FSError FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSADirectoryHandle handle) {
auto res = FSWrapper::FSRewindDirWrapper(handle);
if (res == FS_ERROR_OK) {
if (!isValidDirHandle(handle)) {
@ -198,16 +201,19 @@ FSError FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSDirectoryHandle handl
}
if (dirHandle->realDirHandle != 0) {
if (pFSClient && pCmdBlock) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call real_FSRewindDir for %08X with error_flag %08X (this->getHandle())", getName().c_str(), dirHandle->realDirHandle, (uint32_t) this->getHandle());
if (FSRewindDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, (FSErrorFlag) (uint32_t) this->getHandle()) == FS_STATUS_OK) {
if (clientHandle) {
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] Global FSClient or FSCmdBlock were null", getName().c_str());
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,39 +228,30 @@ FSWrapperMergeDirsWithParent::FSWrapperMergeDirsWithParent(const std::string &na
replaceWithPath,
fallbackOnError,
false) {
pFSClient = new (std::nothrow) FSClient;
pCmdBlock = new (std::nothrow) FSCmdBlock;
if (pFSClient == nullptr || pCmdBlock == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc client or cmdBlock", name.c_str());
freeFSClient();
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);
clientHandle = 0;
}
if (FSAddClient(pFSClient, FS_ERROR_FLAG_ALL) != FS_STATUS_OK) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to addClient");
freeFSClient();
}
FSInitCmdBlock(pCmdBlock);
}
FSWrapperMergeDirsWithParent::~FSWrapperMergeDirsWithParent() {
freeFSClient();
}
void FSWrapperMergeDirsWithParent::freeFSClient() {
if (pFSClient) {
FSDelClient(pFSClient, FS_ERROR_FLAG_ALL);
if (clientHandle) {
FSError res;
if ((res = FSADelClient(clientHandle)) != FS_ERROR_OK) {
DEBUG_FUNCTION_LINE_ERR("[%s] FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res);
}
clientHandle = 0;
}
delete pFSClient;
delete pCmdBlock;
pFSClient = nullptr;
pCmdBlock = nullptr;
}
std::shared_ptr<DirInfoEx> FSWrapperMergeDirsWithParent::getDirExFromHandle(FSDirectoryHandle handle) {
std::shared_ptr<DirInfoEx> FSWrapperMergeDirsWithParent::getDirExFromHandle(FSADirectoryHandle handle) {
auto dir = std::dynamic_pointer_cast<DirInfoEx>(getDirFromHandle(handle));
if (!dir) {
DEBUG_FUNCTION_LINE_ERR("[%s] dynamic_pointer_cast<DirInfoEx *>(%08X) failed", getName().c_str(), handle);
OSFatal("dynamic_pointer_cast<DirInfoEx *> failed");
OSFatal("ContentRedirectionModule: dynamic_pointer_cast<DirInfoEx *> failed");
}
return dir;
}

View File

@ -27,10 +27,12 @@ public:
bool SkipDeletedFilesInReadDir() override;
uint32_t getLayerId() override {
return (uint32_t) clientHandle;
}
private:
void freeFSClient();
FSClient *pFSClient;
FSCmdBlock *pCmdBlock;
FSAClientHandle clientHandle;
std::shared_ptr<DirInfoEx> getDirExFromHandle(FSDirectoryHandle handle);
};

View File

@ -6,19 +6,18 @@
#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>
std::mutex workingDirMutexFS;
std::map<uint32_t, std::string> workingDirsFS;
std::mutex workingDirMutexFSA;
std::map<uint32_t, std::string> workingDirsFSA;
std::mutex workingDirMutex;
std::map<FSAClientHandle, std::string> workingDirs;
std::mutex fsLayerMutex;
std::vector<std::unique_ptr<IFSWrapper>> fsLayers;
std::string getFullPathGeneric(uint32_t client, const char *path, std::mutex &mutex, std::map<uint32_t, std::string> &map) {
std::string getFullPathGeneric(FSAClientHandle client, const char *path, std::mutex &mutex, std::map<FSAClientHandle, std::string> &map) {
std::lock_guard<std::mutex> workingDirLock(mutex);
std::string res;
@ -26,7 +25,7 @@ std::string getFullPathGeneric(uint32_t client, const char *path, std::mutex &mu
if (path[0] != '/' && path[0] != '\\') {
if (map.count(client) == 0) {
DEBUG_FUNCTION_LINE_WARN("No working dir found for client %08X, fallback to \"/\"", client);
workingDirsFS[client] = "/";
workingDirs[client] = "/";
}
res = string_format("%s%s", map.at(client).c_str(), path);
} else {
@ -38,7 +37,7 @@ std::string getFullPathGeneric(uint32_t client, const char *path, std::mutex &mu
return res;
}
void setWorkingDirGeneric(uint32_t client, const char *path, std::mutex &mutex, std::map<uint32_t, std::string> &map) {
void setWorkingDirGeneric(FSAClientHandle client, const char *path, std::mutex &mutex, std::map<FSAClientHandle, std::string> &map) {
if (!path) {
DEBUG_FUNCTION_LINE_WARN("Path was NULL");
return;
@ -55,30 +54,18 @@ void setWorkingDirGeneric(uint32_t client, const char *path, std::mutex &mutex,
}
std::string getFullPathForFSClient(FSClient *pClient, const char *path) {
return getFullPathGeneric((uint32_t) pClient, path, workingDirMutexFS, workingDirsFS);
std::string getFullPath(FSAClientHandle pClient, const char *path) {
return getFullPathGeneric(pClient, path, workingDirMutex, workingDirs);
}
void setWorkingDirForFSClient(FSClient *client, const char *path) {
setWorkingDirGeneric((uint32_t) client, path, workingDirMutexFS, workingDirsFS);
}
std::string getFullPathForFSAClient(FSAClientHandle client, const char *path) {
return getFullPathGeneric((uint32_t) client, path, workingDirMutexFSA, workingDirsFSA);
}
void setWorkingDirForFSAClient(FSAClientHandle client, const char *path) {
setWorkingDirGeneric(client, path, workingDirMutexFSA, workingDirsFSA);
void setWorkingDir(FSAClientHandle client, const char *path) {
setWorkingDirGeneric(client, path, workingDirMutex, workingDirs);
}
void clearFSLayer() {
{
std::lock_guard<std::mutex> workingDirLock(workingDirMutexFS);
workingDirsFS.clear();
}
{
std::lock_guard<std::mutex> workingDirLock(workingDirMutexFSA);
workingDirsFSA.clear();
std::lock_guard<std::mutex> workingDirLock(workingDirMutex);
workingDirs.clear();
}
{
std::lock_guard<std::mutex> layerLock(fsLayerMutex);
@ -86,62 +73,32 @@ void clearFSLayer() {
}
}
FSError doForLayerFSA(const std::function<FSError()> &real_function,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer)> &layer_callback,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer, FSError)> &result_handler) {
std::lock_guard<std::mutex> lock(fsLayerMutex);
if (!fsLayers.empty()) {
uint32_t startIndex = fsLayers.size();
if (startIndex > 0) {
for (uint32_t i = startIndex; i > 0; i--) {
auto &layer = fsLayers[i - 1];
if (!layer->isActive()) {
continue;
}
FSError layerResult = layer_callback(layer);
if (layerResult != FS_ERROR_FORCE_PARENT_LAYER) {
auto result = layerResult >= 0 ? layerResult : (FSError) ((layerResult & FS_ERROR_REAL_MASK) | FS_ERROR_EXTRA_MASK);
if (result < FS_ERROR_OK && result != FS_ERROR_END_OF_DIR && result != FS_ERROR_END_OF_FILE && result != FS_ERROR_CANCELLED) {
if (layer->fallbackOnError()) {
// Only fallback if FS_ERROR_FORCE_NO_FALLBACK flag is not set.
if (static_cast<FSError>(layerResult & FS_ERROR_EXTRA_MASK) != FS_ERROR_FORCE_NO_FALLBACK) {
continue;
}
}
}
return result_handler(layer, result);
}
}
bool sendMessageToThread(FSShimWrapperMessage *param) {
auto *curThread = &gThreadData[OSGetCoreId()];
if (curThread->setup) {
OSMessage send;
send.message = param;
send.args[0] = FS_IO_QUEUE_COMMAND_PROCESS_FS_COMMAND;
auto res = OSSendMessage(&curThread->queue, &send, OS_MESSAGE_FLAGS_NONE);
if (!res) {
DEBUG_FUNCTION_LINE_ERR("Message Queue for ContentRedirection IO Thread is full");
OSFatal("ContentRedirectionModule: Message Queue for ContentRedirection IO Thread is full");
}
return res;
} else {
DEBUG_FUNCTION_LINE_ERR("Thread not setup");
OSFatal("ContentRedirectionModule: Thread not setup");
}
return real_function();
return false;
}
// FUN_0204cc20
#define fsClientHandleFatalErrorAndBlock ((void (*)(FSClientBody *, uint32_t))(0x101C400 + 0x4cc20))
#define fsaDecodeFsaStatusToFsStatus ((FSStatus(*)(FSError))(0x101C400 + 0x4b148))
FSStatus doForLayer(FSClient *client,
FSErrorFlag errorMask,
const std::function<FSStatus(FSErrorFlag errorMask)> &real_function,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer)> &layer_callback,
const std::function<FSStatus(std::unique_ptr<IFSWrapper> &layer, FSStatus)> &result_handler) {
FSErrorFlag realErrorMask = errorMask;
FSError doForLayer(FSShimWrapper *param) {
std::lock_guard<std::mutex> lock(fsLayerMutex);
if (!fsLayers.empty()) {
uint32_t startIndex = fsLayers.size();
for (uint32_t i = fsLayers.size(); i > 0; i--) {
if ((uint32_t) fsLayers[i - 1]->getHandle() == errorMask) {
startIndex = i - 1;
realErrorMask = FS_ERROR_FLAG_ALL;
if ((uint32_t) fsLayers[i - 1]->getLayerId() == param->shim->clientHandle) {
startIndex = i - 1;
break;
}
}
@ -152,12 +109,217 @@ FSStatus doForLayer(FSClient *client,
if (!layer->isActive()) {
continue;
}
FSError layerResult = layer_callback(layer);
auto layerResult = FS_ERROR_FORCE_PARENT_LAYER;
auto command = (FSACommandEnum) param->shim->command;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
switch (command) {
case FSA_COMMAND_OPEN_DIR: {
auto *request = &param->shim->request.openDir;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] OpenDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
// Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *handlePtr = (FSDirectoryHandle *) hackyBuffer[1];
layerResult = layer->FSOpenDirWrapper(fullPath.c_str(), handlePtr);
break;
}
case FSA_COMMAND_READ_DIR: {
auto *request = &param->shim->request.readDir;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] ReadDir: %08X", layer->getName().c_str(), request->handle);
// Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *dirEntryPtr = (FSADirectoryEntry *) hackyBuffer[1];
layerResult = layer->FSReadDirWrapper(request->handle, dirEntryPtr);
break;
}
case FSA_COMMAND_CLOSE_DIR: {
auto *request = &param->shim->request.closeDir;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] CloseDir: %08X", layer->getName().c_str(), request->handle);
layerResult = layer->FSCloseDirWrapper(request->handle);
if (layerResult != FS_ERROR_FORCE_REAL_FUNCTION && layerResult != FS_ERROR_FORCE_PARENT_LAYER) {
if (layer->isValidDirHandle(request->handle)) {
layer->deleteDirHandle(request->handle);
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Expected to delete dirHandle by %08X but it was not found", layer->getName().c_str(), request->handle);
}
}
break;
}
case FSA_COMMAND_REWIND_DIR: {
auto *request = &param->shim->request.rewindDir;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] RewindDir: %08X", layer->getName().c_str(), request->handle);
layerResult = layer->FSRewindDirWrapper(request->handle);
break;
}
case FSA_COMMAND_MAKE_DIR: {
auto *request = &param->shim->request.makeDir;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] MakeDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
layerResult = layer->FSMakeDirWrapper(fullPath.c_str());
break;
}
case FSA_COMMAND_OPEN_FILE: {
auto *request = &param->shim->request.openFile;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path);
// Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *handlePtr = (FSFileHandle *) hackyBuffer[1];
DEBUG_FUNCTION_LINE_VERBOSE("[%s] OpenFile: path %s (full path: %s) mode %s", layer->getName().c_str(), request->path, fullPath.c_str(), request->mode);
layerResult = layer->FSOpenFileWrapper(fullPath.c_str(), request->mode, handlePtr);
break;
}
case FSA_COMMAND_CLOSE_FILE: {
auto *request = &param->shim->request.closeFile;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] CloseFile: %08X", layer->getName().c_str(), request->handle);
layerResult = layer->FSCloseFileWrapper(request->handle);
if (layerResult != FS_ERROR_FORCE_REAL_FUNCTION && layerResult != FS_ERROR_FORCE_PARENT_LAYER) {
if (layer->isValidFileHandle(request->handle)) {
layer->deleteFileHandle(request->handle);
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Expected to delete fileHandle by handle %08X but it was not found", layer->getName().c_str(), request->handle);
}
}
break;
}
case FSA_COMMAND_GET_INFO_BY_QUERY: {
auto *request = &param->shim->request.getInfoByQuery;
if (request->type == FSA_QUERY_INFO_STAT) {
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] GetStat: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
// Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *statPtr = (FSStat *) hackyBuffer[1];
layerResult = layer->FSGetStatWrapper(fullPath.c_str(), statPtr);
}
break;
}
case FSA_COMMAND_STAT_FILE: {
auto *request = &param->shim->request.statFile;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] GetStatFile: %08X", layer->getName().c_str(), request->handle);
// Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *statPtr = (FSStat *) hackyBuffer[1];
layerResult = layer->FSGetStatFileWrapper(request->handle, statPtr);
break;
}
case FSA_COMMAND_READ_FILE: {
auto *request = &param->shim->request.readFile;
if (request->readFlags == FSA_READ_FLAG_NONE) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] ReadFile: buffer %08X size %08X count %08X handle %08X", layer->getName().c_str(), request->buffer, request->size, request->count, request->handle);
layerResult = layer->FSReadFileWrapper(request->buffer, request->size, request->count, request->handle, 0);
} else if (request->readFlags == FSA_READ_FLAG_READ_WITH_POS) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] ReadFileWithPos: buffer %08X size %08X count %08X pos %08X handle %08X", layer->getName().c_str(), request->buffer, request->size, request->count, request->pos, request->handle);
layerResult = layer->FSReadFileWithPosWrapper(request->buffer, request->size, request->count, request->pos, request->handle, 0);
}
break;
}
case FSA_COMMAND_SET_POS_FILE: {
auto *request = &param->shim->request.setPosFile;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] SetPosFile: %08X %08X", layer->getName().c_str(), request->handle, request->pos);
layerResult = layer->FSSetPosFileWrapper(request->handle, request->pos);
break;
}
case FSA_COMMAND_GET_POS_FILE: {
auto *request = &param->shim->request.getPosFile;
// Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *posPtr = (FSAFilePosition *) hackyBuffer[1];
DEBUG_FUNCTION_LINE_VERBOSE("[%s] GetPosFile: %08X", layer->getName().c_str(), request->handle);
layerResult = layer->FSGetPosFileWrapper(request->handle, posPtr);
break;
}
case FSA_COMMAND_IS_EOF: {
auto *request = &param->shim->request.isEof;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] IsEof: %08X", layer->getName().c_str(), request->handle);
layerResult = layer->FSIsEofWrapper(request->handle);
break;
}
case FSA_COMMAND_TRUNCATE_FILE: {
auto *request = &param->shim->request.truncateFile;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] TruncateFile: %08X", layer->getName().c_str(), request->handle);
layerResult = layer->FSTruncateFileWrapper(request->handle);
break;
}
case FSA_COMMAND_WRITE_FILE: {
auto *request = &param->shim->request.writeFile;
if (request->writeFlags == FSA_WRITE_FLAG_NONE) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] WriteFile: buffer %08X size %08X count %08X handle %08X", layer->getName().c_str(), request->buffer, request->size, request->count, request->handle);
layerResult = layer->FSWriteFileWrapper(request->buffer, request->size, request->count, request->handle, 0);
} else if (request->writeFlags == FSA_WRITE_FLAG_READ_WITH_POS) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] WriteFileWithPos: buffer %08X size %08X count %08X pos %08X handle %08X", layer->getName().c_str(), request->buffer, request->size, request->count, request->pos, request->handle);
layerResult = layer->FSWriteFileWithPosWrapper(request->buffer, request->size, request->count, request->pos, request->handle, 0);
}
break;
}
case FSA_COMMAND_REMOVE: {
auto *request = &param->shim->request.remove;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Remove: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
layerResult = layer->FSRemoveWrapper(fullPath.c_str());
break;
}
case FSA_COMMAND_RENAME: {
auto *request = &param->shim->request.rename;
auto fullOldPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->oldPath);
auto fullNewPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->newPath);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Rename: %s -> %s (full path: %s -> %s)", layer->getName().c_str(), request->oldPath, request->newPath, fullOldPath.c_str(), fullNewPath.c_str());
layerResult = layer->FSRenameWrapper(fullOldPath.c_str(), fullNewPath.c_str());
break;
}
case FSA_COMMAND_FLUSH_FILE: {
auto *request = &param->shim->request.flushFile;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] FlushFile: %08X", layer->getName().c_str(), request->handle);
layerResult = layer->FSFlushFileWrapper(request->handle);
break;
}
case FSA_COMMAND_CHANGE_DIR: {
auto *request = &param->shim->request.changeDir;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] ChangeDir: %s", layer->getName().c_str(), request->path);
setWorkingDir((FSAClientHandle) param->shim->clientHandle, request->path);
// We still want to call the original function.
layerResult = FS_ERROR_FORCE_PARENT_LAYER;
break;
}
case FSA_COMMAND_GET_CWD: {
DEBUG_FUNCTION_LINE_WARN("FSA_COMMAND_GET_CWD hook not implemented");
break;
}
case FSA_COMMAND_APPEND_FILE: {
auto *request = &param->shim->request.appendFile;
DEBUG_FUNCTION_LINE_WARN("FSA_COMMAND_APPEND_FILE hook not implemented for handle %08X", request->handle);
break;
}
case FSA_COMMAND_FLUSH_MULTI_QUOTA: {
DEBUG_FUNCTION_LINE_WARN("FSA_COMMAND_FLUSH_MULTI_QUOTA hook not implemented");
break;
}
case FSA_COMMAND_OPEN_FILE_BY_STAT: {
DEBUG_FUNCTION_LINE_WARN("FSA_COMMAND_OPEN_FILE_BY_STAT hook not implemented");
break;
}
case FSA_COMMAND_CHANGE_OWNER: {
DEBUG_FUNCTION_LINE_WARN("FSA_COMMAND_CHANGE_OWNER hook not implemented");
break;
}
case FSA_COMMAND_CHANGE_MODE: {
DEBUG_FUNCTION_LINE_WARN("FSA_COMMAND_CHANGE_OWNER hook not implemented");
break;
}
default: {
break;
}
}
#pragma GCC diagnostic pop
if (layerResult != FS_ERROR_FORCE_PARENT_LAYER) {
auto maskedResult = (FSError) ((layerResult & FS_ERROR_REAL_MASK) | FS_ERROR_EXTRA_MASK);
auto result = layerResult >= 0 ? static_cast<FSStatus>(layerResult) : fsaDecodeFsaStatusToFsStatus(maskedResult);
auto result = layerResult >= 0 ? layerResult : maskedResult;
if (result < FS_STATUS_OK && result != FS_STATUS_END && result != FS_STATUS_CANCELLED) {
if (result < FS_ERROR_OK && result != FS_ERROR_END_OF_FILE && result != FS_ERROR_END_OF_DIR && result != FS_ERROR_CANCELLED) {
if (layer->fallbackOnError()) {
// Only fallback if FS_ERROR_FORCE_NO_FALLBACK flag is not set.
if (static_cast<FSError>(layerResult & FS_ERROR_EXTRA_MASK) != FS_ERROR_FORCE_NO_FALLBACK) {
@ -165,78 +327,36 @@ FSStatus doForLayer(FSClient *client,
}
}
}
if (param->sync == FS_SHIM_TYPE_SYNC) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Return with result %08X %s", layer->getName().c_str(), result, result <= 0 ? FSAGetStatusStr(result) : "");
return result;
} else if (param->sync == FS_SHIM_TYPE_ASYNC) {
// convert to IOSError
auto err = (IOSError) result;
if (result == FS_ERROR_INVALID_BUFFER) {
err = IOS_ERROR_ACCESS;
} else if (result == FS_ERROR_INVALID_CLIENTHANDLE) {
err = IOS_ERROR_INVALID;
} else if (result == FS_ERROR_BUSY) {
err = IOS_ERROR_QFULL;
}
if (result >= FS_STATUS_OK || result == FS_STATUS_END || result == FS_STATUS_CANCELLED) {
DEBUG_FUNCTION_LINE_VERBOSE("Returned %08X by %s", result, layer->getName().c_str());
return result_handler(layer, result);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call async callback :) with result %08X %s", layer->getName().c_str(), err, result <= 0 ? FSAGetStatusStr(result) : "");
param->asyncFS.callback(err, &param->asyncFS);
return FS_ERROR_OK;
} else {
// This should never happen.
DEBUG_FUNCTION_LINE_ERR("Unknown sync type.");
OSFatal("ContentRedirectionModule: Unknown sync type.");
}
FSErrorFlag errorFlags = FS_ERROR_FLAG_NONE;
bool forceError = false;
switch ((int32_t) result) {
case FS_STATUS_MAX:
errorFlags = FS_ERROR_FLAG_MAX;
break;
case FS_STATUS_ALREADY_OPEN:
errorFlags = FS_ERROR_FLAG_ALREADY_OPEN;
break;
case FS_STATUS_EXISTS:
errorFlags = FS_ERROR_FLAG_EXISTS;
break;
case FS_STATUS_NOT_FOUND:
errorFlags = FS_ERROR_FLAG_NOT_FOUND;
break;
case FS_STATUS_NOT_FILE:
errorFlags = FS_ERROR_FLAG_NOT_FILE;
break;
case FS_STATUS_NOT_DIR:
errorFlags = FS_ERROR_FLAG_NOT_DIR;
break;
case FS_STATUS_ACCESS_ERROR:
errorFlags = FS_ERROR_FLAG_ACCESS_ERROR;
break;
case FS_STATUS_PERMISSION_ERROR:
errorFlags = FS_ERROR_FLAG_PERMISSION_ERROR;
break;
case FS_STATUS_FILE_TOO_BIG:
errorFlags = FS_ERROR_FLAG_FILE_TOO_BIG;
break;
case FS_STATUS_STORAGE_FULL:
errorFlags = FS_ERROR_FLAG_STORAGE_FULL;
break;
case FS_STATUS_JOURNAL_FULL:
errorFlags = FS_ERROR_FLAG_JOURNAL_FULL;
break;
case FS_STATUS_UNSUPPORTED_CMD:
errorFlags = FS_ERROR_FLAG_UNSUPPORTED_CMD;
break;
case FS_STATUS_MEDIA_NOT_READY:
case FS_STATUS_MEDIA_ERROR:
case FS_STATUS_CORRUPTED:
case FS_STATUS_FATAL_ERROR:
forceError = true;
break;
case FS_STATUS_OK:
break;
}
if (forceError || (realErrorMask != FS_ERROR_FLAG_NONE && (errorFlags & realErrorMask) == 0)) {
DEBUG_FUNCTION_LINE_ERR("Transit to Fatal Error");
auto clientBody = fsClientGetBody(client);
fsClientHandleFatalErrorAndBlock(clientBody, clientBody->lastError);
return FS_STATUS_FATAL_ERROR;
}
DEBUG_FUNCTION_LINE_VERBOSE("%08X Returned %08X by %s ", errorMask, result, layer->getName().c_str());
return result_handler(layer, result);
} else {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call parent layer / real function", layer->getName().c_str());
}
}
}
}
auto mask = static_cast<FSErrorFlag>((realErrorMask & FS_ERROR_FLAG_REAL_MASK) | FS_ERROR_FLAG_FORCE_REAL);
return real_function(mask);
return FS_ERROR_FORCE_REAL_FUNCTION;
}
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock) {
@ -258,16 +378,14 @@ FSClientBody *fsClientGetBody(FSClient *client) {
return body;
}
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus status) {
FSStatus handleAsyncResult(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus status) {
if (asyncData->callback != nullptr) {
if (asyncData->ioMsgQueue != nullptr) {
DEBUG_FUNCTION_LINE_ERR("callback and ioMsgQueue both set.");
return FS_STATUS_FATAL_ERROR;
OSFatal("ContentRedirectionModule: callback and ioMsgQueue both set.");
}
// userCallbacks are called in the DefaultAppIOQueue.
asyncData->ioMsgQueue = OSGetDefaultAppIOQueue();
//DEBUG_FUNCTION_LINE("Force to OSGetDefaultAppIOQueue (%08X)", asyncData->ioMsgQueue);
}
if (asyncData->ioMsgQueue != nullptr) {
@ -284,6 +402,7 @@ FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asy
result->block = block;
result->status = status;
OSMemoryBarrier();
while (!OSSendMessage(asyncData->ioMsgQueue, (OSMessage *) &(result->ioMsg), OS_MESSAGE_FLAGS_NONE)) {
DEBUG_FUNCTION_LINE_ERR("Failed to send message");
}
@ -302,6 +421,7 @@ int64_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count)
int32_t curResult;
int64_t totalSize = 0;
while (sizeToRead > 0) {
curResult = read(handle, newBuffer, sizeToRead);
if (curResult < 0) {
DEBUG_FUNCTION_LINE_ERR("Reading %08X bytes from handle %08X failed. result %08X errno: %d ", size * count, handle, curResult, errno);
@ -317,9 +437,9 @@ int64_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count)
return totalSize;
}
int64_t writeFromBuffer(int32_t handle, void *buffer, size_t size, size_t count) {
int64_t writeFromBuffer(int32_t handle, const void *buffer, size_t size, size_t count) {
auto sizeToWrite = size * count;
void *ptr = buffer;
auto *ptr = buffer;
int32_t curResult;
int64_t totalSize = 0;
while (sizeToWrite > 0) {
@ -336,4 +456,130 @@ int64_t writeFromBuffer(int32_t handle, void *buffer, size_t size, size_t count)
sizeToWrite -= curResult;
}
return totalSize;
}
FSIOThreadData gThreadData[3];
bool gThreadsRunning = false;
static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv) {
auto *magic = ((FSIOThreadData *) argv);
DEBUG_FUNCTION_LINE_VERBOSE("Hello from IO Thread for core: %d", OSGetCoreId());
constexpr int32_t messageSize = sizeof(magic->messages) / sizeof(magic->messages[0]);
OSInitMessageQueue(&magic->queue, magic->messages, messageSize);
OSMessage recv;
while (OSReceiveMessage(&magic->queue, &recv, OS_MESSAGE_FLAGS_BLOCKING)) {
if (recv.args[0] == FS_IO_QUEUE_COMMAND_STOP) {
DEBUG_FUNCTION_LINE_VERBOSE("Received break command! Stop thread");
break;
} else if (recv.args[0] == FS_IO_QUEUE_COMMAND_PROCESS_FS_COMMAND) {
auto *message = (FSShimWrapperMessage *) recv.message;
auto *param = (FSShimWrapper *) message->param;
FSError res = FS_ERROR_MEDIA_ERROR;
auto syncType = param->sync;
if (param->api == FS_SHIM_API_FS) {
res = processShimBufferForFS(param);
} else if (param->api == FS_SHIM_API_FSA) {
res = processShimBufferForFSA(param);
} else {
DEBUG_FUNCTION_LINE_ERR("Incompatible API type %d", param->api);
OSFatal("ContentRedirectionModule: Incompatible API type");
}
// param is free'd at this point!!!
if (syncType == FS_SHIM_TYPE_SYNC) {
// For sync messages we can't (and don't need to) free "message", because it contains the queue we're about to use.
// But this is not a problem because it's sync anyway.
OSMessage send;
send.args[0] = FS_IO_QUEUE_SYNC_RESULT;
send.args[1] = (uint32_t) res;
if (!OSSendMessage(&message->messageQueue, &send, OS_MESSAGE_FLAGS_NONE)) {
DEBUG_FUNCTION_LINE_ERR("Failed to send message");
OSFatal("ContentRedirectionModule: Failed to send message");
}
} else if (syncType == FS_SHIM_TYPE_ASYNC) {
// If it's async we need to clean up "message" :)
free(message);
}
}
}
return 0;
}
void startFSIOThreads() {
int32_t threadAttributes[] = {OS_THREAD_ATTRIB_AFFINITY_CPU0, OS_THREAD_ATTRIB_AFFINITY_CPU1, OS_THREAD_ATTRIB_AFFINITY_CPU2};
auto stackSize = 16 * 1024;
int coreId = 0;
for (int core : threadAttributes) {
auto *threadData = &gThreadData[coreId];
memset(threadData, 0, sizeof(*threadData));
threadData->setup = false;
threadData->thread = (OSThread *) memalign(8, sizeof(OSThread));
if (!threadData->thread) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate threadData");
OSFatal("ContentRedirectionModule: Failed to allocate IO Thread");
continue;
}
threadData->stack = (uint8_t *) memalign(0x20, stackSize);
if (!threadData->thread) {
free(threadData->thread);
DEBUG_FUNCTION_LINE_ERR("Failed to allocate threadData stack");
OSFatal("ContentRedirectionModule: Failed to allocate IO Thread stack");
continue;
}
OSMemoryBarrier();
if (!OSCreateThread(threadData->thread, &fsIOthreadCallback, 1, (char *) threadData, reinterpret_cast<void *>((uint32_t) threadData->stack + stackSize), stackSize, 0, core)) {
free(threadData->thread);
free(threadData->stack);
threadData->setup = false;
DEBUG_FUNCTION_LINE_ERR("failed to create threadData");
OSFatal("ContentRedirectionModule: Failed to create threadData");
}
strncpy(threadData->threadName, string_format("ContentRedirection IO Thread %d", coreId).c_str(), sizeof(threadData->threadName) - 1);
OSSetThreadName(threadData->thread, threadData->threadName);
OSResumeThread(threadData->thread);
threadData->setup = true;
coreId++;
}
gThreadsRunning = true;
OSMemoryBarrier();
}
void stopFSIOThreads() {
if (!gThreadsRunning) {
return;
}
for (auto &gThread : gThreadData) {
auto *thread = &gThread;
if (!thread->setup) {
continue;
}
OSMessage message;
message.args[0] = FS_IO_QUEUE_COMMAND_STOP;
OSSendMessage(&thread->queue, &message, OS_MESSAGE_FLAGS_NONE);
if (OSIsThreadSuspended(thread->thread)) {
OSResumeThread(thread->thread);
}
OSJoinThread(thread->thread, nullptr);
if (thread->stack) {
free(thread->stack);
thread->stack = nullptr;
}
if (thread->thread) {
free(thread->thread);
thread->thread = nullptr;
}
}
gThreadsRunning = false;
}

View File

@ -1,73 +1,110 @@
#pragma once
#include "IFSWrapper.h"
#include "utils/logger.h"
#include <coreinit/core.h>
#include <coreinit/filesystem.h>
#include <coreinit/filesystem_fsa.h>
#include <functional>
#include <mutex>
#include <romfs_dev.h>
#include <string>
struct FSIOThreadData {
OSThread *thread;
void *stack;
OSMessageQueue queue;
OSMessage messages[0x10];
bool setup;
char threadName[0x50];
};
struct AsyncParamFS {
FSClient *client;
FSCmdBlock *block;
FSErrorFlag errorMask;
FSAsyncData asyncData;
IOSAsyncCallbackFn callback;
};
typedef enum FSShimSyncType {
FS_SHIM_TYPE_SYNC = 1,
FS_SHIM_TYPE_ASYNC = 2
} FSShimSyncType;
typedef enum FSShimApiType {
FS_SHIM_API_FS = 1,
FS_SHIM_API_FSA = 2
} FSShimApiType;
struct FSShimWrapper {
FSAShimBuffer *shim;
AsyncParamFS asyncFS;
FSShimSyncType sync;
FSShimApiType api;
};
struct FSShimWrapperMessage {
FSShimWrapper *param;
OSMessageQueue messageQueue;
OSMessage messages[0x1];
};
#define FS_IO_QUEUE_COMMAND_STOP 0x13371337
#define FS_IO_QUEUE_COMMAND_PROCESS_FS_COMMAND 0x42424242
#define FS_IO_QUEUE_SYNC_RESULT 0x43434343
extern bool gThreadsRunning;
extern FSIOThreadData gThreadData[3];
extern std::mutex fsLayerMutex;
extern std::vector<std::unique_ptr<IFSWrapper>> fsLayers;
#define SYNC_RESULT_HANDLER [filename = __FILENAME__, func = __FUNCTION__, line = __LINE__]([[maybe_unused]] std::unique_ptr<IFSWrapper> &layer, FSStatus res) -> FSStatus { \
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Sync result was %d", res); \
return res; \
}
#define fsaShimPrepareRequestReadFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, uint8_t * buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAReadFlag readFlags))(0x101C400 + 0x436cc))
#define fsaShimPrepareRequestWriteFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAWriteFlag writeFlags))(0x101C400 + 0x437f4))
#define fsaShimPrepareRequestOpenFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *path, const char *mode, FSMode createMode, FSOpenFileFlags openFlag, uint32_t preallocSize))(0x101C400 + 0x43588))
#define fsaShimPrepareRequestCloseFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSFileHandle handle))(0x101C400 + 0x43a00))
#define fsaShimPrepareRequestStatFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSFileHandle handle))(0x101C400 + 0x43998))
#define fsaShimPrepareRequestQueryInfo ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *path, FSAQueryInfoType type))(0x101C400 + 0x44118))
#define fsaShimPrepareRequestSetPos ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSFileHandle handle, FSAFilePosition position))(0x101C400 + 0x43930))
#define fsaShimPrepareRequestGetPos ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSFileHandle handle))(0x101C400 + 0x438fc))
#define fsaShimPrepareRequestIsEof ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSFileHandle handle))(0x101C400 + 0x43964))
#define fsaShimPrepareRequestTruncate ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSFileHandle handle))(0x101C400 + 0x43a34))
#define fsaShimPrepareRequestRemove ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *))(0x101C400 + 0x43aa8))
#define fsaShimPrepareRequestRename ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *, const char *))(0x101C400 + 0x43bc0))
#define fsaShimPrepareRequestFlushFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSFileHandle handle))(0x101C400 + 0x439cc))
#define fsaShimPrepareRequestChangeMode ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *path, FSMode mode, FSMode modeMask))(0x101C400 + 0x43ff4))
#define ASYNC_RESULT_HANDLER [c = client, b = block, a = asyncData, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__]([[maybe_unused]] std::unique_ptr<IFSWrapper> &layer, FSStatus res) -> FSStatus { \
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Async result was %d", res); \
return send_result_async(c, b, a, res); \
}
#define fsaShimPrepareRequestOpenDir ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *path))(0x101C400 + 0x43458))
#define fsaShimPrepareRequestReadDir ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSDirectoryHandle handle))(0x101C400 + 0x434ec))
#define fsaShimPrepareRequestCloseDir ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSDirectoryHandle handle))(0x101C400 + 0x43554))
#define fsaShimPrepareRequestRewindDir ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, FSDirectoryHandle handle))(0x101C400 + 0x43520))
#define fsaShimPrepareRequestMakeDir ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *path, FSMode mode))(0x101C400 + 0x43314))
#define fsaShimPrepareRequestChangeDir ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const char *path))(0x101C400 + 0x43258))
#define SYNC_RESULT_HANDLER_FSA [filename = __FILENAME__, func = __FUNCTION__, line = __LINE__]([[maybe_unused]] std::unique_ptr<IFSWrapper> &layer, FSError res) -> FSError { \
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Sync result was %d", res); \
return res; \
}
#define fsaDecodeFsaStatusToFsStatus ((FSStatus(*)(FSError))(0x101C400 + 0x4b148))
#define fsClientHandleFatalError ((void (*)(FSClientBody *, uint32_t))(0x101C400 + 0x4b34c))
#define fsClientHandleFatalErrorAndBlock ((void (*)(FSClientBody *, uint32_t))(0x101C400 + 0x4cc20))
#define FS_ERROR_FLAG_EXTRA_MASK (FSErrorFlag) 0xFFFF0000
#define FS_ERROR_FLAG_REAL_MASK (FSErrorFlag) 0x0000FFFF
#define FS_ERROR_FLAG_FORCE_REAL (FSErrorFlag) 0xFEDC0000
extern "C" FSError __FSAShimDecodeIosErrorToFsaStatus(IOSHandle handle, IOSError err);
static inline FSErrorFlag getRealErrorFlag(FSErrorFlag flag) {
auto res = flag & FS_ERROR_FLAG_REAL_MASK;
if (res == 0x0000FFFF) {
return FS_ERROR_FLAG_ALL;
}
return static_cast<FSErrorFlag>(res);
}
static inline FSErrorFlag isForceRealFunction(FSErrorFlag flag) {
return static_cast<FSErrorFlag>((flag & FS_ERROR_FLAG_EXTRA_MASK) == FS_ERROR_FLAG_FORCE_REAL);
}
bool sendMessageToThread(FSShimWrapperMessage *param);
void clearFSLayer();
std::string getFullPathForFSClient(FSClient *pClient, const char *path);
FSError doForLayer(FSShimWrapper *param);
void setWorkingDirForFSClient(FSClient *client, const char *path);
FSError processShimBufferForFS(FSShimWrapper *param);
std::string getFullPathForFSAClient(FSAClientHandle client, const char *path);
void setWorkingDirForFSAClient(FSAClientHandle client, const char *path);
FSError doForLayerFSA(const std::function<FSError()> &real_function,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer)> &layer_callback,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer, FSError)> &result_handler);
FSStatus doForLayer(FSClient *client,
FSErrorFlag errorMask,
const std::function<FSStatus(FSErrorFlag errorMask)> &real_function,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer)> &layer_callback,
const std::function<FSStatus(std::unique_ptr<IFSWrapper> &layer, FSStatus)> &result_handler);
FSError processShimBufferForFSA(FSShimWrapper *param);
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock);
FSClientBody *fsClientGetBody(FSClient *client);
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus result);
FSStatus handleAsyncResult(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus status);
int64_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count);
int64_t writeFromBuffer(int32_t handle, void *buffer, size_t size, size_t count);
int64_t writeFromBuffer(int32_t handle, const void *buffer, size_t size, size_t count);
void startFSIOThreads();
void stopFSIOThreads();

View File

@ -1,12 +1,13 @@
#pragma once
#include <coreinit/filesystem.h>
#include <coreinit/filesystem_fsa.h>
#include <functional>
#include <string>
#define FS_ERROR_EXTRA_MASK 0xFFF00000
#define FS_ERROR_REAL_MASK 0x000FFFFF
#define FS_ERROR_FORCE_PARENT_LAYER (FSError) 0xFFE0000
#define FS_ERROR_FORCE_NO_FALLBACK (FSError) 0xFFD0000
#define FS_ERROR_EXTRA_MASK 0xFFF00000
#define FS_ERROR_REAL_MASK 0x000FFFFF
#define FS_ERROR_FORCE_PARENT_LAYER (FSError) 0xFFE00000
#define FS_ERROR_FORCE_NO_FALLBACK (FSError) 0xFFD00000
#define FS_ERROR_FORCE_REAL_FUNCTION (FSError) 0xFFC00000
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
@ -15,21 +16,21 @@ class IFSWrapper {
public:
virtual ~IFSWrapper() = default;
virtual FSError FSOpenDirWrapper(const char *path,
FSDirectoryHandle *handle) {
FSADirectoryHandle *handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSReadDirWrapper(FSDirectoryHandle handle,
FSDirectoryEntry *entry) {
virtual FSError FSReadDirWrapper(FSADirectoryHandle handle,
FSADirectoryEntry *entry) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSCloseDirWrapper(FSDirectoryHandle handle) {
virtual FSError FSCloseDirWrapper(FSADirectoryHandle handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSRewindDirWrapper(FSDirectoryHandle handle) {
virtual FSError FSRewindDirWrapper(FSADirectoryHandle handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
@ -39,29 +40,29 @@ public:
virtual FSError FSOpenFileWrapper(const char *path,
const char *mode,
FSFileHandle *handle) {
FSAFileHandle *handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSCloseFileWrapper(FSFileHandle handle) {
virtual FSError FSCloseFileWrapper(FSAFileHandle handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSGetStatWrapper(const char *path,
FSStat *stats) {
FSAStat *stats) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSGetStatFileWrapper(FSFileHandle handle,
FSStat *stats) {
virtual FSError FSGetStatFileWrapper(FSAFileHandle handle,
FSAStat *stats) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSReadFileWrapper(void *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
FSAFileHandle handle,
uint32_t unk1) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
@ -70,37 +71,46 @@ public:
uint32_t size,
uint32_t count,
uint32_t pos,
FSFileHandle handle,
FSAFileHandle handle,
int32_t unk1) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSSetPosFileWrapper(FSFileHandle handle,
virtual FSError FSSetPosFileWrapper(FSAFileHandle handle,
uint32_t pos) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSGetPosFileWrapper(FSFileHandle handle,
virtual FSError FSGetPosFileWrapper(FSAFileHandle handle,
uint32_t *pos) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSIsEofWrapper(FSFileHandle handle) {
virtual FSError FSIsEofWrapper(FSAFileHandle handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSTruncateFileWrapper(FSFileHandle handle) {
virtual FSError FSTruncateFileWrapper(FSAFileHandle handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSWriteFileWrapper(uint8_t *buffer,
virtual FSError FSWriteFileWrapper(const uint8_t *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
FSAFileHandle handle,
uint32_t unk1) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSWriteFileWithPosWrapper(const uint8_t *buffer,
uint32_t size,
uint32_t count,
FSAFilePosition pos,
FSAFileHandle handle,
uint32_t unk1) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSRemoveWrapper(const char *path) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
@ -110,7 +120,7 @@ public:
return FS_ERROR_FORCE_PARENT_LAYER;
}
virtual FSError FSFlushFileWrapper(FSFileHandle handle) {
virtual FSError FSFlushFileWrapper(FSAFileHandle handle) {
return FS_ERROR_FORCE_PARENT_LAYER;
}
@ -130,15 +140,17 @@ public:
return pName;
}
virtual bool isValidDirHandle(FSDirectoryHandle handle) = 0;
virtual bool isValidDirHandle(FSADirectoryHandle handle) = 0;
virtual bool isValidFileHandle(FSFileHandle handle) = 0;
virtual bool isValidFileHandle(FSAFileHandle handle) = 0;
virtual void deleteDirHandle(FSDirectoryHandle handle) = 0;
virtual void deleteDirHandle(FSADirectoryHandle handle) = 0;
virtual void deleteFileHandle(FSFileHandle handle) = 0;
virtual void deleteFileHandle(FSAFileHandle handle) = 0;
uint32_t getHandle() {
virtual uint32_t getLayerId() = 0;
virtual uint32_t getHandle() {
return (uint32_t) this;
}

View File

@ -2,24 +2,130 @@
#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_ERR("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG");
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG");
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
}
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,8 +142,8 @@ 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; })) {
DEBUG_FUNCTION_LINE_ERR("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", 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;
}
return CONTENT_REDIRECTION_API_ERROR_NONE;
@ -52,13 +158,12 @@ ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) {
}
}
DEBUG_FUNCTION_LINE_ERR("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);
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
}
ContentRedirectionApiErrorType CRGetVersion(ContentRedirectionVersion *outVersion) {
if (outVersion == nullptr) {
DEBUG_FUNCTION_LINE_ERR("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG");
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
}
*outVersion = 1;

View File

@ -1,46 +1,65 @@
#include "FSADirReplacements.h"
#include "FSAFileReplacements.h"
#include "FSDirReplacements.h"
#include "FSFileReplacements.h"
#include "FSAReplacements.h"
#include "FSReplacements.h"
#include "FileUtils.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "version.h"
#include <wums.h>
WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection");
WUMS_USE_WUT_DEVOPTAB();
WUMS_DEPENDS_ON(homebrew_functionpatcher);
#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");
}
}
for (uint32_t i = 0; i < fs_dir_function_replacements_size; i++) {
if (!FunctionPatcherPatchFunction(&fs_dir_function_replacements[i], nullptr)) {
OSFatal("homebrew_content_redirection: Failed to patch function");
}
}
for (uint32_t i = 0; i < fsa_dir_function_replacements_size; i++) {
if (!FunctionPatcherPatchFunction(&fsa_dir_function_replacements[i], nullptr)) {
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();
}
WUMS_APPLICATION_STARTS() {
OSReport("Running ContentRedirectionModule " VERSION VERSION_EXTRA "\n");
initLogging();
startFSIOThreads();
}
WUMS_APPLICATION_ENDS() {
clearFSLayer();
stopFSIOThreads();
deinitLogging();
}
}

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);
});
}

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);

2
src/version.h Normal file
View File

@ -0,0 +1,2 @@
#pragma once
#define VERSION_EXTRA ""