mirror of
https://github.com/wiiu-env/RPXLoadingModule.git
synced 2024-11-26 11:54:14 +01:00
first commit
This commit is contained in:
commit
4551e08745
58
.github/workflows/ci.yml
vendored
Normal file
58
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
name: CI-Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-binary:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
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
|
||||||
|
path: wiiu/modules
|
||||||
|
- name: zip artifact
|
||||||
|
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip wiiu
|
||||||
|
- 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/unknown
|
17
.github/workflows/pr.yml
vendored
Normal file
17
.github/workflows/pr.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: CI-PR
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-binary:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: build binary
|
||||||
|
run: |
|
||||||
|
docker build . -t builder
|
||||||
|
docker run --rm -v ${PWD}:/project builder make
|
||||||
|
- uses: actions/upload-artifact@master
|
||||||
|
with:
|
||||||
|
name: binary
|
||||||
|
path: "*.wms"
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
*.cbp
|
||||||
|
*.elf
|
||||||
|
*.layout
|
||||||
|
*.rpx
|
||||||
|
build/
|
||||||
|
*.save-failed
|
||||||
|
.idea/
|
||||||
|
cmake-build-debug/
|
||||||
|
CMakeLists.txt
|
||||||
|
*.wms
|
6
Dockerfile
Normal file
6
Dockerfile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
FROM wiiuenv/devkitppc:20200810
|
||||||
|
|
||||||
|
COPY --from=wiiuenv/libfunctionpatcher:20200812 /artifacts $DEVKITPRO
|
||||||
|
COPY --from=wiiuenv/wiiumodulesystem:20200812 /artifacts $DEVKITPRO
|
||||||
|
|
||||||
|
WORKDIR project
|
143
Makefile
Normal file
143
Makefile
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
.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 := RPXLoadingModule
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := src \
|
||||||
|
src/utils
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := src
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
CFLAGS := -g -Wall -Wextra -O2 -ffunction-sections\
|
||||||
|
$(MACHDEP)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -std=c++17
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld $(WUMSSPECS)
|
||||||
|
|
||||||
|
LIBS := -lwums -lwut -lfunctionpatcher -lromfs
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# 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).rpx $(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
|
||||||
|
#-------------------------------------------------------------------------------
|
14
README.md
Normal file
14
README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
## 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 rpxloadingmodule-builder
|
||||||
|
|
||||||
|
# make
|
||||||
|
docker run -it --rm -v ${PWD}:/project rpxloadingmodule-builder make
|
||||||
|
|
||||||
|
# make clean
|
||||||
|
docker run -it --rm -v ${PWD}:/project rpxloadingmodule-builder make clean
|
||||||
|
```
|
154
src/FSDirReplacements.cpp
Normal file
154
src/FSDirReplacements.cpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#include "FSDirReplacements.h"
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include "utils/logger.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "FSWrapper.h"
|
||||||
|
#include "FileUtils.h"
|
||||||
|
|
||||||
|
#define SYNC_RESULT_HANDLER [](FSStatus res) -> FSStatus { \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASYNC_RESULT_HANDLER [client, block, asyncData](FSStatus res) -> FSStatus { \
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("Result was %d", res); \
|
||||||
|
return send_result_async(client, block, asyncData, res);\
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSOpenDir, FSClient *client, FSCmdBlock *block, char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSOpenDirWrapper(path, handle, errorMask,
|
||||||
|
[client, block, handle, errorMask]
|
||||||
|
(char *_path) -> FSStatus {
|
||||||
|
return real_FSOpenDir(client, block, _path, handle, errorMask);
|
||||||
|
},
|
||||||
|
SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("Result was %d", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSOpenDir(client, block, path, handle, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSOpenDirAsync, FSClient *client, FSCmdBlock *block, char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSOpenDirWrapper(path, handle, errorMask,
|
||||||
|
[client, block, handle, errorMask, asyncData]
|
||||||
|
(char *_path) -> FSStatus {
|
||||||
|
return real_FSOpenDirAsync(client, block, _path, handle, errorMask, asyncData);
|
||||||
|
},
|
||||||
|
ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSOpenDirAsync(client, block, path, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSReadDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE();
|
||||||
|
FSStatus result = FSReadDirWrapper(handle, entry, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSReadDir(client, block, handle, entry, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSReadDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE();
|
||||||
|
FSStatus result = FSReadDirWrapper(handle, entry, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSReadDirAsync(client, block, handle, entry, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSCloseDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE();
|
||||||
|
FSStatus result = FSCloseDirWrapper(handle, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSCloseDir(client, block, handle, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSCloseDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE();
|
||||||
|
FSStatus result = FSCloseDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSCloseDirAsync(client, block, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSRewindDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE();
|
||||||
|
FSStatus result = FSRewindDirWrapper(handle, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSRewindDir(client, block, handle, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSRewindDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE();
|
||||||
|
FSStatus result = FSRewindDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSRewindDirAsync(client, block, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSMakeDir, FSClient *client, FSCmdBlock *block, char *path, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSMakeDirWrapper(path, errorMask,
|
||||||
|
[client, block, errorMask]
|
||||||
|
(char *_path) -> FSStatus {
|
||||||
|
return real_FSMakeDir(client, block, _path, errorMask);
|
||||||
|
},
|
||||||
|
SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSMakeDir(client, block, path, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSMakeDirAsync, FSClient *client, FSCmdBlock *block, char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSMakeDirWrapper(path, errorMask,
|
||||||
|
[client, block, errorMask, asyncData]
|
||||||
|
(char *_path) -> FSStatus {
|
||||||
|
return real_FSMakeDirAsync(client, block, _path, errorMask, asyncData);
|
||||||
|
},
|
||||||
|
ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return real_FSMakeDirAsync(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),
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t fs_dir_function_replacements_size = sizeof(fs_dir_function_replacements) / sizeof(function_replacement_data_t);
|
16
src/FSDirReplacements.h
Normal file
16
src/FSDirReplacements.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <function_patcher/function_patching.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern function_replacement_data_t fs_dir_function_replacements[];
|
||||||
|
extern uint32_t fs_dir_function_replacements_size;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
404
src/FSFileReplacements.cpp
Normal file
404
src/FSFileReplacements.cpp
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
#include "FSFileReplacements.h"
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
#include <coreinit/dynload.h>
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <wums.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include "utils/logger.h"
|
||||||
|
#include "FileUtils.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
#include "utils/StringTools.h"
|
||||||
|
#include "FSWrapper.h"
|
||||||
|
|
||||||
|
#define SYNC_RESULT_HANDLER [](FSStatus res) -> FSStatus { \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASYNC_RESULT_HANDLER [client, block, asyncData](FSStatus res) -> FSStatus { \
|
||||||
|
return send_result_async(client, block, asyncData, res);\
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, FSFileHandle *handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSOpenFileWrapper(path, mode, handle, errorMask,
|
||||||
|
[client, block, mode, handle, errorMask]
|
||||||
|
(char *_path) -> FSStatus {
|
||||||
|
return real_FSOpenFile(client, block, _path, mode, handle, errorMask);
|
||||||
|
},
|
||||||
|
SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus res = real_FSOpenFile(client, block, path, mode, handle, errorMask);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
FSStatus result = FSOpenFileWrapper(path, mode, handle, errorMask,
|
||||||
|
[client, block, mode, handle, errorMask, asyncData]
|
||||||
|
(char *_path) -> FSStatus {
|
||||||
|
return real_FSOpenFileAsync(client, block, _path, mode, handle, errorMask, asyncData);
|
||||||
|
},
|
||||||
|
ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSOpenFileAsync(client, block, path, mode, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSCloseFileWrapper(handle, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus res = real_FSCloseFile(client, block, handle, errorMask);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSCloseFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSCloseFileWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSCloseFileAsync(client, block, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSGetStat, FSClient *client, FSCmdBlock *block, char *path, FSStat *stats, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSGetStatWrapper(path, stats, errorMask,
|
||||||
|
[client, block, stats, errorMask](char *_path) -> FSStatus {
|
||||||
|
return real_FSGetStat(client, block, _path, stats, errorMask);
|
||||||
|
},
|
||||||
|
SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return real_FSGetStat(client, block, path, stats, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSGetStatAsync, FSClient *client, FSCmdBlock *block, char *path, FSStat *stats, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSGetStatWrapper(path, stats, errorMask,
|
||||||
|
[client, block, stats, errorMask, asyncData](char *_path) -> FSStatus {
|
||||||
|
return real_FSGetStatAsync(client, block, _path, stats, errorMask, asyncData);
|
||||||
|
},
|
||||||
|
ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return real_FSGetStatAsync(client, block, path, stats, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSGetStatFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSStat *stats, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSGetStatFileWrapper(handle, stats, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSGetStatFile(client, block, handle, stats, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSGetStatFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSStat *stats, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSGetStatFileWrapper(handle, stats, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSGetStatFileAsync(client, block, handle, stats, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(int32_t, FSReadFile, FSClient *client, FSCmdBlock *block, void *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSReadFileWrapper(buffer, size, count, handle, unk1, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSReadFile(client, block, buffer, size, count, handle, unk1, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(int32_t, 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("");
|
||||||
|
FSStatus result = FSReadFileWrapper(buffer, size, count, handle, unk1, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return send_result_async(client, block, asyncData, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSReadFileAsync(client, block, buffer, size, count, handle, unk1, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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("");
|
||||||
|
FSStatus result = FSReadFileWithPosWrapper(buffer, size, count, pos, handle, unk1, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%d", size * count);
|
||||||
|
|
||||||
|
FSStatus res = real_FSReadFileWithPos(client, block, buffer, size, count, pos, handle, unk1, errorMask);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(int32_t, 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("");
|
||||||
|
FSStatus result = FSReadFileWithPosWrapper(buffer, size, count, pos, handle, unk1, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSReadFileWithPosAsync(client, block, buffer, size, count, pos, handle, unk1, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSSetPosFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t pos, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSSetPosFileWrapper(handle, pos, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return real_FSSetPosFile(client, block, handle, pos, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSSetPosFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t pos, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSSetPosFileWrapper(handle, pos, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSSetPosFileAsync(client, block, handle, pos, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSGetPosFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t *pos, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSGetPosFileWrapper(handle, pos, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return real_FSGetPosFile(client, block, handle, pos, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSGetPosFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t *pos, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSGetPosFileWrapper(handle, pos, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSGetPosFileAsync(client, block, handle, pos, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSIsEof, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSIsEofWrapper(handle, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return real_FSIsEof(client, block, handle, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSIsEofAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSIsEofWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return real_FSIsEofAsync(client, block, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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("");
|
||||||
|
FSStatus result = FSWriteFileWrapper(buffer, size, count, handle, unk1, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSWriteFile(client, block, buffer, size, count, handle, unk1, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
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("");
|
||||||
|
FSStatus result = FSWriteFileWrapper(buffer, size, count, handle, unk1, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSWriteFileAsync(client, block, buffer, size, count, handle, unk1, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSTruncateFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSTruncateFileWrapper(handle, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSTruncateFile(client, block, handle, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSTruncateFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSTruncateFileWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSTruncateFileAsync(client, block, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSRemove, FSClient *client, FSCmdBlock *block, char *path, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSRemoveWrapper(path, errorMask,
|
||||||
|
[client, block, errorMask](char *_path) -> FSStatus {
|
||||||
|
return real_FSRemove(client, block, _path, errorMask);
|
||||||
|
},
|
||||||
|
SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSRemove(client, block, path, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSRemoveAsync, FSClient *client, FSCmdBlock *block, char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FSRemoveWrapper(path, errorMask,
|
||||||
|
[client, block, errorMask, asyncData](char *_path) -> FSStatus {
|
||||||
|
return real_FSRemoveAsync(client, block, _path, errorMask, asyncData);
|
||||||
|
},
|
||||||
|
ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSRemoveAsync(client, block, path, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSRename, FSClient *client, FSCmdBlock *block, char *oldPath, char *newPath, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s %s", oldPath, newPath);
|
||||||
|
FSStatus result = FSRenameWrapper(oldPath, newPath, errorMask,
|
||||||
|
[client, block, errorMask](char *_oldOath, char *_newPath) -> FSStatus {
|
||||||
|
return real_FSRename(client, block, _oldOath, _newPath, errorMask);
|
||||||
|
},
|
||||||
|
SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSRename(client, block, oldPath, newPath, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSRenameAsync, FSClient *client, FSCmdBlock *block, char *oldPath, char *newPath, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s %s", oldPath, newPath);
|
||||||
|
FSStatus result = FSRenameWrapper(oldPath, newPath, errorMask,
|
||||||
|
[client, block, errorMask, asyncData](char *_oldOath, char *_newPath) -> FSStatus {
|
||||||
|
return real_FSRenameAsync(client, block, _oldOath, _newPath, errorMask, asyncData);
|
||||||
|
},
|
||||||
|
ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSRenameAsync(client, block, oldPath, newPath, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSFlushFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSFlushFileWrapper(handle, errorMask, SYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSFlushFile(client, block, handle, errorMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSFlushFileAsync, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("");
|
||||||
|
FSStatus result = FSFlushFileWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_FSFlushFileAsync(client, block, handle, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSChangeModeAsync, FSClient *client, FSCmdBlock *block, char *path, FSMode mode, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("FSChangeModeAsync %s", path);
|
||||||
|
return real_FSChangeModeAsync(client, block, path, mode, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSGetFreeSpaceSizeAsync, FSClient *client, FSCmdBlock *block, char *path, uint64_t *outSize, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("FSGetFreeSpaceSizeAsync %s", path);
|
||||||
|
return real_FSGetFreeSpaceSizeAsync(client, block, path, outSize, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, FSChangeDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
|
||||||
|
DEBUG_FUNCTION_LINE("FSChangeDirAsync %s", path);
|
||||||
|
strncpy(gWorkingDir, path, sizeof(gWorkingDir));
|
||||||
|
DCFlushRange(gWorkingDir, sizeof(gWorkingDir));
|
||||||
|
return real_FSChangeDirAsync(client, block, path, errorMask, asyncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync),
|
||||||
|
|
||||||
|
//REPLACE_FUNCTION(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync),
|
||||||
|
|
||||||
|
//REPLACE_FUNCTION(FSGetFreeSpaceSizeAsync, LIBRARY_COREINIT, FSGetFreeSpaceSizeAsync),
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t fs_file_function_replacements_size = sizeof(fs_file_function_replacements) / sizeof(function_replacement_data_t);
|
16
src/FSFileReplacements.h
Normal file
16
src/FSFileReplacements.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#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;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
779
src/FSWrapper.cpp
Normal file
779
src/FSWrapper.cpp
Normal file
@ -0,0 +1,779 @@
|
|||||||
|
#include "FSWrapper.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <coreinit/debug.h>
|
||||||
|
#include "utils/logger.h"
|
||||||
|
#include "FileUtils.h"
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
dirMagic_t dir_handles[DIR_HANDLES_LENGTH];
|
||||||
|
fileMagic_t file_handles[FILE_HANDLES_LENGTH];
|
||||||
|
|
||||||
|
std::mutex dir_handle_mutex;
|
||||||
|
std::mutex file_handle_mutex;
|
||||||
|
|
||||||
|
inline void getFullPath(char *pathForCheck, int pathSize, char *path) {
|
||||||
|
if (path[0] != '/' && path[0] != '\\') {
|
||||||
|
snprintf(pathForCheck, pathSize, "%s%s", gWorkingDir, path);
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("Real path is %s", path);
|
||||||
|
} else {
|
||||||
|
strncpy(pathForCheck, path, pathSize - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool checkForSave(char *pathForCheck, int pathSize, char *path) {
|
||||||
|
if (strncmp(path, "/vol/save", 9) == 0) {
|
||||||
|
int copyLen = strlen(path);
|
||||||
|
char copy[copyLen+1];
|
||||||
|
memcpy(copy, path, copyLen);
|
||||||
|
copy[copyLen] = 0;
|
||||||
|
memset(pathForCheck,0, pathSize);
|
||||||
|
snprintf(pathForCheck, pathSize, "%s%s", gSavePath, ©[9]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNewFileHandleIndex() {
|
||||||
|
file_handle_mutex.lock();
|
||||||
|
int32_t handle_id = -1;
|
||||||
|
for (int i = 0; i < FILE_HANDLES_LENGTH; i++) {
|
||||||
|
if (!file_handles[i].in_use) {
|
||||||
|
handle_id = i;
|
||||||
|
if (!file_handles[i].mutex) {
|
||||||
|
file_handles[i].mutex = (OSMutex *) malloc(sizeof(OSMutex));
|
||||||
|
OSInitMutex(file_handles[i].mutex);
|
||||||
|
if (!file_handles[i].mutex) {
|
||||||
|
OSFatal("Failed to alloc memory for mutex");
|
||||||
|
}
|
||||||
|
DCFlushRange(file_handles[i].mutex, sizeof(OSMutex));
|
||||||
|
DCFlushRange(&file_handles[i], sizeof(fileMagic_t));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_handle_mutex.unlock();
|
||||||
|
return handle_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidDirHandle(int32_t handle) {
|
||||||
|
return (handle & HANDLE_INDICATOR_MASK) == DIR_HANDLE_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidFileHandle(int32_t handle) {
|
||||||
|
return (handle & HANDLE_INDICATOR_MASK) == FILE_HANDLE_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t getNewDirHandleIndex() {
|
||||||
|
dir_handle_mutex.lock();
|
||||||
|
int32_t handle_id;
|
||||||
|
for (int i = 0; i < DIR_HANDLES_LENGTH; i++) {
|
||||||
|
if (!dir_handles[i].in_use) {
|
||||||
|
handle_id = i;
|
||||||
|
if (!dir_handles[i].mutex) {
|
||||||
|
dir_handles[i].mutex = (OSMutex *) malloc(sizeof(OSMutex));
|
||||||
|
OSInitMutex(dir_handles[i].mutex);
|
||||||
|
if (!dir_handles[i].mutex) {
|
||||||
|
OSFatal("Failed to alloc memory for mutex");
|
||||||
|
}
|
||||||
|
DCFlushRange(dir_handles[i].mutex, sizeof(OSMutex));
|
||||||
|
DCFlushRange(&dir_handles[i], sizeof(dirMagic_t));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir_handle_mutex.unlock();
|
||||||
|
return handle_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeFileHandle(uint32_t handle) {
|
||||||
|
if (handle >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file_handle_mutex.lock();
|
||||||
|
file_handles[handle].in_use = false;
|
||||||
|
if (file_handles[handle].mutex) {
|
||||||
|
free(file_handles[handle].mutex);
|
||||||
|
file_handles[handle].mutex = nullptr;
|
||||||
|
}
|
||||||
|
DCFlushRange(&file_handles[handle], sizeof(fileMagic_t));
|
||||||
|
file_handle_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeDirHandle(uint32_t handle) {
|
||||||
|
if (handle >= DIR_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dir_handle_mutex.lock();
|
||||||
|
dir_handles[handle].in_use = false;
|
||||||
|
if (dir_handles[handle].mutex) {
|
||||||
|
free(dir_handles[handle].mutex);
|
||||||
|
dir_handles[handle].mutex = nullptr;
|
||||||
|
}
|
||||||
|
memset(&dir_handles[handle], 0, sizeof(dirMagic_t));
|
||||||
|
DCFlushRange(&dir_handles[handle], sizeof(dirMagic_t));
|
||||||
|
dir_handle_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSOpenDirWrapper(char *path,
|
||||||
|
FSDirectoryHandle *handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pathForCheck[256];
|
||||||
|
|
||||||
|
getFullPath(pathForCheck, sizeof(pathForCheck), path);
|
||||||
|
|
||||||
|
if (checkForSave(pathForCheck, sizeof(pathForCheck), pathForCheck)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Redirect save to %s", pathForCheck);
|
||||||
|
return fallback_function(pathForCheck);
|
||||||
|
}
|
||||||
|
if (handle == nullptr) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid args");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(pathForCheck, "/vol/content", 12) == 0) {
|
||||||
|
memcpy(pathForCheck, "rom:", 4);
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
|
||||||
|
int handle_index = getNewDirHandleIndex();
|
||||||
|
|
||||||
|
if (handle_index >= 0) {
|
||||||
|
DIR *dir;
|
||||||
|
if ((dir = opendir(pathForCheck))) {
|
||||||
|
OSLockMutex(dir_handles[handle_index].mutex);
|
||||||
|
dir_handles[handle_index].handle = DIR_HANDLE_MAGIC | handle_index;
|
||||||
|
*handle = dir_handles[handle_index].handle;
|
||||||
|
dir_handles[handle_index].dir = dir;
|
||||||
|
dir_handles[handle_index].in_use = true;
|
||||||
|
strncpy(dir_handles[handle_index].path, pathForCheck, 255);
|
||||||
|
DCFlushRange(&dir_handles[handle_index], sizeof(dirMagic_t));
|
||||||
|
OSUnlockMutex(dir_handles[handle_index].mutex);
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE("Dir not found %s", pathForCheck);
|
||||||
|
if (errorMask & FS_ERROR_FLAG_NOT_FOUND) {
|
||||||
|
result = FS_STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (errorMask & FS_ERROR_FLAG_MAX) {
|
||||||
|
result = FS_STATUS_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
|
||||||
|
FSDirectoryEntry *entry,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidDirHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
if (handle_index >= DIR_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return result_handler(FS_STATUS_FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSLockMutex(dir_handles[handle_index].mutex);
|
||||||
|
DIR *dir = dir_handles[handle_index].dir;
|
||||||
|
|
||||||
|
struct dirent *entry_ = readdir(dir);
|
||||||
|
FSStatus result = FS_STATUS_END;
|
||||||
|
if (entry_) {
|
||||||
|
strncpy(entry->name, entry_->d_name, 254);
|
||||||
|
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{};
|
||||||
|
int strLen = strlen(dir_handles[handle_index].path) + 1 + strlen(entry_->d_name) + 1;
|
||||||
|
char path[strLen];
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", dir_handles[handle_index].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;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSUnlockMutex(dir_handles[handle_index].mutex);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSCloseDirWrapper(FSDirectoryHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidDirHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
if (handle_index >= DIR_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSLockMutex(dir_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
DIR *dir = dir_handles[handle_index].dir;
|
||||||
|
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (closedir(dir) != 0) {
|
||||||
|
result = FS_STATUS_MEDIA_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSUnlockMutex(dir_handles[handle_index].mutex);
|
||||||
|
freeDirHandle(handle_index);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSRewindDirWrapper(FSDirectoryHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidDirHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
if (handle_index >= DIR_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
uint32_t real_handle = handle & HANDLE_MASK;
|
||||||
|
OSLockMutex(dir_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
DIR *dir = dir_handles[real_handle].dir;
|
||||||
|
rewinddir(dir);
|
||||||
|
OSUnlockMutex(dir_handles[handle_index].mutex);
|
||||||
|
return result_handler(FS_STATUS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSMakeDirWrapper(char *path,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pathForCheck[256];
|
||||||
|
|
||||||
|
getFullPath(pathForCheck, sizeof(pathForCheck), path);
|
||||||
|
|
||||||
|
if (checkForSave(pathForCheck, sizeof(pathForCheck), pathForCheck)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Redirect save to %s", pathForCheck);
|
||||||
|
return fallback_function(pathForCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(pathForCheck, "/vol/content", 12) == 0) {
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (errorMask & FS_ERROR_FLAG_ACCESS_ERROR) {
|
||||||
|
result = FS_STATUS_ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSOpenFileWrapper(char *path,
|
||||||
|
const char *mode,
|
||||||
|
FSFileHandle *handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
if (path == nullptr) {
|
||||||
|
OSFATAL_FUNCTION_LINE("Invalid args");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pathForCheck[256];
|
||||||
|
|
||||||
|
getFullPath(pathForCheck, sizeof(pathForCheck), path);
|
||||||
|
|
||||||
|
if (checkForSave(pathForCheck, sizeof(pathForCheck), pathForCheck)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Redirect save to %s", pathForCheck);
|
||||||
|
return fallback_function(pathForCheck);
|
||||||
|
}
|
||||||
|
if (mode == nullptr || handle == nullptr) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid args");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(pathForCheck, "/vol/content", 12) == 0) {
|
||||||
|
memcpy(pathForCheck, "rom:", 4);
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("Trying to open %s", pathForCheck);
|
||||||
|
int handle_index = getNewFileHandleIndex();
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (handle_index >= 0) {
|
||||||
|
OSLockMutex(file_handles[handle_index].mutex);
|
||||||
|
int _mode = 0;
|
||||||
|
// Map flags to open modes
|
||||||
|
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("%s", mode);
|
||||||
|
if (errorMask & FS_ERROR_FLAG_ACCESS_ERROR) {
|
||||||
|
result = FS_STATUS_ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t fd = open(pathForCheck, _mode);
|
||||||
|
if (fd >= 0) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("opened file successfully %d", fd);
|
||||||
|
|
||||||
|
file_handles[handle_index].handle = FILE_HANDLE_MAGIC + handle_index;
|
||||||
|
//DEBUG_FUNCTION_LINE("handle %08X", file_handles[handle_index].handle);
|
||||||
|
*handle = file_handles[handle_index].handle;
|
||||||
|
file_handles[handle_index].fd = fd;
|
||||||
|
file_handles[handle_index].in_use = true;
|
||||||
|
DCFlushRange(&file_handles[handle_index], sizeof(fileMagic_t));
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE("File not found %s", pathForCheck);
|
||||||
|
if (errorMask & FS_ERROR_FLAG_NOT_FOUND) {
|
||||||
|
result = FS_STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OSUnlockMutex(file_handles[handle_index].mutex);
|
||||||
|
} else {
|
||||||
|
if (errorMask & FS_ERROR_FLAG_MAX) {
|
||||||
|
result = FS_STATUS_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSCloseFileWrapper(FSFileHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
|
||||||
|
if (handle_index >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSLockMutex(file_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
int real_fd = file_handles[handle_index].fd;
|
||||||
|
file_handles[handle_index].in_use = false;
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("closing %d", real_fd);
|
||||||
|
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (close(real_fd) != 0) {
|
||||||
|
result = FS_STATUS_MEDIA_ERROR;
|
||||||
|
}
|
||||||
|
OSUnlockMutex(file_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
freeFileHandle(handle_index);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSGetStatWrapper(char *path, FSStat *stats, FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
if (path == nullptr) {
|
||||||
|
OSFATAL_FUNCTION_LINE("Invalid args");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pathForCheck[256];
|
||||||
|
|
||||||
|
getFullPath(pathForCheck, sizeof(pathForCheck), path);
|
||||||
|
|
||||||
|
if (checkForSave(pathForCheck, sizeof(pathForCheck), pathForCheck)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Redirect save to %s", pathForCheck);
|
||||||
|
return fallback_function(pathForCheck);
|
||||||
|
}
|
||||||
|
if (strncmp(pathForCheck, "/vol/content", 12) == 0) {
|
||||||
|
memcpy(pathForCheck, "rom:", 4);
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (stats == nullptr) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid args");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
} else {
|
||||||
|
struct stat path_stat{};
|
||||||
|
memset(&path_stat, 0, sizeof(path_stat));
|
||||||
|
if (stat(pathForCheck, &path_stat) < 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("Path not found %s", pathForCheck);
|
||||||
|
if (errorMask & FS_ERROR_FLAG_NOT_FOUND) {
|
||||||
|
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("stats file for %s, size %016lLX", path, stats->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSGetStatFileWrapper(FSFileHandle handle,
|
||||||
|
FSStat *stats,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
|
||||||
|
if (handle_index >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSLockMutex(file_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
int real_fd = file_handles[handle_index].fd;
|
||||||
|
//DEBUG_FUNCTION_LINE("FSGetStatFileAsync real_fd %d", real_fd);
|
||||||
|
|
||||||
|
struct stat path_stat{};
|
||||||
|
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (fstat(real_fd, &path_stat) < 0) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSUnlockMutex(file_handles[handle_index].mutex);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSReadFileWrapper(void *buffer,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t count,
|
||||||
|
FSFileHandle handle,
|
||||||
|
uint32_t unk1,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
|
||||||
|
if (handle_index >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size * count == 0) {
|
||||||
|
return result_handler(FS_STATUS_OK);
|
||||||
|
}
|
||||||
|
if (buffer == nullptr && (size * count != 0)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Fatal read FSErrorFlag errorMask, buffer is null but size*count is not");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus result;
|
||||||
|
|
||||||
|
OSLockMutex(file_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
int real_fd = file_handles[handle_index].fd;
|
||||||
|
|
||||||
|
int32_t read = readIntoBuffer(real_fd, buffer, size, count);
|
||||||
|
|
||||||
|
if (read < 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to read from handle");
|
||||||
|
result = FS_STATUS_MEDIA_ERROR;
|
||||||
|
} else {
|
||||||
|
result = (FSStatus) (read / size);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSUnlockMutex(file_handles[handle_index].mutex);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSReadFileWithPosWrapper(void *buffer,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t count,
|
||||||
|
uint32_t pos,
|
||||||
|
FSFileHandle handle,
|
||||||
|
int32_t unk1,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
FSStatus result;
|
||||||
|
if ((result = FSSetPosFileWrapper(handle, pos, errorMask,
|
||||||
|
[](FSStatus res) -> FSStatus { return res; })
|
||||||
|
) != FS_STATUS_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = FSReadFileWrapper(buffer, size, count, handle, unk1, errorMask,
|
||||||
|
[](FSStatus res) -> FSStatus { return res; }
|
||||||
|
);
|
||||||
|
if (result != FS_STATUS_USE_REAL_OS && result != FS_STATUS_FATAL_ERROR) {
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSSetPosFileWrapper(FSFileHandle handle,
|
||||||
|
uint32_t pos,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
|
||||||
|
if (handle_index >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
OSLockMutex(file_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
int real_fd = file_handles[handle_index].fd;
|
||||||
|
|
||||||
|
if (lseek(real_fd, (off_t) pos, SEEK_SET) != pos) {
|
||||||
|
DEBUG_FUNCTION_LINE("Seek failed");
|
||||||
|
result = FS_STATUS_MEDIA_ERROR;
|
||||||
|
}
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("pos set to %d ", pos, real_fd);
|
||||||
|
|
||||||
|
OSUnlockMutex(file_handles[handle_index].mutex);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSGetPosFileWrapper(FSFileHandle handle,
|
||||||
|
uint32_t *pos,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
|
||||||
|
if (handle_index >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
OSLockMutex(file_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
int real_fd = file_handles[handle_index].fd;
|
||||||
|
|
||||||
|
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
|
||||||
|
if (currentPos == -1) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to get current pos");
|
||||||
|
result = FS_STATUS_MEDIA_ERROR;
|
||||||
|
} else {
|
||||||
|
*pos = currentPos;
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("result was %d for %d", *pos, real_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSUnlockMutex(file_handles[handle_index].mutex);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSIsEofWrapper(FSFileHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
|
||||||
|
if (handle_index >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
OSLockMutex(file_handles[handle_index].mutex);
|
||||||
|
|
||||||
|
int real_fd = file_handles[handle_index].fd;
|
||||||
|
|
||||||
|
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
|
||||||
|
off_t endPos = lseek(real_fd, (off_t) 0, SEEK_END);
|
||||||
|
|
||||||
|
if (currentPos == endPos) {
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("FSIsEof END for %d\n", real_fd);
|
||||||
|
result = FS_STATUS_END;
|
||||||
|
} else {
|
||||||
|
lseek(real_fd, currentPos, SEEK_CUR);
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("FSIsEof OK for %d\n", real_fd);
|
||||||
|
result = FS_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSUnlockMutex(file_handles[handle_index].mutex);
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSTruncateFileWrapper(FSFileHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
uint32_t handle_index = handle & HANDLE_MASK;
|
||||||
|
|
||||||
|
if (handle_index >= FILE_HANDLES_LENGTH) {
|
||||||
|
DEBUG_FUNCTION_LINE("Invalid handle");
|
||||||
|
return FS_STATUS_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (errorMask & FS_ERROR_FLAG_ACCESS_ERROR) {
|
||||||
|
result = FS_STATUS_ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSWriteFileWrapper(uint8_t *buffer,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t count,
|
||||||
|
FSFileHandle handle,
|
||||||
|
uint32_t unk1,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (errorMask & FS_ERROR_FLAG_ACCESS_ERROR) {
|
||||||
|
result = FS_STATUS_ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSRemoveWrapper(char *path,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == nullptr) {
|
||||||
|
OSFATAL_FUNCTION_LINE("Invalid args");
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pathForCheck[256];
|
||||||
|
|
||||||
|
getFullPath(pathForCheck, sizeof(pathForCheck), path);
|
||||||
|
|
||||||
|
if (checkForSave(pathForCheck, sizeof(pathForCheck), pathForCheck)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Redirect save to %s", pathForCheck);
|
||||||
|
return fallback_function(pathForCheck);
|
||||||
|
}
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (errorMask & FS_ERROR_FLAG_ACCESS_ERROR) {
|
||||||
|
result = FS_STATUS_ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSRenameWrapper(char *oldPath,
|
||||||
|
char *newPath,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_oldPath, char *_newPath)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pathForCheck1[256];
|
||||||
|
char pathForCheck2[256];
|
||||||
|
|
||||||
|
getFullPath(pathForCheck1, sizeof(pathForCheck1), oldPath);
|
||||||
|
getFullPath(pathForCheck2, sizeof(pathForCheck2), newPath);
|
||||||
|
|
||||||
|
if (checkForSave(pathForCheck1, sizeof(pathForCheck1), pathForCheck1)) {
|
||||||
|
if (checkForSave(pathForCheck2, sizeof(pathForCheck2), pathForCheck2)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Redirect save to %s/%s", pathForCheck1, pathForCheck2);
|
||||||
|
return fallback_function(pathForCheck1, pathForCheck2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
|
||||||
|
if (errorMask & FS_ERROR_FLAG_ACCESS_ERROR) {
|
||||||
|
result = FS_STATUS_ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSStatus FSFlushFileWrapper(FSFileHandle handle, FSErrorFlag errorMask, std::function<FSStatus(FSStatus)> result_handler) {
|
||||||
|
if (!gIsMounted || !isValidFileHandle(handle)) {
|
||||||
|
return FS_STATUS_USE_REAL_OS;
|
||||||
|
}
|
||||||
|
FSStatus result = FS_STATUS_OK;
|
||||||
|
if (errorMask & FS_ERROR_FLAG_ACCESS_ERROR) {
|
||||||
|
result = FS_STATUS_ACCESS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result_handler(result);
|
||||||
|
}
|
147
src/FSWrapper.h
Normal file
147
src/FSWrapper.h
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include <coreinit/mutex.h>
|
||||||
|
|
||||||
|
typedef struct dirMagic {
|
||||||
|
uint32_t handle;
|
||||||
|
DIR *dir;
|
||||||
|
bool in_use;
|
||||||
|
char path[255];
|
||||||
|
OSMutex *mutex;
|
||||||
|
} dirMagic_t;
|
||||||
|
|
||||||
|
typedef struct fileMagic {
|
||||||
|
uint32_t handle;
|
||||||
|
bool in_use;
|
||||||
|
int fd;
|
||||||
|
OSMutex *mutex;
|
||||||
|
} fileMagic_t;
|
||||||
|
|
||||||
|
#define HANDLE_INDICATOR_MASK 0xFFFFFF00
|
||||||
|
#define HANDLE_MASK (0x000000FF)
|
||||||
|
#define DIR_HANDLE_MAGIC 0x30000000
|
||||||
|
#define FILE_HANDLE_MAGIC 0x30000100
|
||||||
|
|
||||||
|
#define FILE_HANDLES_LENGTH 64
|
||||||
|
#define DIR_HANDLES_LENGTH 64
|
||||||
|
|
||||||
|
#define FS_STATUS_USE_REAL_OS (FSStatus) 0xFFFF0000
|
||||||
|
|
||||||
|
extern dirMagic_t dir_handles[DIR_HANDLES_LENGTH];
|
||||||
|
extern fileMagic_t file_handles[FILE_HANDLES_LENGTH];
|
||||||
|
|
||||||
|
FSStatus FSOpenDirWrapper(char *path,
|
||||||
|
FSDirectoryHandle *handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
|
||||||
|
FSDirectoryEntry *entry,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSCloseDirWrapper(FSDirectoryHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSRewindDirWrapper(FSDirectoryHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSMakeDirWrapper(char *path,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSOpenFileWrapper(char *path,
|
||||||
|
const char *mode,
|
||||||
|
FSFileHandle *handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSCloseFileWrapper(FSFileHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSGetStatWrapper(char *path,
|
||||||
|
FSStat *stats,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSGetStatFileWrapper(FSFileHandle handle,
|
||||||
|
FSStat *stats,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSReadFileWrapper(void *buffer,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t count,
|
||||||
|
FSFileHandle handle,
|
||||||
|
uint32_t unk1,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSReadFileWithPosWrapper(void *buffer,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t count,
|
||||||
|
uint32_t pos,
|
||||||
|
FSFileHandle handle,
|
||||||
|
int32_t unk1,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSSetPosFileWrapper(FSFileHandle handle,
|
||||||
|
uint32_t pos,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSGetPosFileWrapper(FSFileHandle handle,
|
||||||
|
uint32_t *pos,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSIsEofWrapper(FSFileHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSTruncateFileWrapper(FSFileHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSWriteFileWrapper(uint8_t *buffer,
|
||||||
|
uint32_t size,
|
||||||
|
uint32_t count,
|
||||||
|
FSFileHandle handle,
|
||||||
|
uint32_t unk1,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSRemoveWrapper(char *path,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_path)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSRenameWrapper(char *oldPath,
|
||||||
|
char *newPath,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(char *_oldPath, char *_newPath)> fallback_function,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
FSStatus FSFlushFileWrapper(FSFileHandle handle,
|
||||||
|
FSErrorFlag errorMask,
|
||||||
|
std::function<FSStatus(FSStatus)> result_handler);
|
||||||
|
|
||||||
|
int32_t getNewDirHandleIndex();
|
||||||
|
|
||||||
|
int32_t getNewFileHandleIndex();
|
||||||
|
|
||||||
|
bool isValidDirHandle(int32_t handle);
|
||||||
|
|
||||||
|
bool isValidFileHandle(int32_t handle);
|
199
src/FileUtils.cpp
Normal file
199
src/FileUtils.cpp
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
#include "FileUtils.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <coreinit/debug.h>
|
||||||
|
#include <map>
|
||||||
|
#include <coreinit/thread.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include "utils/StringTools.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
#include "utils/logger.h"
|
||||||
|
|
||||||
|
extern "C" OSMessageQueue *OSGetDefaultAppIOQueue();
|
||||||
|
|
||||||
|
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock) {
|
||||||
|
if (!cmdBlock) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto body = (FSCmdBlockBody *) (ROUNDUP((uint32_t) cmdBlock, 0x40));
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mutex sendMutex;
|
||||||
|
|
||||||
|
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus status) {
|
||||||
|
if (asyncData->callback != nullptr) {
|
||||||
|
if (asyncData->ioMsgQueue != nullptr) {
|
||||||
|
OSFatal("ERROR: 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) {
|
||||||
|
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("Failed to send message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FS_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count) {
|
||||||
|
int32_t 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 = -1;
|
||||||
|
int32_t totalSize = 0;
|
||||||
|
int32_t toRead = 0;
|
||||||
|
while (sizeToRead > 0) {
|
||||||
|
curResult = read(handle, newBuffer, sizeToRead);
|
||||||
|
if (curResult < 0) {
|
||||||
|
int errsv = errno;
|
||||||
|
DEBUG_FUNCTION_LINE("Error: Reading %08X bytes from handle %08X. result %08X errno: %d ", size * count, handle, curResult, errsv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (curResult == 0) {
|
||||||
|
//DEBUG_FUNCTION_LINE("EOF");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newBuffer = (void *) (((uint32_t) newBuffer) + curResult);
|
||||||
|
totalSize += curResult;
|
||||||
|
sizeToRead -= curResult;
|
||||||
|
if (sizeToRead > 0) {
|
||||||
|
//DEBUG_FUNCTION_LINE("Reading. missing %08X bytes\n", sizeToRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
////DEBUG_FUNCTION_LINE("Success: Read %08X bytes from handle %08X. result %08X \n", size * count, handle, totalSize);
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t CheckFile(const char * filepath) {
|
||||||
|
if(!filepath)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct stat filestat{};
|
||||||
|
|
||||||
|
char dirnoslash[strlen(filepath)+2];
|
||||||
|
snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath);
|
||||||
|
|
||||||
|
while(dirnoslash[strlen(dirnoslash)-1] == '/')
|
||||||
|
dirnoslash[strlen(dirnoslash)-1] = '\0';
|
||||||
|
|
||||||
|
char * notRoot = strrchr(dirnoslash, '/');
|
||||||
|
if(!notRoot) {
|
||||||
|
strcat(dirnoslash, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(dirnoslash, &filestat) == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t CreateSubfolder(const char * fullpath) {
|
||||||
|
if(!fullpath)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int32_t result = 0;
|
||||||
|
|
||||||
|
char dirnoslash[strlen(fullpath)+1];
|
||||||
|
strcpy(dirnoslash, fullpath);
|
||||||
|
|
||||||
|
int32_t pos = strlen(dirnoslash)-1;
|
||||||
|
while(dirnoslash[pos] == '/') {
|
||||||
|
dirnoslash[pos] = '\0';
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CheckFile(dirnoslash)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
char parentpath[strlen(dirnoslash)+2];
|
||||||
|
strcpy(parentpath, dirnoslash);
|
||||||
|
char * ptr = strrchr(parentpath, '/');
|
||||||
|
|
||||||
|
if(!ptr) {
|
||||||
|
//!Device root directory (must be with '/')
|
||||||
|
strcat(parentpath, "/");
|
||||||
|
struct stat filestat{};
|
||||||
|
if (stat(parentpath, &filestat) == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr++;
|
||||||
|
ptr[0] = '\0';
|
||||||
|
|
||||||
|
result = CreateSubfolder(parentpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mkdir(dirnoslash, 0777) == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t getRPXInfoForPath(const std::string &path, romfs_fileInfo *info) {
|
||||||
|
if (romfsMount("rcc", path.c_str(), RomfsSource_FileDescriptor_CafeOS) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
if (!(dir = opendir("rcc:/"))) {
|
||||||
|
romfsUnmount("rcc");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
bool found = false;
|
||||||
|
int res = -3;
|
||||||
|
while ((entry = readdir(dir)) != nullptr) {
|
||||||
|
DEBUG_FUNCTION_LINE("%s", entry->d_name);
|
||||||
|
if (StringTools::EndsWith(entry->d_name, ".rpx")) {
|
||||||
|
if (romfsGetFileInfoPerPath("rcc", entry->d_name, info) >= 0) {
|
||||||
|
found = true;
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
romfsUnmount("rcc");
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
25
src/FileUtils.h
Normal file
25
src/FileUtils.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include <romfs_dev.h>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock);
|
||||||
|
|
||||||
|
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus result);
|
||||||
|
|
||||||
|
int32_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count);
|
||||||
|
|
||||||
|
int32_t CreateSubfolder(const char *fullpath);
|
||||||
|
|
||||||
|
int32_t getRPXInfoForPath(const std::string &path, romfs_fileInfo *info);
|
50
src/RPXLoading.cpp
Normal file
50
src/RPXLoading.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <coreinit/ios.h>
|
||||||
|
#include <romfs_dev.h>
|
||||||
|
#include "utils/logger.h"
|
||||||
|
#include "RPXLoading.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "FileUtils.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool loadRPXFromSDOnNextLaunch(const std::string &path) {
|
||||||
|
LOAD_REQUEST request;
|
||||||
|
memset(&request, 0, sizeof(request));
|
||||||
|
|
||||||
|
request.command = 0xFC; // IPC_CUSTOM_LOAD_CUSTOM_RPX;
|
||||||
|
request.target = 0; // LOAD_FILE_TARGET_SD_CARD
|
||||||
|
request.filesize = 0; // unknown
|
||||||
|
request.fileoffset = 0; //
|
||||||
|
|
||||||
|
romfs_fileInfo info;
|
||||||
|
|
||||||
|
std::string completePath = "/vol/external01/" + path;
|
||||||
|
int res = getRPXInfoForPath(completePath, &info);
|
||||||
|
if (res >= 0) {
|
||||||
|
request.filesize = ((uint32_t * ) & info.length)[1];
|
||||||
|
request.fileoffset = ((uint32_t * ) & info.offset)[1];
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE("not a bundle %s %d", completePath.c_str(), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(request.path, path.c_str(), 255);
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE("Loading file %s size: %08X offset: %08X", request.path, request.filesize, request.fileoffset);
|
||||||
|
|
||||||
|
DCFlushRange(&request, sizeof(LOAD_REQUEST));
|
||||||
|
|
||||||
|
int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0);
|
||||||
|
if (mcpFd >= 0) {
|
||||||
|
int out = 0;
|
||||||
|
IOS_Ioctl(mcpFd, 100, &request, sizeof(request), &out, sizeof(out));
|
||||||
|
IOS_Close(mcpFd);
|
||||||
|
}
|
||||||
|
|
||||||
|
gTryToReplaceOnNextLaunch = true;
|
||||||
|
memset(gLoadedBundlePath,0, sizeof(gLoadedBundlePath));
|
||||||
|
strncpy(gLoadedBundlePath, completePath.c_str(), completePath.length());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUMS_EXPORT_FUNCTION(loadRPXFromSDOnNextLaunch);
|
10
src/RPXLoading.h
Normal file
10
src/RPXLoading.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct __attribute((packed)) {
|
||||||
|
uint32_t command;
|
||||||
|
uint32_t target;
|
||||||
|
uint32_t filesize;
|
||||||
|
uint32_t fileoffset;
|
||||||
|
char path[256];
|
||||||
|
} LOAD_REQUEST;
|
||||||
|
|
9
src/globals.cpp
Normal file
9
src/globals.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
bool gIsMounted __attribute__((section(".data"))) = false;
|
||||||
|
uint32_t gCurrentHash __attribute__((section(".data"))) = 0;
|
||||||
|
bool gTryToReplaceOnNextLaunch __attribute__((section(".data"))) = false;
|
||||||
|
char gLoadedBundlePath[256] __attribute__((section(".data")));
|
||||||
|
char gSavePath[256] __attribute__((section(".data")));
|
||||||
|
char gWorkingDir[256] __attribute__((section(".data")));
|
||||||
|
char gTempPath[256] __attribute__((section(".data")));
|
10
src/globals.h
Normal file
10
src/globals.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include <wums.h>
|
||||||
|
#include <coreinit/mutex.h>
|
||||||
|
|
||||||
|
extern bool gIsMounted;
|
||||||
|
extern bool gTryToReplaceOnNextLaunch;
|
||||||
|
extern char gLoadedBundlePath[256];
|
||||||
|
extern char gSavePath[256];
|
||||||
|
extern char gWorkingDir[256];
|
||||||
|
extern char gTempPath[256];
|
||||||
|
extern uint32_t gCurrentHash;
|
73
src/main.cpp
Normal file
73
src/main.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include <wums.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <whb/log_udp.h>
|
||||||
|
#include <coreinit/debug.h>
|
||||||
|
#include <coreinit/title.h>
|
||||||
|
#include <sysapp/title.h>
|
||||||
|
#include <string>
|
||||||
|
#include "utils/logger.h"
|
||||||
|
#include "utils/StringTools.h"
|
||||||
|
#include "FSFileReplacements.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "FileUtils.h"
|
||||||
|
#include "FSDirReplacements.h"
|
||||||
|
|
||||||
|
#include <romfs_dev.h>
|
||||||
|
|
||||||
|
WUMS_MODULE_EXPORT_NAME("homebrew_rpx_loader");
|
||||||
|
|
||||||
|
WUMS_INITIALIZE(args) {
|
||||||
|
WHBLogUdpInit();
|
||||||
|
DEBUG_FUNCTION_LINE("Patch functions");
|
||||||
|
// we only patch static functions :)
|
||||||
|
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");
|
||||||
|
gIsMounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WUMS_APPLICATION_ENDS() {
|
||||||
|
DEBUG_FUNCTION_LINE("bye bye from rpx loader");
|
||||||
|
if (gIsMounted) {
|
||||||
|
romfsUnmount("rom");
|
||||||
|
gIsMounted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WUMS_APPLICATION_STARTS() {
|
||||||
|
uint32_t upid = OSGetUPID();
|
||||||
|
if (upid != 2 && upid != 15) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WHBLogUdpInit();
|
||||||
|
if (_SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_HEALTH_AND_SAFETY) != OSGetTitleID()) {
|
||||||
|
DEBUG_FUNCTION_LINE("gTryToReplaceOnNextLaunch and gIsMounted to FALSE");
|
||||||
|
gTryToReplaceOnNextLaunch = false;
|
||||||
|
gIsMounted = false;
|
||||||
|
} else {
|
||||||
|
if (gTryToReplaceOnNextLaunch) {
|
||||||
|
gCurrentHash = StringTools::hash(gLoadedBundlePath);
|
||||||
|
|
||||||
|
std::string basePath = StringTools::strfmt("/vol/external01/wiiu/apps/save/%08X", gCurrentHash);
|
||||||
|
std::string common = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/common", gCurrentHash);
|
||||||
|
std::string user = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/80000002", gCurrentHash);
|
||||||
|
|
||||||
|
strncpy(gSavePath,basePath.c_str(), 255);
|
||||||
|
memset(gWorkingDir,0, sizeof(gWorkingDir));
|
||||||
|
|
||||||
|
CreateSubfolder(common.c_str());
|
||||||
|
CreateSubfolder(user.c_str());
|
||||||
|
DEBUG_FUNCTION_LINE("Created %s and %s", common.c_str(), user.c_str());
|
||||||
|
if (romfsMount("rom", gLoadedBundlePath, RomfsSource_FileDescriptor_CafeOS) == 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("MOUNTED!");
|
||||||
|
gIsMounted = true;
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE("MOUNTED FAILED %s", gLoadedBundlePath);
|
||||||
|
gIsMounted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
122
src/utils/OnLeavingScope.h
Normal file
122
src/utils/OnLeavingScope.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are based on the article posted at the
|
||||||
|
* following location:
|
||||||
|
*
|
||||||
|
* http://crascit.com/2015/06/03/on-leaving-scope-part-2/
|
||||||
|
*
|
||||||
|
* The material in that article has some commonality with the code made
|
||||||
|
* available as part of Facebook's folly library at:
|
||||||
|
*
|
||||||
|
* https://github.com/facebook/folly/blob/master/folly/ScopeGuard.h
|
||||||
|
*
|
||||||
|
* Furthermore, similar material is currently part of a draft proposal
|
||||||
|
* to the C++ standards committee, referencing the same work by Andrei
|
||||||
|
* Alexandresu that led to the folly implementation. The draft proposal
|
||||||
|
* can be found at:
|
||||||
|
*
|
||||||
|
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189.pdf
|
||||||
|
*
|
||||||
|
* With the above in mind, the content below is made available under
|
||||||
|
* the same license terms as folly to minimize any legal concerns.
|
||||||
|
* Should there be ambiguity over copyright ownership between Facebook
|
||||||
|
* and myself for any material included in this file, it should be
|
||||||
|
* interpreted that Facebook is the copyright owner for the ambiguous
|
||||||
|
* section of code concerned.
|
||||||
|
*
|
||||||
|
* Craig Scott
|
||||||
|
* 3rd June 2015
|
||||||
|
*
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Copyright 2015 Craig Scott
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRASCIT_ONLEAVINGSCOPE_H
|
||||||
|
#define CRASCIT_ONLEAVINGSCOPE_H
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is intended to be used only to create a local object on the
|
||||||
|
* stack. It accepts a function object in its constructor and will invoke
|
||||||
|
* that function object in its destructor. The class provides a move
|
||||||
|
* constructor so it can be used with the onLeavingScope factory function,
|
||||||
|
* but it cannot be copied.
|
||||||
|
*
|
||||||
|
* Do no use this function directly, use the onLeavingScope() factory
|
||||||
|
* function instead.
|
||||||
|
*/
|
||||||
|
template<typename Func>
|
||||||
|
class OnLeavingScope
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Prevent copying
|
||||||
|
OnLeavingScope(const OnLeavingScope&) = delete;
|
||||||
|
OnLeavingScope& operator=(const OnLeavingScope&) = delete;
|
||||||
|
|
||||||
|
// Allow moving
|
||||||
|
OnLeavingScope(OnLeavingScope&& other) :
|
||||||
|
m_func(std::move(other.m_func)),
|
||||||
|
m_owner(other.m_owner)
|
||||||
|
{
|
||||||
|
other.m_owner = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnLeavingScope(const Func& f) :
|
||||||
|
m_func(f),
|
||||||
|
m_owner(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
OnLeavingScope(Func&& f) :
|
||||||
|
m_func(std::move(f)),
|
||||||
|
m_owner(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~OnLeavingScope()
|
||||||
|
{
|
||||||
|
if (m_owner)
|
||||||
|
m_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Func m_func;
|
||||||
|
bool m_owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory function for creating an OnLeavingScope object. It is intended
|
||||||
|
* to be used like so:
|
||||||
|
*
|
||||||
|
* auto cleanup = onLeavingScope(...);
|
||||||
|
*
|
||||||
|
* where the ... could be a lambda function, function object or pointer to
|
||||||
|
* a free function to be invoked when the cleanup object goes out of scope.
|
||||||
|
* The function object must take no function arguments, but can return any
|
||||||
|
* type (the return value is ignored).
|
||||||
|
*
|
||||||
|
* The \a Func template parameter would rarely, if ever, be manually
|
||||||
|
* specified. Normally, it would be deduced automatically by the compiler
|
||||||
|
* from the object passed as the function argument.
|
||||||
|
*/
|
||||||
|
template<typename Func>
|
||||||
|
OnLeavingScope<typename std::decay<Func>::type> onLeavingScope(Func&& f)
|
||||||
|
{
|
||||||
|
return OnLeavingScope<typename std::decay<Func>::type>(std::forward<Func>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CRASCIT_ONLEAVINGSCOPE_H
|
305
src/utils/StringTools.cpp
Normal file
305
src/utils/StringTools.cpp
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010
|
||||||
|
* by Dimok
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any
|
||||||
|
* damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any
|
||||||
|
* purpose, including commercial applications, and to alter it and
|
||||||
|
* redistribute it freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you
|
||||||
|
* must not claim that you wrote the original software. If you use
|
||||||
|
* this software in a product, an acknowledgment in the product
|
||||||
|
* documentation would be appreciated but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and
|
||||||
|
* must not be misrepresented as being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from any source
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* for WiiXplorer 2010
|
||||||
|
***************************************************************************/
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <wut_types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <utils/StringTools.h>
|
||||||
|
|
||||||
|
|
||||||
|
BOOL StringTools::EndsWith(const std::string &a, const std::string &b) {
|
||||||
|
if (b.size() > a.size())
|
||||||
|
return false;
|
||||||
|
return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *StringTools::byte_to_binary(int32_t x) {
|
||||||
|
static char b[9];
|
||||||
|
b[0] = '\0';
|
||||||
|
|
||||||
|
int32_t z;
|
||||||
|
for (z = 128; z > 0; z >>= 1) {
|
||||||
|
strcat(b, ((x & z) == z) ? "1" : "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StringTools::removeCharFromString(std::string &input, char toBeRemoved) {
|
||||||
|
std::string output = input;
|
||||||
|
size_t position;
|
||||||
|
while (1) {
|
||||||
|
position = output.find(toBeRemoved);
|
||||||
|
if (position == std::string::npos)
|
||||||
|
break;
|
||||||
|
output.erase(position, 1);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *StringTools::fmt(const char *format, ...) {
|
||||||
|
static char strChar[512];
|
||||||
|
strChar[0] = 0;
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
va_start(va, format);
|
||||||
|
if ((vsprintf(strChar, format, va) >= 0)) {
|
||||||
|
va_end(va);
|
||||||
|
return (const char *) strChar;
|
||||||
|
}
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t *StringTools::wfmt(const char *format, ...) {
|
||||||
|
static char tmp[512];
|
||||||
|
static wchar_t strWChar[512];
|
||||||
|
strWChar[0] = 0;
|
||||||
|
tmp[0] = 0;
|
||||||
|
|
||||||
|
if (!format)
|
||||||
|
return (const wchar_t *) strWChar;
|
||||||
|
|
||||||
|
if (strcmp(format, "") == 0)
|
||||||
|
return (const wchar_t *) strWChar;
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
va_start(va, format);
|
||||||
|
if ((vsprintf(tmp, format, va) >= 0)) {
|
||||||
|
int bt;
|
||||||
|
int32_t strlength = strlen(tmp);
|
||||||
|
bt = mbstowcs(strWChar, tmp, (strlength < 512) ? strlength : 512);
|
||||||
|
|
||||||
|
if (bt > 0) {
|
||||||
|
strWChar[bt] = 0;
|
||||||
|
return (const wchar_t *) strWChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t StringTools::strprintf(std::string &str, const char *format, ...) {
|
||||||
|
static char tmp[512];
|
||||||
|
tmp[0] = 0;
|
||||||
|
int32_t result = 0;
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
va_start(va, format);
|
||||||
|
if ((vsprintf(tmp, format, va) >= 0)) {
|
||||||
|
str = tmp;
|
||||||
|
result = str.size();
|
||||||
|
}
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StringTools::strfmt(const char *format, ...) {
|
||||||
|
std::string str;
|
||||||
|
static char tmp[512];
|
||||||
|
tmp[0] = 0;
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
va_start(va, format);
|
||||||
|
if ((vsprintf(tmp, format, va) >= 0)) {
|
||||||
|
str = tmp;
|
||||||
|
}
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL StringTools::char2wchar_t(const char *strChar, wchar_t *dest) {
|
||||||
|
if (!strChar || !dest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int bt;
|
||||||
|
bt = mbstowcs(dest, strChar, strlen(strChar));
|
||||||
|
if (bt > 0) {
|
||||||
|
dest[bt] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t StringTools::strtokcmp(const char *string, const char *compare, const char *separator) {
|
||||||
|
if (!string || !compare)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char TokCopy[512];
|
||||||
|
strncpy(TokCopy, compare, sizeof(TokCopy));
|
||||||
|
TokCopy[511] = '\0';
|
||||||
|
|
||||||
|
char *strTok = strtok(TokCopy, separator);
|
||||||
|
|
||||||
|
while (strTok != NULL) {
|
||||||
|
if (strcasecmp(string, strTok) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strTok = strtok(NULL, separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t StringTools::strextcmp(const char *string, const char *extension, char seperator) {
|
||||||
|
if (!string || !extension)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char *ptr = strrchr(string, seperator);
|
||||||
|
if (!ptr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return strcasecmp(ptr + 1, extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string> StringTools::stringSplit(const std::string &inValue, const std::string &splitter) {
|
||||||
|
std::string value = inValue;
|
||||||
|
std::vector<std::string> result;
|
||||||
|
while (true) {
|
||||||
|
uint32_t index = value.find(splitter);
|
||||||
|
if (index == std::string::npos) {
|
||||||
|
result.push_back(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::string first = value.substr(0, index);
|
||||||
|
result.push_back(first);
|
||||||
|
if (index + splitter.size() == value.length()) {
|
||||||
|
result.push_back("");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index + splitter.size() > value.length()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value = value.substr(index + splitter.size(), value.length());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *StringTools::FullpathToFilename(const char *path) {
|
||||||
|
if (!path)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
const char *ptr = path;
|
||||||
|
const char *Filename = ptr;
|
||||||
|
|
||||||
|
while (*ptr != '\0') {
|
||||||
|
if (ptr[0] == '/' && ptr[1] != '\0')
|
||||||
|
Filename = ptr + 1;
|
||||||
|
|
||||||
|
++ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StringTools::RemoveDoubleSlashs(std::string &str) {
|
||||||
|
uint32_t length = str.size();
|
||||||
|
|
||||||
|
//! clear path of double slashes
|
||||||
|
for (uint32_t i = 1; i < length; ++i) {
|
||||||
|
if (str[i - 1] == '/' && str[i] == '/') {
|
||||||
|
str.erase(i, 1);
|
||||||
|
i--;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// You must free the result if result is non-NULL.
|
||||||
|
char *StringTools::str_replace(char *orig, char *rep, char *with) {
|
||||||
|
char *result; // the return string
|
||||||
|
char *ins; // the next insert point
|
||||||
|
char *tmp; // varies
|
||||||
|
int len_rep; // length of rep (the string to remove)
|
||||||
|
int len_with; // length of with (the string to replace rep with)
|
||||||
|
int len_front; // distance between rep and end of last rep
|
||||||
|
int count; // number of replacements
|
||||||
|
|
||||||
|
// sanity checks and initialization
|
||||||
|
if (!orig || !rep)
|
||||||
|
return NULL;
|
||||||
|
len_rep = strlen(rep);
|
||||||
|
if (len_rep == 0)
|
||||||
|
return NULL; // empty rep causes infinite loop during count
|
||||||
|
if (!with)
|
||||||
|
with = "";
|
||||||
|
len_with = strlen(with);
|
||||||
|
|
||||||
|
// count the number of replacements needed
|
||||||
|
ins = orig;
|
||||||
|
for (count = 0; tmp = strstr(ins, rep); ++count) {
|
||||||
|
ins = tmp + len_rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = result = (char *) malloc(strlen(orig) + (len_with - len_rep) * count + 1);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// first time through the loop, all the variable are set correctly
|
||||||
|
// from here on,
|
||||||
|
// tmp points to the end of the result string
|
||||||
|
// ins points to the next occurrence of rep in orig
|
||||||
|
// orig points to the remainder of orig after "end of rep"
|
||||||
|
while (count--) {
|
||||||
|
ins = strstr(orig, rep);
|
||||||
|
len_front = ins - orig;
|
||||||
|
tmp = strncpy(tmp, orig, len_front) + len_front;
|
||||||
|
tmp = strcpy(tmp, with) + len_with;
|
||||||
|
orig += len_front + len_rep; // move to next "end of rep"
|
||||||
|
}
|
||||||
|
strcpy(tmp, orig);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* hash: compute hash value of string */
|
||||||
|
uint32_t StringTools::hash(char *str) {
|
||||||
|
unsigned int h;
|
||||||
|
unsigned char *p;
|
||||||
|
|
||||||
|
h = 0;
|
||||||
|
for (p = (unsigned char *) str; *p != '\0'; p++) {
|
||||||
|
h = 37 * h + *p;
|
||||||
|
}
|
||||||
|
return h; // or, h % ARRAY_SIZE;
|
||||||
|
}
|
||||||
|
|
67
src/utils/StringTools.h
Normal file
67
src/utils/StringTools.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2010
|
||||||
|
* by Dimok
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any
|
||||||
|
* damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any
|
||||||
|
* purpose, including commercial applications, and to alter it and
|
||||||
|
* redistribute it freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you
|
||||||
|
* must not claim that you wrote the original software. If you use
|
||||||
|
* this software in a product, an acknowledgment in the product
|
||||||
|
* documentation would be appreciated but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and
|
||||||
|
* must not be misrepresented as being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from any source
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* for WiiXplorer 2010
|
||||||
|
***************************************************************************/
|
||||||
|
#ifndef __STRING_TOOLS_H
|
||||||
|
#define __STRING_TOOLS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <wut_types.h>
|
||||||
|
|
||||||
|
class StringTools {
|
||||||
|
public:
|
||||||
|
static BOOL EndsWith(const std::string &a, const std::string &b);
|
||||||
|
|
||||||
|
static const char *byte_to_binary(int32_t x);
|
||||||
|
|
||||||
|
static std::string removeCharFromString(std::string &input, char toBeRemoved);
|
||||||
|
|
||||||
|
static const char *fmt(const char *format, ...);
|
||||||
|
|
||||||
|
static const wchar_t *wfmt(const char *format, ...);
|
||||||
|
|
||||||
|
static int32_t strprintf(std::string &str, const char *format, ...);
|
||||||
|
|
||||||
|
static std::string strfmt(const char *format, ...);
|
||||||
|
|
||||||
|
static BOOL char2wchar_t(const char *src, wchar_t *dest);
|
||||||
|
|
||||||
|
static int32_t strtokcmp(const char *string, const char *compare, const char *separator);
|
||||||
|
|
||||||
|
static int32_t strextcmp(const char *string, const char *extension, char seperator);
|
||||||
|
|
||||||
|
static char *str_replace(char *orig, char *rep, char *with);
|
||||||
|
|
||||||
|
static const char *FullpathToFilename(const char *path);
|
||||||
|
|
||||||
|
static void RemoveDoubleSlashs(std::string &str);
|
||||||
|
|
||||||
|
static std::vector<std::string> stringSplit(const std::string &value, const std::string &splitter);
|
||||||
|
|
||||||
|
static uint32_t hash(char *str);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __STRING_TOOLS_H */
|
||||||
|
|
33
src/utils/logger.h
Normal file
33
src/utils/logger.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <whb/log.h>
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||||
|
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||||
|
|
||||||
|
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
|
||||||
|
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \
|
||||||
|
OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) DEBUG_FUNCTION_LINE(FMT, ##ARGS)
|
||||||
|
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...)
|
||||||
|
|
||||||
|
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \
|
||||||
|
WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
69
src/utils/utils.c
Normal file
69
src/utils/utils.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <whb/log.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <coreinit/debug.h>
|
||||||
|
#include "utils/logger.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); ", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL OSFatal_printf(const char *fmt, ...) {
|
||||||
|
char *buf1 = memalign(4,PRINTF_BUFFER_LENGTH);
|
||||||
|
char *buf2 = memalign(4,PRINTF_BUFFER_LENGTH);
|
||||||
|
va_list va;
|
||||||
|
|
||||||
|
if (!buf1) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buf2) {
|
||||||
|
free(buf1);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(va, fmt);
|
||||||
|
|
||||||
|
vsnprintf(buf1, PRINTF_BUFFER_LENGTH, fmt, va);
|
||||||
|
snprintf(buf2, PRINTF_BUFFER_LENGTH, "%s\n", buf1);
|
||||||
|
OSFatal(buf2);
|
||||||
|
|
||||||
|
free(buf1);
|
||||||
|
free(buf2);
|
||||||
|
va_end(va);
|
||||||
|
return TRUE;
|
||||||
|
}
|
38
src/utils/utils.h
Normal file
38
src/utils/utils.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LIMIT(x, min, max) \
|
||||||
|
({ \
|
||||||
|
typeof( x ) _x = x; \
|
||||||
|
typeof( min ) _min = min; \
|
||||||
|
typeof( max ) _max = max; \
|
||||||
|
( ( ( _x ) < ( _min ) ) ? ( _min ) : ( ( _x ) > ( _max ) ) ? ( _max) : ( _x ) ); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define DegToRad(a) ( (a) * 0.01745329252f )
|
||||||
|
#define RadToDeg(a) ( (a) * 57.29577951f )
|
||||||
|
|
||||||
|
#define ALIGN4(x) (((x) + 3) & ~3)
|
||||||
|
#define ALIGN32(x) (((x) + 31) & ~31)
|
||||||
|
|
||||||
|
// those work only in powers of 2
|
||||||
|
#define ROUNDDOWN(val, align) ((val) & ~(align-1))
|
||||||
|
#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align-1)), align)
|
||||||
|
|
||||||
|
|
||||||
|
#define le16(i) ((((uint16_t) ((i) & 0xFF)) << 8) | ((uint16_t) (((i) & 0xFF00) >> 8)))
|
||||||
|
#define le32(i) ((((uint32_t)le16((i) & 0xFFFF)) << 16) | ((uint32_t)le16(((i) & 0xFFFF0000) >> 16)))
|
||||||
|
#define le64(i) ((((uint64_t)le32((i) & 0xFFFFFFFFLL)) << 32) | ((uint64_t)le32(((i) & 0xFFFFFFFF00000000LL) >> 32)))
|
||||||
|
|
||||||
|
//Needs to have log_init() called beforehand.
|
||||||
|
void dumpHex(const void *data, size_t size);
|
||||||
|
BOOL OSFatal_printf(const char *fmt, ...);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user