first commit
This commit is contained in:
commit
7c9c0348fe
|
@ -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
|
|
@ -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
|
|
@ -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"
|
|
@ -0,0 +1,10 @@
|
|||
*.cbp
|
||||
*.elf
|
||||
*.layout
|
||||
*.rpx
|
||||
build/
|
||||
*.save-failed
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
CMakeLists.txt
|
||||
*.wms
|
|
@ -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
|
|
@ -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
|
||||
#-------------------------------------------------------------------------------
|
|
@ -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`
|
|
@ -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]{};
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
|
@ -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;
|
|
@ -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);
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
#include <coreinit/filesystem.h>
|
||||
|
||||
struct FileInfo {
|
||||
public:
|
||||
FSFileHandle handle;
|
||||
int fd;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue