first commit

This commit is contained in:
Maschell 2021-01-01 01:39:28 +01:00
commit 4551e08745
25 changed files with 2774 additions and 0 deletions

58
.github/workflows/ci.yml vendored Normal file
View 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
View 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
View File

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

6
Dockerfile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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, &copy[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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

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