first commit

This commit is contained in:
Maschell 2022-04-14 22:41:41 +02:00
commit 7c9c0348fe
28 changed files with 3184 additions and 0 deletions

67
.clang-format Normal file
View File

@ -0,0 +1,67 @@
# Generated from CLion C/C++ Code Style settings
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: Consecutive
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 8
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Right
ReflowComments: false
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never

65
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: CI-Release
on:
push:
branches:
- main
jobs:
clang-format:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./src
build-binary:
runs-on: ubuntu-18.04
needs: clang-format
steps:
- uses: actions/checkout@v2
- name: build binary
run: |
docker build . -t builder
docker run --rm -v ${PWD}:/project builder make
- uses: actions/upload-artifact@master
with:
name: binary
path: "*.wms"
deploy-binary:
needs: build-binary
runs-on: ubuntu-18.04
steps:
- name: Get environment variables
id: get_repository_name
run: |
echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//") >> $GITHUB_ENV
echo DATETIME=$(echo $(date '+%Y%m%d-%H%M%S')) >> $GITHUB_ENV
- uses: actions/download-artifact@master
with:
name: binary
- 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 }}
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

25
.github/workflows/pr.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: CI-PR
on: [pull_request]
jobs:
clang-format:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./src
build-binary:
runs-on: ubuntu-18.04
needs: clang-format
steps:
- uses: actions/checkout@v2
- name: build binary
run: |
docker build . -t builder
docker run --rm -v ${PWD}:/project builder make
- uses: actions/upload-artifact@master
with:
name: binary
path: "*.wms"

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
*.cbp
*.elf
*.layout
*.rpx
build/
*.save-failed
.idea/
cmake-build-debug/
CMakeLists.txt
*.wms

8
Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM wiiuenv/devkitppc:20220303
COPY --from=wiiuenv/libfunctionpatcher:20220211 /artifacts $DEVKITPRO
COPY --from=wiiuenv/wiiumodulesystem:20220204 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libromfs_wiiu:20220414 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libcontentredirection:20220414 /artifacts $DEVKITPRO
WORKDIR project

147
Makefile Normal file
View File

@ -0,0 +1,147 @@
#-------------------------------------------------------------------------------
.SUFFIXES:
#-------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/wums/share/wums_rules
WUMS_ROOT := $(DEVKITPRO)/wums
WUT_ROOT := $(DEVKITPRO)/wut
#-------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#-------------------------------------------------------------------------------
TARGET := ContentRedirectionModule
BUILD := build
SOURCES := src \
src/utils
DATA := data
INCLUDES := src
#-------------------------------------------------------------------------------
# options for code generation
#-------------------------------------------------------------------------------
CFLAGS := -Wall -Wextra -Os -ffunction-sections\
$(MACHDEP)
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)
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -g
CFLAGS += -DDEBUG -g
endif
LIBS := -lwums -lwut -lfunctionpatcher
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
# containing include and lib
#-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUT_ROOT)/usr $(WUMS_ROOT)
#-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#-------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#-------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#-------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#-------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#-------------------------------------------------------------------------------
export LD := $(CC)
#-------------------------------------------------------------------------------
else
#-------------------------------------------------------------------------------
export LD := $(CXX)
#-------------------------------------------------------------------------------
endif
#-------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean all
#-------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#-------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).wms $(TARGET).elf
#-------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#-------------------------------------------------------------------------------
# main targets
#-------------------------------------------------------------------------------
all : $(OUTPUT).wms
$(OUTPUT).wms : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#-------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#-------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#-------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
%.o: %.s
@echo $(notdir $<)
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
-include $(DEPENDS)
#-------------------------------------------------------------------------------
endif
#-------------------------------------------------------------------------------

27
README.md Normal file
View File

@ -0,0 +1,27 @@
[![CI-Release](https://github.com/wiiu-env/ContentRedirectionModule/actions/workflows/ci.yml/badge.svg)](https://github.com/wiiu-env/ContentRedirectionModule/actions/workflows/ci.yml)
## Usage
(`[ENVIRONMENT]` is a placeholder for the actual environment name.)
1. Copy the file `ContentRedirectionModule.wms` into `sd:/wiiu/environments/[ENVIRONMENT]/modules`.
2. Requires the [WUMSLoader](https://github.com/wiiu-env/WUMSLoader) in `sd:/wiiu/environments/[ENVIRONMENT]/modules/setup`.
3. Use [libcontentredirection](https://github.com/wiiu-env/libcontentredirection).
## Building using the Dockerfile
It's possible to use a docker image for building. This way you don't need anything installed on your host system.
```
# Build docker image (only needed once)
docker build . -t contentredirectionmodule-builder
# make
docker run -it --rm -v ${PWD}:/project contentredirectionmodule-builder make
# make clean
docker run -it --rm -v ${PWD}:/project contentredirectionmodule-builder make clean
```
## Format the code via docker
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./src -i`

10
src/DirInfo.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <coreinit/filesystem.h>
#include <sys/dirent.h>
struct DirInfo {
virtual ~DirInfo() = default;
FSDirectoryHandle handle{};
DIR *dir{};
char path[0x280]{};
};

16
src/DirInfoEx.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "DirInfo.h"
#include <coreinit/filesystem.h>
typedef struct FSDirectoryEntryEx {
FSDirectoryEntry realEntry{};
bool isMarkedAsDeleted = false;
} FSDirectoryEntryEx;
struct DirInfoEx : public DirInfo {
public:
FSDirectoryEntryEx *readResult = nullptr;
int readResultCapacity = 0;
int readResultNumberOfEntries = 0;
FSDirectoryHandle realDirHandle = 0;
};

220
src/FSDirReplacements.cpp Normal file
View File

@ -0,0 +1,220 @@
#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, 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 {
auto res = real_FSOpenDir(c, b, p, h, realErrorMask);
return res;
},
[f = getFullPathForClient(client, path), h = handle](IFSWrapper *layer) -> FSStatus {
return layer->FSOpenDirWrapper(f.c_str(), h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSOpenDirAsync, FSClient *client, FSCmdBlock *block, 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 = getFullPathForClient(client, path), h = handle](IFSWrapper *layer) -> FSStatus {
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](IFSWrapper *layer) -> FSStatus {
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](IFSWrapper *layer) -> FSStatus {
return layer->FSReadDirWrapper(h, e);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSCloseDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("", errorMask);
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](IFSWrapper *layer) -> FSStatus {
return layer->FSCloseDirWrapper(h);
},
[h = handle, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](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](IFSWrapper *layer) -> FSStatus {
return layer->FSCloseDirWrapper(h);
},
[c = client, b = block, h = handle, a = asyncData, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](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](IFSWrapper *layer) -> FSStatus {
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](IFSWrapper *layer) -> FSStatus {
return layer->FSRewindDirWrapper(h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSMakeDir, FSClient *client, FSCmdBlock *block, 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 = getFullPathForClient(client, path)](IFSWrapper *layer) -> FSStatus {
return layer->FSMakeDirWrapper(f.c_str());
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSMakeDirAsync, FSClient *client, FSCmdBlock *block, 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 = getFullPathForClient(client, path)](IFSWrapper *layer) -> FSStatus {
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);
setWorkingDir(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);

11
src/FSDirReplacements.h Normal file
View File

@ -0,0 +1,11 @@
#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;

579
src/FSFileReplacements.cpp Normal file
View File

@ -0,0 +1,579 @@
#include "FSFileReplacements.h"
#include "FileUtils.h"
#include "utils/logger.h"
#include <coreinit/dynload.h>
#include <cstdarg>
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 = getFullPathForClient(client, path), m = mode, h = handle](IFSWrapper *layer) -> FSStatus {
return layer->FSOpenFileWrapper(f.c_str(), m, h);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSOpenFileAsync, FSClient *client, FSCmdBlock *block, 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, getRealErrorFlag(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 = getFullPathForClient(client, path), m = mode, h = handle](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
return layer->FSCloseFileWrapper(h);
},
[h = handle, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](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("");
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](IFSWrapper *layer) -> FSStatus {
return layer->FSCloseFileWrapper(h);
},
[c = client, b = block, h = handle, a = asyncData, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__](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, 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 = getFullPathForClient(client, path), s = stats](IFSWrapper *layer) -> FSStatus {
return layer->FSGetStatWrapper(p.c_str(), s);
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSGetStatAsync, FSClient *client, FSCmdBlock *block, 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 = getFullPathForClient(client, path), s = stats](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
return layer->FSGetPosFileWrapper(h, p);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSIsEof, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
DEBUG_FUNCTION_LINE_VERBOSE("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
return layer->FSTruncateFileWrapper(h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRemove, FSClient *client, FSCmdBlock *block, 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 = getFullPathForClient(client, path)](IFSWrapper *layer) -> FSStatus {
return layer->FSRemoveWrapper(p.c_str());
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRemoveAsync, FSClient *client, FSCmdBlock *block, 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 = getFullPathForClient(client, path)](IFSWrapper *layer) -> FSStatus {
return layer->FSRemoveWrapper(p.c_str());
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRename, FSClient *client, FSCmdBlock *block, char *oldPath, 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 = getFullPathForClient(client, oldPath), nP = getFullPathForClient(client, newPath)](IFSWrapper *layer) -> FSStatus {
return layer->FSRenameWrapper(oP.c_str(), nP.c_str());
},
SYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSRenameAsync,
FSClient *client,
FSCmdBlock *block,
char *oldPath,
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 = getFullPathForClient(client, oldPath), nP = getFullPathForClient(client, newPath)](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
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("");
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](IFSWrapper *layer) -> FSStatus {
return layer->FSFlushFileWrapper(h);
},
ASYNC_RESULT_HANDLER);
}
DECL_FUNCTION(FSStatus, FSChangeModeAsync,
FSClient *client,
FSCmdBlock *block,
char *path,
[[maybe_unused]] FSMode mode,
[[maybe_unused]] FSMode modeMask,
[[maybe_unused]] FSErrorFlag errorMask,
FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED FSChangeModeAsync %s mode: %08X", path, mode);
return real_FSChangeModeAsync(client, block, path, mode, modeMask, errorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSGetFreeSpaceSizeAsync, FSClient *client, FSCmdBlock *block, char *path, [[maybe_unused]] uint64_t *outSize, [[maybe_unused]] FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED FSGetFreeSpaceSizeAsync %s", path);
return real_FSGetFreeSpaceSizeAsync(client, block, path, outSize, errorMask, asyncData);
}
DECL_FUNCTION(void, OSSetExceptionCallbackEx, void) {
return;
}
function_replacement_data_t fs_file_function_replacements[] = {
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(OSSetExceptionCallbackEx, LIBRARY_COREINIT, OSSetExceptionCallbackEx),
//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);

18
src/FSFileReplacements.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <function_patcher/function_patching.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern function_replacement_data_t fs_file_function_replacements[];
extern uint32_t fs_file_function_replacements_size;
extern function_replacement_data_t test_function_replacements[];
extern uint32_t test_function_replacements_size;
#ifdef __cplusplus
}
#endif

714
src/FSWrapper.cpp Normal file
View File

@ -0,0 +1,714 @@
#include "FSWrapper.h"
#include "FileUtils.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include <algorithm>
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <cstdio>
#include <filesystem>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/unistd.h>
FSStatus FSWrapper::FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle) {
if (!IsPathToReplace(path)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (handle == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] handle was nullptr", getName().c_str());
return FS_STATUS_FATAL_ERROR;
}
FSStatus result = FS_STATUS_OK;
auto *dirHandle = getNewDirHandle();
if (dirHandle != nullptr) {
DIR *dir;
auto newPath = GetNewPath(path);
if ((dir = opendir(newPath.c_str()))) {
dirHandle->dir = dir;
dirHandle->handle = (((uint32_t) dirHandle) & 0x0FFFFFFF) | 0x30000000;
*handle = dirHandle->handle;
dirHandle->path[0] = '\0';
strncat(dirHandle->path, newPath.c_str(), sizeof(dirHandle->path) - 1);
{
std::lock_guard<std::mutex> lock(openDirsMutex);
openDirs.push_back(dirHandle);
OSMemoryBarrier();
}
} else {
delete dirHandle;
result = FS_STATUS_NOT_FOUND;
}
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc dir handle", getName().c_str());
result = FS_STATUS_MAX;
}
return result;
}
FSStatus FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry) {
if (!isValidDirHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *dirHandle = getDirFromHandle(handle);
DIR *dir = dirHandle->dir;
FSStatus result = FS_STATUS_END;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] readdir %08X (handle %08X)", getName().c_str(), dir, handle);
do {
struct dirent *entry_ = readdir(dir);
if (entry_) {
if (SkipDeletedFilesInReadDir() && std::string_view(entry_->d_name).starts_with(deletePrefix)) {
DEBUG_FUNCTION_LINE_ERR("Skip file file name %s because of the prefix", entry_->d_name);
continue;
}
entry->name[0] = '\0';
strncat(entry->name, entry_->d_name, sizeof(entry->name) - 1);
entry->info.mode = (FSMode) FS_MODE_READ_OWNER;
if (entry_->d_type == DT_DIR) {
entry->info.flags = (FSStatFlags) ((uint32_t) FS_STAT_DIRECTORY);
entry->info.size = 0;
} else {
entry->info.flags = (FSStatFlags) 0;
if (strcmp(entry_->d_name, ".") == 0 || strcmp(entry_->d_name, "..") == 0) {
entry->info.size = 0;
} else {
struct stat sb {};
auto strLen = strlen(dirHandle->path) + 1 + strlen(entry_->d_name) + 1;
char path[strLen];
snprintf(path, sizeof(path), "%s/%s", dirHandle->path, entry_->d_name);
if (stat(path, &sb) >= 0) {
entry->info.size = sb.st_size;
entry->info.flags = (FSStatFlags) 0;
entry->info.owner = sb.st_uid;
entry->info.group = sb.st_gid;
}
}
}
result = FS_STATUS_OK;
}
break;
} while (true);
return result;
}
FSStatus FSWrapper::FSCloseDirWrapper(FSDirectoryHandle handle) {
if (!isValidDirHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *dirHandle = getDirFromHandle(handle);
DIR *dir = dirHandle->dir;
FSStatus result = FS_STATUS_OK;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] closedir %08X (handle %08X)", getName().c_str(), dir, handle);
if (closedir(dir) != 0) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close dir %08X (handle %08X)", getName().c_str(), dir, handle);
result = FS_STATUS_MEDIA_ERROR;
}
dirHandle->dir = nullptr;
return result;
}
FSStatus FSWrapper::FSRewindDirWrapper(FSDirectoryHandle handle) {
if (!isValidDirHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *dirHandle = getDirFromHandle(handle);
DIR *dir = dirHandle->dir;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] rewindir %08X (handle %08X)", getName().c_str(), dir, handle);
rewinddir(dir);
return FS_STATUS_OK;
}
FSStatus FSWrapper::FSMakeDirWrapper(const char *path) {
if (!IsPathToReplace(path)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (!pIsWriteable) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to create dir %s but layer is not writeable", getName().c_str(), path);
return FS_STATUS_ACCESS_ERROR;
}
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED MAKE DIR");
return FS_STATUS_FATAL_ERROR;
}
FSStatus FSWrapper::FSOpenFileWrapper(const char *path, const char *mode, FSFileHandle *handle) {
if (!IsPathToReplace(path)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (path == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] path was nullptr", getName().c_str());
return FS_STATUS_FATAL_ERROR;
}
if (mode == nullptr || handle == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] mode or handle was nullptr", getName().c_str());
return FS_STATUS_FATAL_ERROR;
}
auto newPath = GetNewPath(path);
if (pCheckIfDeleted && CheckFileShouldBeIgnored(newPath)) {
return static_cast<FSStatus>((FS_STATUS_NOT_FOUND & 0x0000FFFF) | FS_STATUS_FORCE_NO_FALLBACK);
}
auto result = FS_STATUS_OK;
int _mode;
// Map flags to open modes
if (!IsFileModeAllowed(mode)) {
OSReport("## WARN ## [%s] Given mode is not allowed %s", getName().c_str(), mode);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Given mode is not allowed %s", getName().c_str(), mode);
return FS_STATUS_ACCESS_ERROR;
}
if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) {
_mode = 0x000;
} else if (strcmp(mode, "r+") == 0) {
_mode = 0x002;
} else if (strcmp(mode, "w") == 0) {
_mode = 0x601;
} else if (strcmp(mode, "w+") == 0) {
_mode = 0x602;
} else if (strcmp(mode, "a") == 0) {
_mode = 0x209;
} else if (strcmp(mode, "a+") == 0) {
_mode = 0x20A;
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] mode \"%s\" was allowed but is unsupported", getName().c_str(), mode);
return FS_STATUS_ACCESS_ERROR;
}
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Open %s (as %s) mode %s,", getName().c_str(), path, newPath.c_str(), mode);
int32_t fd = open(newPath.c_str(), _mode);
if (fd >= 0) {
auto *fileHandle = getNewFileHandle();
if (fileHandle) {
std::lock_guard<std::mutex> lock(openFilesMutex);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Opened %s (as %s) mode %s, fd %d (%08X)", getName().c_str(), path, newPath.c_str(), mode, fd, handle);
fileHandle->handle = (((uint32_t) fileHandle) & 0x0FFFFFFF) | 0x30000000;
*handle = fileHandle->handle;
fileHandle->fd = fd;
openFiles.push_back(fileHandle);
OSMemoryBarrier();
} else {
close(fd);
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc new fileHandle", getName().c_str());
result = FS_STATUS_MAX;
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] File not found %s (%s)", getName().c_str(), path, newPath.c_str());
result = FS_STATUS_NOT_FOUND;
}
return result;
}
FSStatus FSWrapper::FSCloseFileWrapper(FSFileHandle handle) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *fileHandle = getFileFromHandle(handle);
int real_fd = fileHandle->fd;
FSStatus result = FS_STATUS_OK;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Close %d (handle %08X)", getName().c_str(), real_fd, handle);
if (close(real_fd) != 0) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close %d (handle %08X) ", getName().c_str(), real_fd, handle);
result = FS_STATUS_MEDIA_ERROR;
}
fileHandle->fd = -1;
return result;
}
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());
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());
return true;
}
return false;
}
FSStatus FSWrapper::FSGetStatWrapper(const char *path, FSStat *stats) {
if (path == nullptr || stats == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] path was or stats nullptr", getName().c_str());
return FS_STATUS_FATAL_ERROR;
}
if (!IsPathToReplace(path)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto newPath = GetNewPath(path);
struct stat path_stat {};
if (pCheckIfDeleted && CheckFileShouldBeIgnored(newPath)) {
return static_cast<FSStatus>((FS_STATUS_NOT_FOUND & 0x0000FFFF) | FS_STATUS_FORCE_NO_FALLBACK);
}
FSStatus result = FS_STATUS_OK;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] stat of %s (%s)", getName().c_str(), path, newPath.c_str());
if (stat(newPath.c_str(), &path_stat) < 0) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Path %s (%s) not found ", getName().c_str(), path, newPath.c_str());
result = FS_STATUS_NOT_FOUND;
} else {
memset(&(stats->flags), 0, sizeof(stats->flags));
if (S_ISDIR(path_stat.st_mode)) {
stats->flags = (FSStatFlags) ((uint32_t) FS_STAT_DIRECTORY);
} else {
stats->size = path_stat.st_size;
stats->mode = (FSMode) FS_MODE_READ_OWNER;
stats->flags = (FSStatFlags) 0;
stats->owner = path_stat.st_uid;
stats->group = path_stat.st_gid;
}
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Stats file for %s (%s), size %016llX", getName().c_str(), path, newPath.c_str(), stats->size);
}
return result;
}
FSStatus FSWrapper::FSGetStatFileWrapper(FSFileHandle handle, FSStat *stats) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *fileHandle = getFileFromHandle(handle);
int real_fd = fileHandle->fd;
struct stat path_stat {};
FSStatus result = FS_STATUS_OK;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] fstat of fd %d (FSFileHandle %08X)", getName().c_str(), real_fd, handle);
if (fstat(real_fd, &path_stat) < 0) {
DEBUG_FUNCTION_LINE_ERR("[%s] fstat of fd %d (FSFileHandle %08X) failed", getName().c_str(), real_fd, handle);
result = FS_STATUS_MEDIA_ERROR;
} else {
memset(&(stats->flags), 0, sizeof(stats->flags));
stats->size = path_stat.st_size;
stats->mode = (FSMode) FS_MODE_READ_OWNER;
stats->flags = (FSStatFlags) 0;
stats->owner = path_stat.st_uid;
stats->group = path_stat.st_gid;
}
return result;
}
FSStatus FSWrapper::FSReadFileWrapper(void *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (size * count == 0) {
return FS_STATUS_OK;
}
if (buffer == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] buffer is null but size * count is not 0 (It's: %d)", getName().c_str(), size * count);
return FS_STATUS_FATAL_ERROR;
}
auto *fileHandle = getFileFromHandle(handle);
int real_fd = fileHandle->fd;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Read %u bytes of fd %08X (FSFileHandle %08X) to buffer %08X", getName().c_str(), size * count, real_fd, handle, buffer);
int32_t read = readIntoBuffer(real_fd, buffer, size, count);
FSStatus 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);
result = FS_STATUS_MEDIA_ERROR;
} else {
result = static_cast<FSStatus>((uint32_t) (read & 0xFFFFFFFF) / size);
}
return result;
}
FSStatus FSWrapper::FSReadFileWithPosWrapper(void *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, int32_t unk1) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Read from with position.", getName().c_str());
FSStatus result;
if ((result = this->FSSetPosFileWrapper(handle, pos)) != FS_STATUS_OK) {
return result;
}
result = this->FSReadFileWrapper(buffer, size, count, handle, unk1);
return result;
}
FSStatus FSWrapper::FSSetPosFileWrapper(FSFileHandle handle, uint32_t pos) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *fileHandle = getFileFromHandle(handle);
FSStatus result = FS_STATUS_OK;
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);
if (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);
result = FS_STATUS_MEDIA_ERROR;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] pos set to %u for fd %d (FSFileHandle %08X)", getName().c_str(), pos, real_fd, handle);
}
return result;
}
FSStatus FSWrapper::FSGetPosFileWrapper(FSFileHandle handle, uint32_t *pos) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *fileHandle = getFileFromHandle(handle);
FSStatus result = FS_STATUS_OK;
int real_fd = fileHandle->fd;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
if (currentPos == -1) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to get current position (res: %lld) of fd (handle %08X) to check EoF", getName().c_str(), currentPos, real_fd, handle);
result = FS_STATUS_MEDIA_ERROR;
} else {
*pos = currentPos;
}
return result;
}
FSStatus FSWrapper::FSIsEofWrapper(FSFileHandle handle) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
auto *fileHandle = getFileFromHandle(handle);
FSStatus result;
int real_fd = fileHandle->fd;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get end position for truncation", getName().c_str(), real_fd, handle);
off_t endPos = lseek(real_fd, (off_t) 0, SEEK_END);
if (currentPos == -1 || endPos == -1) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to get current position (res: %lld) or endPos (res: %lld) of fd (handle %08X) to check EoF", getName().c_str(), currentPos, endPos, real_fd, handle);
result = FS_STATUS_MEDIA_ERROR;
} else if (currentPos == endPos) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] FSIsEof END for %d\n", getName().c_str(), real_fd);
result = FS_STATUS_END;
} else {
lseek(real_fd, currentPos, SEEK_CUR);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] FSIsEof OK for %d\n", getName().c_str(), real_fd);
result = FS_STATUS_OK;
}
return result;
}
FSStatus FSWrapper::FSTruncateFileWrapper(FSFileHandle handle) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (!pIsWriteable) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to truncate fd %d (handle %08X) but layer is not writeable", getName().c_str(), getFileFromHandle(handle)->fd, handle);
return FS_STATUS_ACCESS_ERROR;
}
auto *fileHandle = getFileFromHandle(handle);
FSStatus result = FS_STATUS_OK;
int real_fd = fileHandle->fd;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
if (currentPos == -1) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to get current position of fd (handle %08X) to truncate file", getName().c_str(), real_fd, handle);
result = FS_STATUS_MEDIA_ERROR;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Truncate fd %08X (FSFileHandle %08X) to %lld bytes ", getName().c_str(), real_fd, handle, currentPos);
if (ftruncate(real_fd, currentPos) < 0) {
DEBUG_FUNCTION_LINE_ERR("[%s] ftruncate failed for fd %08X (FSFileHandle %08X) errno %d", getName().c_str(), real_fd, handle, errno);
result = FS_STATUS_MEDIA_ERROR;
}
}
return result;
}
FSStatus FSWrapper::FSWriteFileWrapper(uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (!pIsWriteable) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to write to fd %d (handle %08X) but layer is not writeable", getName().c_str(), getFileFromHandle(handle)->fd, handle);
return FS_STATUS_ACCESS_ERROR;
}
auto *fileHandle = getFileFromHandle(handle);
FSStatus result;
int real_fd = fileHandle->fd;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Write %u bytes to fd %08X (FSFileHandle %08X) from buffer %08X", getName().c_str(), count * size, real_fd, handle, buffer);
auto writeRes = writeFromBuffer(real_fd, buffer, size, count);
if (writeRes < 0) {
auto err = errno;
DEBUG_FUNCTION_LINE_ERR("[%s] Write failed %u bytes to fd %08X (FSFileHandle %08X) from buffer %08X errno %d", getName().c_str(), count * size, real_fd, handle, buffer, err);
if (err == EFBIG) {
result = FS_STATUS_FILE_TOO_BIG;
} else if (err == EACCES) {
result = FS_STATUS_ACCESS_ERROR;
} else {
result = FS_STATUS_MEDIA_ERROR;
}
} else {
result = static_cast<FSStatus>((uint32_t) (writeRes & 0xFFFFFFFF) / size);
}
return result;
}
FSStatus FSWrapper::FSRemoveWrapper(const char *path) {
if (!IsPathToReplace(path)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (!pIsWriteable) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to remove %s but layer is not writeable", getName().c_str(), path);
return FS_STATUS_ACCESS_ERROR;
}
auto newPath = GetNewPath(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);
if (err == ENOTDIR) {
return FS_STATUS_NOT_DIR;
} else if (err == EACCES) {
return FS_STATUS_ACCESS_ERROR;
} else if (err == EISDIR) {
return FS_STATUS_NOT_FILE;
} else if (err == EPERM) {
return FS_STATUS_PERMISSION_ERROR;
}
return FS_STATUS_MEDIA_ERROR;
}
return FS_STATUS_OK;
}
FSStatus FSWrapper::FSRenameWrapper(const char *oldPath, const char *newPath) {
if (!IsPathToReplace(oldPath) || !IsPathToReplace(newPath)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (!pIsWriteable) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to rename %s to %s but layer is not writeable", getName().c_str(), oldPath, newPath);
return FS_STATUS_ACCESS_ERROR;
}
auto oldPathRedirect = GetNewPath(oldPath);
auto newPathRedirect = GetNewPath(newPath);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Rename %s (%s) -> %s (%s)", getName().c_str(), oldPath, oldPathRedirect.c_str(), newPath, newPathRedirect.c_str());
if (rename(oldPathRedirect.c_str(), newPathRedirect.c_str()) < 0) {
auto err = errno;
DEBUG_FUNCTION_LINE_ERR("[%s] Rename failed %s (%s) -> %s (%s). errno %d", getName().c_str(), oldPath, oldPathRedirect.c_str(), newPath, newPathRedirect.c_str(), err);
if (err == ENOTDIR) {
return FS_STATUS_NOT_DIR;
} else if (err == EACCES) {
return FS_STATUS_ACCESS_ERROR;
} else if (err == EISDIR) {
return FS_STATUS_NOT_FILE;
} else if (err == EPERM) {
return FS_STATUS_PERMISSION_ERROR;
}
return FS_STATUS_MEDIA_ERROR;
}
return FS_STATUS_OK;
}
FSStatus FSWrapper::FSFlushFileWrapper(FSFileHandle handle) {
if (!isValidFileHandle(handle)) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
if (!pIsWriteable) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to fsync fd %d (handle %08X)) but layer is not writeable", getName().c_str(), getFileFromHandle(handle)->fd, handle);
return FS_STATUS_ACCESS_ERROR;
}
auto *fileHandle = getFileFromHandle(handle);
int real_fd = fileHandle->fd;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] fsync fd %08X (FSFileHandle %08X)", real_fd, handle);
FSStatus result = FS_STATUS_OK;
if (fsync(real_fd) < 0) {
DEBUG_FUNCTION_LINE_ERR("[%s] fsync failed for fd %08X (FSFileHandle %08X)", getName().c_str(), real_fd, handle);
result = FS_STATUS_MEDIA_ERROR;
}
return result;
}
bool FSWrapper::IsFileModeAllowed(const char *mode) {
if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) {
return true;
}
if (pIsWriteable && (strcmp(mode, "r+") == 0 ||
strcmp(mode, "w") == 0 ||
strcmp(mode, "w+") == 0 ||
strcmp(mode, "a") == 0 ||
strcmp(mode, "a+") == 0)) {
return true;
}
return false;
}
bool FSWrapper::IsPathToReplace(const std::string_view &path) {
return path.starts_with(pPathToReplace);
}
std::string FSWrapper::GetNewPath(const std::string_view &path) {
auto subStr = path.substr(this->pPathToReplace.length());
auto res = string_format("%s%.*s", this->pReplacePathWith.c_str(), int(subStr.length()), subStr.data());
std::replace(res.begin(), res.end(), '/', '/');
uint32_t length = res.size();
//! clear path of double slashes
for (uint32_t i = 1; i < length; ++i) {
if (res[i - 1] == '/' && res[i] == '/') {
res.erase(i, 1);
i--;
length--;
}
}
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Redirect %.*s -> %s", getName().c_str(), int(path.length()), path.data(), res.c_str());
return res;
}
bool FSWrapper::isValidFileHandle(FSFileHandle handle) {
std::lock_guard<std::mutex> lock(openFilesMutex);
return std::ranges::any_of(openFiles, [handle](FileInfo *val) { return val->handle == handle; });
}
bool FSWrapper::isValidDirHandle(FSDirectoryHandle handle) {
std::lock_guard<std::mutex> lock(openDirsMutex);
return std::ranges::any_of(openDirs, [handle](DirInfo *val) { return val->handle == handle; });
}
FileInfo *FSWrapper::getNewFileHandle() {
return new (std::nothrow) FileInfo;
}
DirInfo *FSWrapper::getNewDirHandle() {
return new (std::nothrow) DirInfo;
}
FileInfo *FSWrapper::getFileFromHandle(FSFileHandle handle) {
std::lock_guard<std::mutex> lock(openFilesMutex);
for (auto &file : openFiles) {
if (file->handle == handle) {
return file;
}
}
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");
return nullptr;
}
DirInfo *FSWrapper::getDirFromHandle(FSDirectoryHandle handle) {
std::lock_guard<std::mutex> lock(openDirsMutex);
for (auto &dir : openDirs) {
if (dir->handle == handle) {
return dir;
}
}
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");
return nullptr;
}
void FSWrapper::deleteDirHandle(FSDirectoryHandle handle) {
std::lock_guard<std::mutex> lock(openDirsMutex);
bool found = false;
int count = 0;
DirInfo *info;
for (auto &val : openDirs) {
if ((FSDirectoryHandle) val->handle == handle) {
found = true;
info = val;
break;
}
count++;
}
if (found) {
openDirs.erase(openDirs.begin() + count);
delete info;
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle);
}
}
void FSWrapper::deleteFileHandle(FSFileHandle handle) {
std::lock_guard<std::mutex> lock(openFilesMutex);
bool found = false;
auto count = 0;
FileInfo *info;
for (auto &val : openFiles) {
if ((FSFileHandle) val->handle == handle) {
found = true;
info = val;
break;
}
count++;
}
if (found) {
openFiles.erase(openFiles.begin() + count);
delete info;
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle);
}
}
bool FSWrapper::SkipDeletedFilesInReadDir() {
return true;
}

139
src/FSWrapper.h Normal file
View File

@ -0,0 +1,139 @@
#pragma once
#include "DirInfo.h"
#include "FileInfo.h"
#include "IFSWrapper.h"
#include "utils/logger.h"
#include <coreinit/filesystem.h>
#include <coreinit/mutex.h>
#include <functional>
#include <mutex>
class FSWrapper : public IFSWrapper {
public:
FSWrapper(const std::string &name, const std::string &pathToReplace, const std::string &replacePathWith, bool fallbackOnError, bool isWriteable) {
this->pName = name;
this->pPathToReplace = pathToReplace;
this->pReplacePathWith = replacePathWith;
this->pFallbackOnError = fallbackOnError;
this->pIsWriteable = isWriteable;
this->pCheckIfDeleted = fallbackOnError;
std::replace(pPathToReplace.begin(), pPathToReplace.end(), '\\', '/');
std::replace(pReplacePathWith.begin(), pReplacePathWith.end(), '\\', '/');
}
~FSWrapper() override {
if (!openFiles.empty()) {
DEBUG_FUNCTION_LINE_ERR("Clean leaked fileInfos");
for (auto &file : openFiles) {
delete file;
}
openFiles.clear();
}
if (!openDirs.empty()) {
DEBUG_FUNCTION_LINE_ERR("Clean leaked dirInfos");
for (auto &dir : openDirs) {
delete dir;
}
openDirs.clear();
}
}
FSStatus FSOpenDirWrapper(const char *path,
FSDirectoryHandle *handle) override;
FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
FSDirectoryEntry *entry) override;
FSStatus FSCloseDirWrapper(FSDirectoryHandle handle) override;
FSStatus FSMakeDirWrapper(const char *path) override;
FSStatus FSRewindDirWrapper(FSDirectoryHandle handle) override;
FSStatus FSOpenFileWrapper(const char *path,
const char *mode,
FSFileHandle *handle) override;
FSStatus FSCloseFileWrapper(FSFileHandle handle) override;
FSStatus FSGetStatWrapper(const char *path, FSStat *stats) override;
FSStatus FSGetStatFileWrapper(FSFileHandle handle,
FSStat *stats) override;
FSStatus FSReadFileWrapper(void *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
uint32_t unk1) override;
FSStatus FSReadFileWithPosWrapper(void *buffer,
uint32_t size,
uint32_t count,
uint32_t pos,
FSFileHandle handle,
int32_t unk1) override;
FSStatus FSSetPosFileWrapper(FSFileHandle handle,
uint32_t pos) override;
FSStatus FSGetPosFileWrapper(FSFileHandle handle,
uint32_t *pos) override;
FSStatus FSIsEofWrapper(FSFileHandle handle) override;
FSStatus FSTruncateFileWrapper(FSFileHandle handle) override;
FSStatus FSWriteFileWrapper(uint8_t *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
uint32_t unk1) override;
FSStatus FSRemoveWrapper(const char *path) override;
FSStatus FSRenameWrapper(const char *oldPath,
const char *newPath) override;
FSStatus FSFlushFileWrapper(FSFileHandle handle) override;
protected:
virtual bool IsFileModeAllowed(const char *mode);
virtual bool IsPathToReplace(const std::string_view &path);
std::string GetNewPath(const std::string_view &path);
DirInfo *getDirFromHandle(FSDirectoryHandle handle);
FileInfo *getFileFromHandle(FSFileHandle handle);
bool isValidDirHandle(FSDirectoryHandle handle) override;
bool isValidFileHandle(FSFileHandle handle) override;
void deleteDirHandle(FSDirectoryHandle handle) override;
void deleteFileHandle(FSFileHandle handle) override;
virtual bool CheckFileShouldBeIgnored(std::string &path);
virtual FileInfo *getNewFileHandle();
virtual DirInfo *getNewDirHandle();
virtual bool SkipDeletedFilesInReadDir();
bool pCheckIfDeleted = false;
std::string deletePrefix = ".deleted_";
private:
std::string pPathToReplace;
std::string pReplacePathWith;
bool pIsWriteable = false;
std::mutex openFilesMutex;
std::mutex openDirsMutex;
std::vector<FileInfo *> openFiles;
std::vector<DirInfo *> openDirs;
};

View File

@ -0,0 +1,257 @@
#include "FSWrapperMergeDirsWithParent.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/filesystem.h>
#include <filesystem>
FSStatus FSWrapperMergeDirsWithParent::FSOpenDirWrapper(const char *path,
FSDirectoryHandle *handle) {
auto res = FSWrapper::FSOpenDirWrapper(path, handle);
if (res == FS_STATUS_OK) {
if (handle == nullptr || !isValidDirHandle(*handle)) {
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), *handle);
return FS_STATUS_FATAL_ERROR;
}
auto *dirHandle = getDirExFromHandle(*handle);
if (dirHandle != nullptr) {
dirHandle->readResultCapacity = 0;
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, (uint32_t) this);
// Call FSOpen with "this" as errorFlag call FSOpen for "parent" layers only.
if (FSOpenDir(pFSClient, pCmdBlock, path, &realHandle, (FSErrorFlag) (uint32_t) this) == FS_STATUS_OK) {
dirHandle->realDirHandle = realHandle;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Failed to open real dir %s", getName().c_str(), path);
}
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
}
OSMemoryBarrier();
}
}
return res;
}
bool FSWrapperMergeDirsWithParent::SkipDeletedFilesInReadDir() {
return false;
}
FSStatus FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry) {
do {
auto res = FSWrapper::FSReadDirWrapper(handle, entry);
if (res == FS_STATUS_OK || res == FS_STATUS_END) {
if (!isValidDirHandle(handle)) {
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle);
return FS_STATUS_FATAL_ERROR;
}
auto *dirHandle = getDirExFromHandle(handle);
if (res == FS_STATUS_OK) {
if (dirHandle->readResultCapacity == 0) {
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, handle);
OSFatal("Failed to alloc memory for read result");
}
dirHandle->readResultCapacity = 1;
}
if (dirHandle->readResultNumberOfEntries >= dirHandle->readResultCapacity) {
auto newCapacity = dirHandle->readResultCapacity * 2;
dirHandle->readResult = (FSDirectoryEntryEx *) realloc(dirHandle->readResult, newCapacity * sizeof(FSDirectoryEntryEx));
dirHandle->readResultCapacity = newCapacity;
if (dirHandle->readResult == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to realloc memory for %08X (handle %08X)", getName().c_str(), dirHandle, handle);
OSFatal("Failed to alloc memory for read result");
}
}
memcpy(&dirHandle->readResult[dirHandle->readResultNumberOfEntries].realEntry, entry, sizeof(FSDirectoryEntry));
dirHandle->readResultNumberOfEntries++;
/**
* Read the next entry if this enntry starts with deletePrefix. We keep the entry but mark it as deleted.
*/
if (std::string_view(entry->name).starts_with(deletePrefix)) {
dirHandle->readResult[dirHandle->readResultNumberOfEntries].isMarkedAsDeleted = true;
OSMemoryBarrier();
continue;
}
OSMemoryBarrier();
} else if (res == FS_STATUS_END) {
// Read the real directory.
if (dirHandle->realDirHandle != 0) {
if (pFSClient && pCmdBlock) {
FSDirectoryEntry realDirEntry;
FSStatus 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);
readDirResult = FSReadDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, &realDirEntry, (FSErrorFlag) (uint32_t) this);
if (readDirResult == FS_STATUS_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"
if (strcmp(curResult->realEntry.name, nameDeleted.c_str()) == 0) {
found = true;
break;
}
// Check if this is a new result
if (strcmp(curResult->realEntry.name, realDirEntry.name) == 0 && !curResult->isMarkedAsDeleted) {
found = true;
break;
}
}
// If it's new we can use it :)
if (!found) {
memcpy(entry, &realDirEntry, sizeof(FSDirectoryEntry));
res = FS_STATUS_OK;
break;
}
} else if (readDirResult == FS_STATUS_END) {
res = FS_STATUS_END;
break;
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] real_FSReadDir returned an unexpected error: %08X", getName().c_str(), readDirResult);
res = FS_STATUS_END;
break;
}
}
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
}
}
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Unexpected result %d", getName().c_str(), res);
}
}
return res;
} while (true);
}
FSStatus FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSDirectoryHandle handle) {
auto res = FSWrapper::FSCloseDirWrapper(handle);
if (res == FS_STATUS_OK) {
if (!isValidDirHandle(handle)) {
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle);
return FS_STATUS_FATAL_ERROR;
}
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);
auto realResult = FSCloseDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, (FSErrorFlag) (uint32_t) this);
if (realResult == FS_STATUS_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;
}
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
}
}
if (dirHandle->readResult != nullptr) {
free(dirHandle->readResult);
dirHandle->readResult = nullptr;
dirHandle->readResultCapacity = 0;
dirHandle->readResultNumberOfEntries = 0;
}
OSMemoryBarrier();
}
return res;
}
FSStatus FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSDirectoryHandle handle) {
auto res = FSWrapper::FSRewindDirWrapper(handle);
if (res == FS_STATUS_OK) {
if (!isValidDirHandle(handle)) {
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle);
return FS_STATUS_FATAL_ERROR;
}
auto *dirHandle = getDirExFromHandle(handle);
if (dirHandle->readResult != nullptr) {
dirHandle->readResultNumberOfEntries = 0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
memset(dirHandle->readResult, 0, sizeof(FSDirectoryEntryEx) * dirHandle->readResultCapacity);
#pragma GCC diagnostic pop
}
if (dirHandle->realDirHandle != 0) {
if (pFSClient && pCmdBlock) {
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call real_FSRewindDir for %08X with error_flag %08X (this)", getName().c_str(), dirHandle->realDirHandle, (uint32_t) this);
if (FSRewindDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, (FSErrorFlag) (uint32_t) this) == FS_STATUS_OK) {
dirHandle->realDirHandle = 0;
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to rewind dir for realDirHandle %08X", getName().c_str(), dirHandle->realDirHandle);
}
} else {
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
}
}
OSMemoryBarrier();
}
return res;
}
FSWrapperMergeDirsWithParent::FSWrapperMergeDirsWithParent(const std::string &name,
const std::string &pathToReplace,
const std::string &replaceWithPath,
bool fallbackOnError) : FSWrapper(name,
pathToReplace,
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();
}
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);
}
delete pFSClient;
delete pCmdBlock;
pFSClient = nullptr;
pCmdBlock = nullptr;
}
DirInfoEx *FSWrapperMergeDirsWithParent::getDirExFromHandle(FSDirectoryHandle handle) {
auto *dir = getDirFromHandle(handle);
auto res = dynamic_cast<DirInfoEx *>(dir);
if (res == nullptr) {
DEBUG_FUNCTION_LINE_ERR("[%s] dynamic_cast<DirInfoEx *>(%08X)", getName().c_str(), handle);
OSFatal("dynamic_cast<DirInfoEx *> failed");
}
return res;
}
DirInfo *FSWrapperMergeDirsWithParent::getNewDirHandle() {
return new (std::nothrow) DirInfoEx;
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "DirInfoEx.h"
#include "FSWrapper.h"
#include <coreinit/filesystem.h>
#include <functional>
class FSWrapperMergeDirsWithParent : public FSWrapper {
public:
FSWrapperMergeDirsWithParent(const std::string &name,
const std::string &pathToReplace,
const std::string &replaceWithPath,
bool fallbackOnError);
~FSWrapperMergeDirsWithParent() override;
FSStatus FSOpenDirWrapper(const char *path,
FSDirectoryHandle *handle) override;
FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
FSDirectoryEntry *entry) override;
FSStatus FSCloseDirWrapper(FSDirectoryHandle handle) override;
FSStatus FSRewindDirWrapper(FSDirectoryHandle handle) override;
DirInfo *getNewDirHandle() override;
bool SkipDeletedFilesInReadDir() override;
private:
void freeFSClient();
FSClient *pFSClient;
FSCmdBlock *pCmdBlock;
DirInfoEx *getDirExFromHandle(FSDirectoryHandle handle);
};

8
src/FileInfo.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <coreinit/filesystem.h>
struct FileInfo {
public:
FSFileHandle handle;
int fd;
};

270
src/FileUtils.cpp Normal file
View File

@ -0,0 +1,270 @@
#include "FileUtils.h"
#include "FSWrapper.h"
#include "IFSWrapper.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <coreinit/cache.h>
#include <coreinit/thread.h>
#include <map>
#include <unistd.h>
#include <wums.h>
std::mutex workingDirMutex;
std::map<FSClient *, std::string> workingDirs;
std::mutex fsLayerMutex;
std::vector<IFSWrapper *> fsLayers;
std::string getFullPathForClient(FSClient *pClient, char *path) {
std::string res;
if (path[0] != '/' && path[0] != '\\') {
if (workingDirs.count(pClient) > 0) {
res = string_format("%s%s", workingDirs.at(pClient).c_str(), path);
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to find working dir for client");
}
} else {
res = path;
}
std::replace(res.begin(), res.end(), '\\', '/');
return res;
}
void setWorkingDir(FSClient *client, const char *path) {
std::lock_guard<std::mutex> workingDirLock(workingDirMutex);
workingDirs[client] = path;
OSMemoryBarrier();
}
void clearFSLayer() {
{
std::lock_guard<std::mutex> workingDirLock(workingDirMutex);
workingDirs.clear();
}
{
std::lock_guard<std::mutex> layerlock(fsLayerMutex);
for (auto &layer : fsLayers) {
delete layer;
}
fsLayers.clear();
}
}
// FUN_0204cc20
#define fsClientHandleFatalErrorAndBlock ((void (*)(FSClientBody *, uint32_t))(0x101C400 + 0x4cc20))
FSStatus doForLayer(FSClient *client,
FSErrorFlag errorMask,
const std::function<FSStatus(FSErrorFlag errorMask)> &real_function,
const std::function<FSStatus(IFSWrapper *layer)> &layer_callback,
const std::function<FSStatus(IFSWrapper *layer, FSStatus)> &result_handler) {
FSErrorFlag realErrorMask = errorMask;
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] == errorMask) {
startIndex = i - 1;
realErrorMask = FS_ERROR_FLAG_ALL;
break;
}
}
if (startIndex > 0) {
for (uint32_t i = startIndex; i > 0; i--) {
auto layer = fsLayers[i - 1];
if (!layer->isActive()) {
continue;
}
auto result = layer_callback(layer);
if (result != FS_STATUS_FORCE_PARENT_LAYER) {
if (result < FS_STATUS_OK && result != FS_STATUS_END && result != FS_STATUS_CANCELLED) {
if (layer->fallbackOnError()) {
// Only fallback if FS_STATUS_FORCE_NO_FALLBACK flag is not set.
if (static_cast<FSStatus>(result & 0xFFFF0000) != FS_STATUS_FORCE_NO_FALLBACK) {
continue;
} else {
// Remove FS_STATUS_FORCE_NO_FALLBACK flag.
result = static_cast<FSStatus>((result & 0x0000FFFF) | 0xFFFF0000);
}
}
}
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);
}
FSErrorFlag errorFlags = FS_ERROR_FLAG_NONE;
bool forceError = false;
switch ((int) 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);
}
}
}
}
auto mask = static_cast<FSErrorFlag>((realErrorMask & FS_ERROR_FLAG_REAL_MASK) | FS_ERROR_FLAG_FORCE_REAL);
return real_function(mask);
}
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock) {
if (!cmdBlock) {
return nullptr;
}
auto body = (FSCmdBlockBody *) (ROUNDUP((uint32_t) cmdBlock, 0x40));
return body;
}
FSClientBody *fsClientGetBody(FSClient *client) {
if (!client) {
return nullptr;
}
auto body = (FSClientBody *) (ROUNDUP((uint32_t) client, 0x40));
body->client = client;
return body;
}
FSStatus send_result_async(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;
}
// userCallbacks are called in the DefaultAppIOQueue.
asyncData->ioMsgQueue = OSGetDefaultAppIOQueue();
//DEBUG_FUNCTION_LINE("Force to OSGetDefaultAppIOQueue (%08X)", asyncData->ioMsgQueue);
}
if (asyncData->ioMsgQueue != nullptr) {
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
FSAsyncResult *result = &(fsCmdBlockGetBody(block)->asyncResult);
//DEBUG_FUNCTION_LINE("Send result %d to ioMsgQueue (%08X)", status, asyncData->ioMsgQueue);
result->asyncData.callback = asyncData->callback;
result->asyncData.param = asyncData->param;
result->asyncData.ioMsgQueue = asyncData->ioMsgQueue;
memset(&result->ioMsg, 0, sizeof(result->ioMsg));
result->ioMsg.data = result;
result->ioMsg.type = OS_FUNCTION_TYPE_FS_CMD_ASYNC;
result->client = client;
result->block = block;
result->status = status;
while (!OSSendMessage(asyncData->ioMsgQueue, (OSMessage *) &(result->ioMsg), OS_MESSAGE_FLAGS_NONE)) {
DEBUG_FUNCTION_LINE_ERR("Failed to send message");
}
}
return FS_STATUS_OK;
}
int64_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count) {
auto sizeToRead = size * count;
/*
// https://github.com/decaf-emu/decaf-emu/blob/131aeb14fccff8461a5fd9f2aa5c040ba3880ef5/src/libdecaf/src/cafe/libraries/coreinit/coreinit_fs_cmd.cpp#L2346
if (sizeToRead > 0x100000) {
sizeToRead = 0x100000;
}*/
void *newBuffer = buffer;
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);
return -1;
}
if (curResult == 0) {
break;
}
newBuffer = (void *) (((uint32_t) newBuffer) + curResult);
totalSize += curResult;
sizeToRead -= curResult;
}
return totalSize;
}
int64_t writeFromBuffer(int32_t handle, void *buffer, size_t size, size_t count) {
auto sizeToWrite = size * count;
void *ptr = buffer;
int32_t curResult;
int64_t totalSize = 0;
while (sizeToWrite > 0) {
curResult = write(handle, ptr, sizeToWrite);
if (curResult < 0) {
DEBUG_FUNCTION_LINE_ERR("Writing %08X bytes from handle %08X failed. result %08X errno: %d ", size * count, handle, curResult, errno);
return -1;
}
if (curResult == 0) {
break;
}
ptr = (void *) (((uint32_t) ptr) + curResult);
totalSize += curResult;
sizeToWrite -= curResult;
}
return totalSize;
}

85
src/FileUtils.h Normal file
View File

@ -0,0 +1,85 @@
#pragma once
#include "IFSWrapper.h"
#include <coreinit/filesystem.h>
#include <functional>
#include <mutex>
#include <romfs_dev.h>
#include <string>
extern std::mutex fsLayerMutex;
extern std::vector<IFSWrapper *> fsLayers;
#define SYNC_RESULT_HANDLER [filename = __FILENAME__, func = __FUNCTION__, line = __LINE__]([[maybe_unused]] IFSWrapper *layer, FSStatus res) -> FSStatus { \
DEBUG_FUNCTION_LINE_VERBOSE_EX(filename, func, line, "Sync result was %d", res); \
return res; \
}
#define ASYNC_RESULT_HANDLER [c = client, b = block, a = asyncData, filename = __FILENAME__, func = __FUNCTION__, line = __LINE__]([[maybe_unused]] 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 FS_ERROR_FLAG_EXTRA_MASK (FSErrorFlag) 0xFFFF0000
#define FS_ERROR_FLAG_REAL_MASK (FSErrorFlag) 0x0000FFFF
#define FS_ERROR_FLAG_FORCE_REAL (FSErrorFlag) 0xFEDC0000
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);
}
void clearFSLayer();
struct WUT_PACKED FSCmdBlockBody { //! FSAsyncResult object used for this command.
WUT_UNKNOWN_BYTES(0x96C);
FSAsyncResult asyncResult;
};
WUT_CHECK_OFFSET(FSCmdBlockBody, 0x96C, asyncResult);
WUT_CHECK_SIZE(FSCmdBlockBody, 0x96C + 0x28);
struct WUT_PACKED FSClientBody {
WUT_UNKNOWN_BYTES(0x1448);
void *fsm;
WUT_UNKNOWN_BYTES(0x15E8 - 0x1448 - 0x04);
uint32_t lastError;
BOOL isLastErrorWithoutVolume;
WUT_UNKNOWN_BYTES(0x161C - 0x15E8 - 0x4 - 0x4);
FSClient *client;
};
WUT_CHECK_OFFSET(FSClientBody, 0x1448, fsm);
WUT_CHECK_OFFSET(FSClientBody, 0x15E8, lastError);
WUT_CHECK_OFFSET(FSClientBody, 0x15EC, isLastErrorWithoutVolume);
WUT_CHECK_OFFSET(FSClientBody, 0x161C, client);
WUT_CHECK_SIZE(FSClientBody, 0x161C + 0x4);
std::string getFullPathForClient(FSClient *pClient, char *path);
void setWorkingDir(FSClient *client, const char *path);
FSStatus doForLayer(FSClient *client,
FSErrorFlag errorMask,
const std::function<FSStatus(FSErrorFlag errorMask)> &real_function,
const std::function<FSStatus(IFSWrapper *layer)> &layer_callback,
const std::function<FSStatus(IFSWrapper *layer, FSStatus)> &result_handler);
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock);
FSClientBody *fsClientGetBody(FSClient *client);
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus result);
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);
int32_t CreateSubfolder(const char *fullpath);

146
src/IFSWrapper.h Normal file
View File

@ -0,0 +1,146 @@
#pragma once
#include <coreinit/filesystem.h>
#include <functional>
#include <string>
#define FS_STATUS_FORCE_PARENT_LAYER (FSStatus) 0xFFFF0000
#define FS_STATUS_FORCE_NO_FALLBACK (FSStatus) 0xFFFE0000
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
class IFSWrapper {
public:
virtual ~IFSWrapper() = default;
virtual FSStatus FSOpenDirWrapper(const char *path,
FSDirectoryHandle *handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
FSDirectoryEntry *entry) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSCloseDirWrapper(FSDirectoryHandle handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSRewindDirWrapper(FSDirectoryHandle handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSMakeDirWrapper(const char *path) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSOpenFileWrapper(const char *path,
const char *mode,
FSFileHandle *handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSCloseFileWrapper(FSFileHandle handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSGetStatWrapper(const char *path,
FSStat *stats) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSGetStatFileWrapper(FSFileHandle handle,
FSStat *stats) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSReadFileWrapper(void *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
uint32_t unk1) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSReadFileWithPosWrapper(void *buffer,
uint32_t size,
uint32_t count,
uint32_t pos,
FSFileHandle handle,
int32_t unk1) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSSetPosFileWrapper(FSFileHandle handle,
uint32_t pos) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSGetPosFileWrapper(FSFileHandle handle,
uint32_t *pos) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSIsEofWrapper(FSFileHandle handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSTruncateFileWrapper(FSFileHandle handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSWriteFileWrapper(uint8_t *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
uint32_t unk1) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSRemoveWrapper(const char *path) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSRenameWrapper(const char *oldPath,
const char *newPath) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual FSStatus FSFlushFileWrapper(FSFileHandle handle) {
return FS_STATUS_FORCE_PARENT_LAYER;
}
virtual bool fallbackOnError() {
return pFallbackOnError;
}
virtual bool isActive() {
return pIsActive;
}
virtual void setActive(bool newValue) {
pIsActive = newValue;
}
[[nodiscard]] virtual std::string getName() const {
return pName;
}
virtual bool isValidDirHandle(FSDirectoryHandle handle) = 0;
virtual bool isValidFileHandle(FSFileHandle handle) = 0;
virtual void deleteDirHandle(FSDirectoryHandle handle) = 0;
virtual void deleteFileHandle(FSFileHandle handle) = 0;
private:
bool pIsActive = true;
protected:
bool pFallbackOnError = false;
std::string pName;
};
#pragma GCC diagnostic pop

96
src/export.cpp Normal file
View File

@ -0,0 +1,96 @@
#include "FSWrapper.h"
#include "FSWrapperMergeDirsWithParent.h"
#include "FileUtils.h"
#include "IFSWrapper.h"
#include "utils/logger.h"
#include <content_redirection/redirection.h>
#include <coreinit/debug.h>
#include <mutex>
#include <wums/exports.h>
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");
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
}
IFSWrapper *ptr;
if (layerType == FS_LAYER_TYPE_CONTENT_REPLACE) {
ptr = new (std::nothrow) FSWrapper(layerName, "/vol/content", replacementDir, false, false);
} else if (layerType == FS_LAYER_TYPE_CONTENT_MERGE) {
ptr = new (std::nothrow) FSWrapperMergeDirsWithParent(layerName, "/vol/content", replacementDir, true);
} else if (layerType == FS_LAYER_TYPE_SAVE_REPLACE) {
ptr = new (std::nothrow) FSWrapper(layerName, "/vol/save", 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;
}
if (ptr) {
DEBUG_FUNCTION_LINE_VERBOSE("Added new layer (%s). Replacement dir: %s Type:%d", layerName, replacementDir, layerType);
std::lock_guard<std::mutex> lock(fsLayerMutex);
fsLayers.push_back(ptr);
*handle = (CRLayerHandle) ptr;
return CONTENT_REDIRECTION_API_ERROR_NONE;
}
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory");
return CONTENT_REDIRECTION_API_ERROR_NO_MEMORY;
}
ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) {
std::lock_guard<std::mutex> lock(fsLayerMutex);
auto count = 0;
auto found = false;
IFSWrapper *layer;
for (auto &cur : fsLayers) {
if ((CRLayerHandle) cur == handle) {
found = true;
layer = cur;
break;
}
count++;
}
if (!found) {
DEBUG_FUNCTION_LINE_ERR("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
}
fsLayers.erase(fsLayers.begin() + count);
delete layer;
return CONTENT_REDIRECTION_API_ERROR_NONE;
}
ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) {
std::lock_guard<std::mutex> lock(fsLayerMutex);
auto found = false;
IFSWrapper *layer;
for (auto &cur : fsLayers) {
if ((CRLayerHandle) cur == handle) {
found = true;
layer = cur;
break;
}
}
if (!found) {
DEBUG_FUNCTION_LINE_ERR("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
}
layer->setActive(active);
return CONTENT_REDIRECTION_API_ERROR_NONE;
}
uint32_t CRGetVersion() {
return CONTENT_REDIRECT_MODULE_VERSION;
}
int CRAddDevice(const devoptab_t *device) {
return AddDevice(device);
}
int CRRemoveDevice(const char *name) {
return RemoveDevice(name);
}
WUMS_EXPORT_FUNCTION(CRGetVersion);
WUMS_EXPORT_FUNCTION(CRAddFSLayer);
WUMS_EXPORT_FUNCTION(CRRemoveFSLayer);
WUMS_EXPORT_FUNCTION(CRSetActive);
WUMS_EXPORT_FUNCTION(CRAddDevice);
WUMS_EXPORT_FUNCTION(CRRemoveDevice);

30
src/main.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "FSDirReplacements.h"
#include "FSFileReplacements.h"
#include "FileUtils.h"
#include "utils/logger.h"
#include <coreinit/memorymap.h>
#include <coreinit/title.h>
#include <sysapp/title.h>
#include <wums.h>
WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection");
WUMS_USE_WUT_DEVOPTAB();
WUMS_INITIALIZE() {
initLogging();
DEBUG_FUNCTION_LINE("Patch functions");
// we only patch static functions, we don't need re-patch them at every launch
FunctionPatcherPatchFunction(fs_file_function_replacements, fs_file_function_replacements_size);
FunctionPatcherPatchFunction(fs_dir_function_replacements, fs_dir_function_replacements_size);
DEBUG_FUNCTION_LINE("Patch functions finished");
deinitLogging();
}
WUMS_APPLICATION_STARTS() {
initLogging();
}
WUMS_APPLICATION_ENDS() {
clearFSLayer();
deinitLogging();
}

14
src/utils/StringTools.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
template<typename... Args>
std::string string_format(const std::string &format, Args... args) {
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
auto size = static_cast<size_t>(size_s);
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
}

36
src/utils/logger.c Normal file
View File

@ -0,0 +1,36 @@
#ifdef DEBUG
#include <stdint.h>
#include <whb/log_cafe.h>
#include <whb/log_module.h>
#include <whb/log_udp.h>
uint32_t moduleLogInit = false;
uint32_t cafeLogInit = false;
uint32_t udpLogInit = false;
#endif // DEBUG
void initLogging() {
#ifdef DEBUG
if (!(moduleLogInit = WHBLogModuleInit())) {
cafeLogInit = WHBLogCafeInit();
udpLogInit = WHBLogUdpInit();
}
#endif // DEBUG
}
void deinitLogging() {
#ifdef DEBUG
if (moduleLogInit) {
WHBLogModuleDeinit();
moduleLogInit = false;
}
if (cafeLogInit) {
WHBLogCafeDeinit();
cafeLogInit = false;
}
if (udpLogInit) {
WHBLogUdpDeinit();
udpLogInit = false;
}
#endif // DEBUG
}

73
src/utils/logger.h Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include <coreinit/debug.h>
#include <cstring>
#include <whb/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
// #define VERBOSE_DEBUG
#ifdef DEBUG
#ifdef VERBOSE_DEBUG
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) \
do { \
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) \
do { \
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", FILENAME, FUNCTION, LINE, ##ARGS); \
} while (0)
#else
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) while (0)
#endif
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
do { \
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \
do { \
WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
#else
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
#endif
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) \
do { \
OSReport("## ERROR ## [%23s]%30s@L%04d: ##ERROR## " FMT "\n", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) \
do { \
OSReport("[%23s]%30s@L%04d: ##ERROR## " FMT "\n", FILENAME, FUNCTION, LINE, ##ARGS); \
} while (0)
void initLogging();
void deinitLogging();
#ifdef __cplusplus
}
#endif

39
src/utils/utils.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "utils/logger.h"
#include <string.h>
#include <whb/log.h>
#define PRINTF_BUFFER_LENGTH 2048
// https://gist.github.com/ccbrown/9722406
void dumpHex(const void *data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
DEBUG_FUNCTION_LINE("0x%08X (0x0000): ", data);
for (i = 0; i < size; ++i) {
WHBLogWritef("%02X ", ((unsigned char *) data)[i]);
if (((unsigned char *) data)[i] >= ' ' && ((unsigned char *) data)[i] <= '~') {
ascii[i % 16] = ((unsigned char *) data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i + 1) % 8 == 0 || i + 1 == size) {
WHBLogWritef(" ");
if ((i + 1) % 16 == 0) {
WHBLogPrintf("| %s ", ascii);
if (i + 1 < size) {
DEBUG_FUNCTION_LINE("0x%08X (0x%04X); ", ((uint32_t) data) + i + 1, i + 1);
}
} else if (i + 1 == size) {
ascii[(i + 1) % 16] = '\0';
if ((i + 1) % 16 <= 8) {
WHBLogWritef(" ");
}
for (j = (i + 1) % 16; j < 16; ++j) {
WHBLogWritef(" ");
}
WHBLogPrintf("| %s ", ascii);
}
}
}
}

38
src/utils/utils.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <malloc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define LIMIT(x, min, max) \
({ \
typeof(x) _x = x; \
typeof(min) _min = min; \
typeof(max) _max = max; \
(((_x) < (_min)) ? (_min) : ((_x) > (_max)) ? (_max) \
: (_x)); \
})
#define DegToRad(a) ((a) *0.01745329252f)
#define RadToDeg(a) ((a) *57.29577951f)
#define ALIGN4(x) (((x) + 3) & ~3)
#define ALIGN32(x) (((x) + 31) & ~31)
// those work only in powers of 2
#define ROUNDDOWN(val, align) ((val) & ~(align - 1))
#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align - 1)), align)
#define le16(i) ((((uint16_t) ((i) &0xFF)) << 8) | ((uint16_t) (((i) &0xFF00) >> 8)))
#define le32(i) ((((uint32_t) le16((i) &0xFFFF)) << 16) | ((uint32_t) le16(((i) &0xFFFF0000) >> 16)))
#define le64(i) ((((uint64_t) le32((i) &0xFFFFFFFFLL)) << 32) | ((uint64_t) le32(((i) &0xFFFFFFFF00000000LL) >> 32)))
//Needs to have log_init() called beforehand.
void dumpHex(const void *data, size_t size);
#ifdef __cplusplus
}
#endif