commit 4551e087459ce5168f2810562bd24329f2da8ce2 Author: Maschell Date: Fri Jan 1 01:39:28 2021 +0100 first commit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..aef051a --- /dev/null +++ b/.github/workflows/ci.yml @@ -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 \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..0413d45 --- /dev/null +++ b/.github/workflows/pr.yml @@ -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" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba57c28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.cbp +*.elf +*.layout +*.rpx +build/ +*.save-failed +.idea/ +cmake-build-debug/ +CMakeLists.txt +*.wms diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..77f4982 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM wiiuenv/devkitppc:20200810 + +COPY --from=wiiuenv/libfunctionpatcher:20200812 /artifacts $DEVKITPRO +COPY --from=wiiuenv/wiiumodulesystem:20200812 /artifacts $DEVKITPRO + +WORKDIR project \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e4aa0d --- /dev/null +++ b/Makefile @@ -0,0 +1,143 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/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 +#------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..99664d1 --- /dev/null +++ b/README.md @@ -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 +``` diff --git a/src/FSDirReplacements.cpp b/src/FSDirReplacements.cpp new file mode 100644 index 0000000..f8a61bf --- /dev/null +++ b/src/FSDirReplacements.cpp @@ -0,0 +1,154 @@ +#include "FSDirReplacements.h" +#include +#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); \ No newline at end of file diff --git a/src/FSDirReplacements.h b/src/FSDirReplacements.h new file mode 100644 index 0000000..81c518f --- /dev/null +++ b/src/FSDirReplacements.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#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 + diff --git a/src/FSFileReplacements.cpp b/src/FSFileReplacements.cpp new file mode 100644 index 0000000..30d8045 --- /dev/null +++ b/src/FSFileReplacements.cpp @@ -0,0 +1,404 @@ +#include "FSFileReplacements.h" +#include "globals.h" + +#include +#include +#include +#include +#include + +#include +#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); \ No newline at end of file diff --git a/src/FSFileReplacements.h b/src/FSFileReplacements.h new file mode 100644 index 0000000..da7ccf8 --- /dev/null +++ b/src/FSFileReplacements.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#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 + diff --git a/src/FSWrapper.cpp b/src/FSWrapper.cpp new file mode 100644 index 0000000..fd5effc --- /dev/null +++ b/src/FSWrapper.cpp @@ -0,0 +1,779 @@ +#include "FSWrapper.h" +#include +#include +#include +#include +#include +#include +#include +#include +#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 fallback_function, + std::function 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 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 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 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 fallback_function, + std::function 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 fallback_function, + std::function 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 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 fallback_function, + std::function 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 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 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 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 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 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 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 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 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 fallback_function, + std::function 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 fallback_function, + std::function 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 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); +} \ No newline at end of file diff --git a/src/FSWrapper.h b/src/FSWrapper.h new file mode 100644 index 0000000..fdc84f0 --- /dev/null +++ b/src/FSWrapper.h @@ -0,0 +1,147 @@ +#pragma once + +#include +#include +#include +#include +#include + +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 fallback_function, + std::function result_handler); + +FSStatus FSReadDirWrapper(FSDirectoryHandle handle, + FSDirectoryEntry *entry, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSCloseDirWrapper(FSDirectoryHandle handle, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSRewindDirWrapper(FSDirectoryHandle handle, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSMakeDirWrapper(char *path, + FSErrorFlag errorMask, + std::function fallback_function, + std::function result_handler); + +FSStatus FSOpenFileWrapper(char *path, + const char *mode, + FSFileHandle *handle, + FSErrorFlag errorMask, + std::function fallback_function, + std::function result_handler); + +FSStatus FSCloseFileWrapper(FSFileHandle handle, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSGetStatWrapper(char *path, + FSStat *stats, + FSErrorFlag errorMask, + std::function fallback_function, + std::function result_handler); + +FSStatus FSGetStatFileWrapper(FSFileHandle handle, + FSStat *stats, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSReadFileWrapper(void *buffer, + uint32_t size, + uint32_t count, + FSFileHandle handle, + uint32_t unk1, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSReadFileWithPosWrapper(void *buffer, + uint32_t size, + uint32_t count, + uint32_t pos, + FSFileHandle handle, + int32_t unk1, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSSetPosFileWrapper(FSFileHandle handle, + uint32_t pos, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSGetPosFileWrapper(FSFileHandle handle, + uint32_t *pos, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSIsEofWrapper(FSFileHandle handle, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSTruncateFileWrapper(FSFileHandle handle, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSWriteFileWrapper(uint8_t *buffer, + uint32_t size, + uint32_t count, + FSFileHandle handle, + uint32_t unk1, + FSErrorFlag errorMask, + std::function result_handler); + +FSStatus FSRemoveWrapper(char *path, + FSErrorFlag errorMask, + std::function fallback_function, + std::function result_handler); + +FSStatus FSRenameWrapper(char *oldPath, + char *newPath, + FSErrorFlag errorMask, + std::function fallback_function, + std::function result_handler); + +FSStatus FSFlushFileWrapper(FSFileHandle handle, + FSErrorFlag errorMask, + std::function result_handler); + +int32_t getNewDirHandleIndex(); + +int32_t getNewFileHandleIndex(); + +bool isValidDirHandle(int32_t handle); + +bool isValidFileHandle(int32_t handle); \ No newline at end of file diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp new file mode 100644 index 0000000..cccefec --- /dev/null +++ b/src/FileUtils.cpp @@ -0,0 +1,199 @@ +#include "FileUtils.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/src/FileUtils.h b/src/FileUtils.h new file mode 100644 index 0000000..a93edb9 --- /dev/null +++ b/src/FileUtils.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + + +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); \ No newline at end of file diff --git a/src/RPXLoading.cpp b/src/RPXLoading.cpp new file mode 100644 index 0000000..3e43b5f --- /dev/null +++ b/src/RPXLoading.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#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); \ No newline at end of file diff --git a/src/RPXLoading.h b/src/RPXLoading.h new file mode 100644 index 0000000..baea5be --- /dev/null +++ b/src/RPXLoading.h @@ -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; + diff --git a/src/globals.cpp b/src/globals.cpp new file mode 100644 index 0000000..fa68ffe --- /dev/null +++ b/src/globals.cpp @@ -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"))); \ No newline at end of file diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 0000000..e563bac --- /dev/null +++ b/src/globals.h @@ -0,0 +1,10 @@ +#include +#include + +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; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..bcace97 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include "utils/logger.h" +#include "utils/StringTools.h" +#include "FSFileReplacements.h" +#include "globals.h" +#include "FileUtils.h" +#include "FSDirReplacements.h" + +#include + +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; + } + } + } +} + diff --git a/src/utils/OnLeavingScope.h b/src/utils/OnLeavingScope.h new file mode 100644 index 0000000..2043893 --- /dev/null +++ b/src/utils/OnLeavingScope.h @@ -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 +#include + + +/** + * 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 +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 +OnLeavingScope::type> onLeavingScope(Func&& f) +{ + return OnLeavingScope::type>(std::forward(f)); +} + +#endif // CRASCIT_ONLEAVINGSCOPE_H diff --git a/src/utils/StringTools.cpp b/src/utils/StringTools.cpp new file mode 100644 index 0000000..739de30 --- /dev/null +++ b/src/utils/StringTools.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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 StringTools::stringSplit(const std::string &inValue, const std::string &splitter) { + std::string value = inValue; + std::vector 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; +} + diff --git a/src/utils/StringTools.h b/src/utils/StringTools.h new file mode 100644 index 0000000..8fa3319 --- /dev/null +++ b/src/utils/StringTools.h @@ -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 +#include +#include + +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 stringSplit(const std::string &value, const std::string &splitter); + + static uint32_t hash(char *str); +}; + +#endif /* __STRING_TOOLS_H */ + diff --git a/src/utils/logger.h b/src/utils/logger.h new file mode 100644 index 0000000..5fc6b17 --- /dev/null +++ b/src/utils/logger.h @@ -0,0 +1,33 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#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 diff --git a/src/utils/utils.c b/src/utils/utils.c new file mode 100644 index 0000000..d170c6d --- /dev/null +++ b/src/utils/utils.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/src/utils/utils.h b/src/utils/utils.h new file mode 100644 index 0000000..a9f0b69 --- /dev/null +++ b/src/utils/utils.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#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 \ No newline at end of file