Rewrite to use libcontentredirection and libwuhbutils.

This commit is contained in:
Maschell 2022-03-12 21:21:21 +01:00
parent 533c25d6bf
commit ebe901ebdc
24 changed files with 367 additions and 2767 deletions

View File

@ -1,8 +1,10 @@
FROM wiiuenv/devkitppc:20220303
FROM wiiuenv/devkitppc:20220417
COPY --from=wiiuenv/librpxloader:20220212 /artifacts $DEVKITPRO
COPY --from=wiiuenv/librpxloader:20220417 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libfunctionpatcher:20220211 /artifacts $DEVKITPRO
COPY --from=wiiuenv/wiiumodulesystem:20220204 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libromfs_wiiu:20220305 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libwuhbutils:20220415 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libcontentredirection:20220414 /artifacts $DEVKITPRO
COPY --from=wiiuenv/libromfs_wiiu:20220414 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -34,17 +34,17 @@ CFLAGS := -Wall -Wextra -Os -ffunction-sections\
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
CXXFLAGS := $(CFLAGS) -std=c++17
CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld $(WUMSSPECS)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld -Tcontentredirection.ld -Twuhbutils.ld $(WUMSSPECS)
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -g
CFLAGS += -DDEBUG -g
endif
LIBS := -lwums -lwut -lfunctionpatcher -lromfs -lz
LIBS := -lwums -lwut -lfunctionpatcher -lcontentredirection -lwuhbutils -lromfs
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
@ -71,6 +71,7 @@ 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)))
DEFFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.def)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#-------------------------------------------------------------------------------
@ -88,7 +89,7 @@ endif
#-------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES_SRC := $(DEFFILES:.def=.o) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
@ -128,11 +129,18 @@ $(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
#-------------------------------------------------------------------------------
%.o: %.def
$(SILENTMSG) $(notdir $<)
$(SILENTCMD)rplimportgen $< $*.s $*.ld $(ERROR_FILTER)
$(SILENTCMD)$(CC) -x assembler-with-cpp $(ASFLAGS) -c $*.s -o $@ $(ERROR_FILTER)
#---------------------------------------------------------------------------------
%_bin.h %.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)

View File

@ -1,194 +0,0 @@
#include "FSDirReplacements.h"
#include "FSWrapper.h"
#include "FileUtils.h"
#include "globals.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <coreinit/filesystem.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);
FSErrorFlag realErrorMask = errorMask;
// Even real_FSOpenDir is still calling our FSOpenDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
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;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSOpenDirAsync(client, block, path, handle, realErrorMask, 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();
FSErrorFlag realErrorMask = errorMask;
// Even real_FSReadDir is still calling our FSReadDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
FSStatus result = FSReadDirWrapper(handle, entry, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSReadDirAsync(client, block, handle, entry, realErrorMask, 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();
FSErrorFlag realErrorMask = errorMask;
// Even real_FSCloseDir is still calling our FSCloseDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
FSStatus result = FSCloseDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSCloseDirAsync(client, block, handle, realErrorMask, 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();
FSErrorFlag realErrorMask = errorMask;
// Even real_FSRewindDir is still calling our FSRewindDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
FSStatus result = FSRewindDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSRewindDirAsync(client, block, handle, realErrorMask, 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);
}
DECL_FUNCTION(FSStatus, FSChangeDirAsync, FSClient *client, FSCmdBlock *block, const char *path, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("FSChangeDirAsync %s", path);
snprintf(gReplacementInfo.contentReplacementInfo.workingDir, sizeof(gReplacementInfo.contentReplacementInfo.workingDir), "%s", path);
auto len = strlen(gReplacementInfo.contentReplacementInfo.workingDir);
if (len > 0 && gReplacementInfo.contentReplacementInfo.workingDir[len - 1] != '/') {
gReplacementInfo.contentReplacementInfo.workingDir[len - 1] = '/';
gReplacementInfo.contentReplacementInfo.workingDir[len] = 0;
}
OSMemoryBarrier();
return real_FSChangeDirAsync(client, block, path, errorMask, asyncData);
}
function_replacement_data_t fs_dir_function_replacements[] = {
REPLACE_FUNCTION(FSOpenDir, LIBRARY_COREINIT, FSOpenDir),
REPLACE_FUNCTION(FSOpenDirAsync, LIBRARY_COREINIT, FSOpenDirAsync),
REPLACE_FUNCTION(FSReadDir, LIBRARY_COREINIT, FSReadDir),
REPLACE_FUNCTION(FSReadDirAsync, LIBRARY_COREINIT, FSReadDirAsync),
REPLACE_FUNCTION(FSCloseDir, LIBRARY_COREINIT, FSCloseDir),
REPLACE_FUNCTION(FSCloseDirAsync, LIBRARY_COREINIT, FSCloseDirAsync),
REPLACE_FUNCTION(FSRewindDir, LIBRARY_COREINIT, FSRewindDir),
REPLACE_FUNCTION(FSRewindDirAsync, LIBRARY_COREINIT, FSRewindDirAsync),
REPLACE_FUNCTION(FSMakeDir, LIBRARY_COREINIT, FSMakeDir),
REPLACE_FUNCTION(FSMakeDirAsync, LIBRARY_COREINIT, FSMakeDirAsync),
REPLACE_FUNCTION(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync),
};
uint32_t fs_dir_function_replacements_size = sizeof(fs_dir_function_replacements) / sizeof(function_replacement_data_t);

View File

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

View File

@ -1,399 +0,0 @@
#include "FSFileReplacements.h"
#include "globals.h"
#include <coreinit/dynload.h>
#include <cstring>
#include "FSWrapper.h"
#include "FileUtils.h"
#include "utils/logger.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);
}
function_replacement_data_t fs_file_function_replacements[] = {
REPLACE_FUNCTION(FSOpenFile, LIBRARY_COREINIT, FSOpenFile),
REPLACE_FUNCTION(FSOpenFileAsync, LIBRARY_COREINIT, FSOpenFileAsync),
REPLACE_FUNCTION(FSCloseFile, LIBRARY_COREINIT, FSCloseFile),
REPLACE_FUNCTION(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync),
REPLACE_FUNCTION(FSGetStat, LIBRARY_COREINIT, FSGetStat),
REPLACE_FUNCTION(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync),
REPLACE_FUNCTION(FSGetStatFile, LIBRARY_COREINIT, FSGetStatFile),
REPLACE_FUNCTION(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync),
REPLACE_FUNCTION(FSReadFile, LIBRARY_COREINIT, FSReadFile),
REPLACE_FUNCTION(FSReadFileAsync, LIBRARY_COREINIT, FSReadFileAsync),
REPLACE_FUNCTION(FSReadFileWithPos, LIBRARY_COREINIT, FSReadFileWithPos),
REPLACE_FUNCTION(FSReadFileWithPosAsync, LIBRARY_COREINIT, FSReadFileWithPosAsync),
REPLACE_FUNCTION(FSSetPosFile, LIBRARY_COREINIT, FSSetPosFile),
REPLACE_FUNCTION(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync),
REPLACE_FUNCTION(FSGetPosFile, LIBRARY_COREINIT, FSGetPosFile),
REPLACE_FUNCTION(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync),
REPLACE_FUNCTION(FSIsEof, LIBRARY_COREINIT, FSIsEof),
REPLACE_FUNCTION(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync),
REPLACE_FUNCTION(FSWriteFile, LIBRARY_COREINIT, FSWriteFile),
REPLACE_FUNCTION(FSWriteFileAsync, LIBRARY_COREINIT, FSWriteFileAsync),
REPLACE_FUNCTION(FSTruncateFile, LIBRARY_COREINIT, FSTruncateFile),
REPLACE_FUNCTION(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync),
REPLACE_FUNCTION(FSRemove, LIBRARY_COREINIT, FSRemove),
REPLACE_FUNCTION(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync),
REPLACE_FUNCTION(FSRename, LIBRARY_COREINIT, FSRename),
REPLACE_FUNCTION(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync),
REPLACE_FUNCTION(FSFlushFile, LIBRARY_COREINIT, FSFlushFile),
REPLACE_FUNCTION(FSFlushFileAsync, LIBRARY_COREINIT, FSFlushFileAsync),
//REPLACE_FUNCTION(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync),
//REPLACE_FUNCTION(FSGetFreeSpaceSizeAsync, LIBRARY_COREINIT, FSGetFreeSpaceSizeAsync),
};
uint32_t fs_file_function_replacements_size = sizeof(fs_file_function_replacements) / sizeof(function_replacement_data_t);

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,160 +0,0 @@
#pragma once
#include <coreinit/filesystem.h>
#include <coreinit/mutex.h>
#include <cstdint>
#include <dirent.h>
#include <functional>
typedef struct dirMagic {
uint32_t handle{};
DIR *dir{};
bool in_use{};
char path[256]{};
OSMutex *mutex{};
FSDirectoryEntry *readResult = nullptr;
int readResultCapacity = 0;
int readResultNumberOfEntries = 0;
FSDirectoryHandle realDirHandle = 0;
} dirMagic_t;
typedef struct fileMagic {
uint32_t handle;
bool in_use;
int fd;
OSMutex *mutex;
} fileMagic_t;
#define ERROR_FLAG_MASK (0xFFFF0000)
#define FORCE_REAL_FUNC_MAGIC (0x42420000)
#define FORCE_REAL_FUNC_WITH_FULL_ERRORS (FORCE_REAL_FUNC_MAGIC | 0x0000FFFF)
#define HANDLE_INDICATOR_MASK 0xFFFFFF00
#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,
const std::function<FSStatus(char *)> &fallback_function,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
FSDirectoryEntry *entry,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSCloseDirWrapper(FSDirectoryHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSRewindDirWrapper(FSDirectoryHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSMakeDirWrapper(char *path,
FSErrorFlag errorMask,
const std::function<FSStatus(char *)> &fallback_function,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSOpenFileWrapper(char *path,
const char *mode,
FSFileHandle *handle,
FSErrorFlag errorMask,
const std::function<FSStatus(char *)> &fallback_function,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSCloseFileWrapper(FSFileHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSGetStatWrapper(char *path,
FSStat *stats,
FSErrorFlag errorMask,
const std::function<FSStatus(char *)> &fallback_function,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSGetStatFileWrapper(FSFileHandle handle,
FSStat *stats,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSReadFileWrapper(void *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
uint32_t unk1,
FSErrorFlag errorMask,
const 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,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSSetPosFileWrapper(FSFileHandle handle,
uint32_t pos,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSGetPosFileWrapper(FSFileHandle handle,
uint32_t *pos,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSIsEofWrapper(FSFileHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSTruncateFileWrapper(FSFileHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSWriteFileWrapper(uint8_t *buffer,
uint32_t size,
uint32_t count,
FSFileHandle handle,
uint32_t unk1,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSRemoveWrapper(char *path,
FSErrorFlag errorMask,
const std::function<FSStatus(char *)> &fallback_function,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSRenameWrapper(char *oldPath,
char *newPath,
FSErrorFlag errorMask,
const std::function<FSStatus(char *, char *)> &fallback_function,
const std::function<FSStatus(FSStatus)> &result_handler);
FSStatus FSFlushFileWrapper(FSFileHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler);
int32_t getNewDirHandleIndex();
int32_t getNewFileHandleIndex();
bool isValidDirHandle(int32_t handle);
bool isValidFileHandle(int32_t handle);

View File

@ -1,94 +1,7 @@
#include "FileUtils.h"
#include <mutex>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/thread.h>
#include <dirent.h>
#include <fcntl.h>
#include <map>
extern "C" OSMessageQueue *OSGetDefaultAppIOQueue();
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock) {
if (!cmdBlock) {
return nullptr;
}
auto body = (FSCmdBlockBody *) (ROUNDUP((uint32_t) cmdBlock, 0x40));
return body;
}
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) {
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
FSAsyncResult *result = &(fsCmdBlockGetBody(block)->asyncResult);
//DEBUG_FUNCTION_LINE("Send result %d to ioMsgQueue (%08X)", status, asyncData->ioMsgQueue);
result->asyncData.callback = asyncData->callback;
result->asyncData.param = asyncData->param;
result->asyncData.ioMsgQueue = asyncData->ioMsgQueue;
memset(&result->ioMsg, 0, sizeof(result->ioMsg));
result->ioMsg.data = result;
result->ioMsg.type = OS_FUNCTION_TYPE_FS_CMD_ASYNC;
result->client = client;
result->block = block;
result->status = status;
while (!OSSendMessage(asyncData->ioMsgQueue, (OSMessage *) &(result->ioMsg), OS_MESSAGE_FLAGS_NONE)) {
DEBUG_FUNCTION_LINE("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) {
DEBUG_FUNCTION_LINE("Error: Reading %08X bytes from handle %08X. result %08X errno: %d ", size * count, handle, curResult, errno);
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;
}
#include <sys/stat.h>
int32_t CheckFile(const char *filepath) {
if (!filepath) {
@ -164,93 +77,4 @@ int32_t CreateSubfolder(const char *fullpath) {
}
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:/code/"))) {
romfsUnmount("rcc");
return -2;
}
bool found = false;
int res = -3;
while ((entry = readdir(dir)) != nullptr) {
if (StringTools::EndsWith(entry->d_name, ".rpx")) {
if (romfsGetFileInfoPerPath("rcc", (std::string("code/") + entry->d_name).c_str(), info) >= 0) {
found = true;
res = 0;
} else {
DEBUG_FUNCTION_LINE("Failed to get info for %s", entry->d_name);
}
break;
}
}
closedir(dir);
romfsUnmount("rcc");
if (!found) {
return -4;
}
return res;
}
int32_t LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) {
//! always initialze input
*inbuffer = nullptr;
if (size) {
*size = 0;
}
int32_t iFd = open(filepath, O_RDONLY);
if (iFd < 0) {
return -1;
}
uint32_t filesize = lseek(iFd, 0, SEEK_END);
lseek(iFd, 0, SEEK_SET);
auto *buffer = (uint8_t *) memalign(0x40, ROUNDUP(filesize, 0x40));
if (buffer == nullptr) {
close(iFd);
return -2;
}
uint32_t blocksize = 0x20000;
uint32_t done = 0;
int32_t readBytes;
while (done < filesize) {
if (done + blocksize > filesize) {
blocksize = filesize - done;
}
readBytes = read(iFd, buffer + done, blocksize);
if (readBytes <= 0)
break;
done += readBytes;
}
::close(iFd);
if (done != filesize) {
free(buffer);
buffer = nullptr;
return -3;
}
*inbuffer = buffer;
//! sign is optional input
if (size) {
*size = filesize;
}
return filesize;
}

View File

@ -1,23 +1,9 @@
#pragma once
#include <coreinit/filesystem.h>
#include <functional>
#include <romfs_dev.h>
#include <string>
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);

View File

@ -1,21 +1,30 @@
#include "RPXLoading.h"
#include "FileUtils.h"
#include "globals.h"
#include "utils/FileReader.h"
#include "utils/FileReaderCompressed.h"
#include "utils/StringTools.h"
#include "utils/ini.h"
#include "utils/logger.h"
#include <algorithm>
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/ios.h>
#include <cstring>
#include <dirent.h>
#include <memory>
#include <mutex>
#include <nn/acp/title.h>
#include <romfs_dev.h>
#include <rpxloader.h>
#include <rpxloader/rpxloader.h>
#include <string>
#include <wuhb_utils/utils.h>
std::mutex fileReaderListMutex;
std::vector<FileReader *> openFileReaders;
void RPXLoadingCleanUp() {
const std::lock_guard<std::mutex> lock(fileReaderListMutex);
for (auto &reader : openFileReaders) {
delete reader;
}
openFileReaders.clear();
}
/*
* Patch the meta xml for the home menu
@ -38,36 +47,54 @@ DECL_FUNCTION(int32_t, HBM_NN_ACP_ACPGetTitleMetaXmlByDevice, uint32_t titleid_u
}
DECL_FUNCTION(int, RPX_FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, int *handle, int error) {
const char *iconTex = "iconTex.tga";
if (StringTools::EndsWith(path, iconTex)) {
if (gReplacementInfo.rpxReplacementInfo.isRPXReplaced) {
if (StringTools::EndsWith(path, iconTex)) {
auto *reader = new FileReader(reinterpret_cast<uint8_t *>(gReplacementInfo.rpxReplacementInfo.iconCache), ICON_SIZE);
*handle = (uint32_t) reader;
return FS_STATUS_OK;
}
const char *iconTex = "iconTex.tga";
std::string_view pathView = path;
if (gReplacementInfo.rpxReplacementInfo.isRPXReplaced && pathView.ends_with(iconTex)) {
const std::lock_guard<std::mutex> lock(fileReaderListMutex);
auto *reader = new (std::nothrow) FileReader(reinterpret_cast<uint8_t *>(gReplacementInfo.rpxReplacementInfo.iconCache), ICON_SIZE);
if (!reader) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for the FileReader");
return FS_STATUS_FATAL_ERROR;
}
openFileReaders.push_back(reader);
*handle = reinterpret_cast<int>(reader);
return FS_STATUS_OK;
}
int result = real_RPX_FSOpenFile(client, block, path, mode, handle, error);
return result;
}
DECL_FUNCTION(FSStatus, RPX_FSReadFile, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, uint32_t flags) {
// We check if the handle is part of our heap (the MemoryMapping Module allocates to 0x80000000)
if ((handle & 0xF0000000) == 0x80000000) {
auto reader = (FileReader *) handle;
return (FSStatus) reader->read(buffer, size * count);
if (gReplacementInfo.rpxReplacementInfo.isRPXReplaced) {
const std::lock_guard<std::mutex> lock(fileReaderListMutex);
for (auto &reader : openFileReaders) {
if ((uint32_t) reader == (uint32_t) handle) {
return (FSStatus) (reader->read(buffer, size * count) / size);
}
}
}
FSStatus result = real_RPX_FSReadFile(client, block, buffer, size, count, handle, unk1, flags);
return result;
return real_RPX_FSReadFile(client, block, buffer, size, count, handle, unk1, flags);
}
DECL_FUNCTION(FSStatus, RPX_FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t flags) {
// We check if the handle is part of our heap (the MemoryMapping Module allocates to 0x80000000)
if ((handle & 0xF0000000) == 0x80000000) {
auto reader = (FileReader *) handle;
delete reader;
return FS_STATUS_OK;
if (gReplacementInfo.rpxReplacementInfo.isRPXReplaced) {
const std::lock_guard<std::mutex> lock(fileReaderListMutex);
bool found = false;
int index = 0;
FileReader *reader = nullptr;
for (auto &cur : openFileReaders) {
if ((uint32_t) cur == (uint32_t) handle) {
found = true;
reader = cur;
break;
}
index++;
}
if (found) {
openFileReaders.erase(openFileReaders.begin() + index);
delete reader;
return FS_STATUS_OK;
}
}
return real_RPX_FSCloseFile(client, block, handle, flags);
@ -115,63 +142,56 @@ bool RL_LoadFromSDOnNextLaunch(const char *bundle_path) {
request.filesize = 0; // unknown filesize
request.fileoffset = 0; //
romfs_fileInfo info;
WUHBRPXInfo fileInfo;
bool metaLoaded = false;
std::string completePath = std::string("/vol/external01/") + bundle_path;
int res = getRPXInfoForPath(completePath, &info);
bool isBundle = false;
if (res >= 0) {
isBundle = true;
request.filesize = ((uint32_t *) &info.length)[1];
request.fileoffset = ((uint32_t *) &info.offset)[1];
if (romfsMount("rcc", completePath.c_str(), RomfsSource_FileDescriptor_CafeOS) == 0) {
uint8_t *buffer = nullptr;
uint32_t size = 0;
if (LoadFileToMem("rcc:/meta/meta.ini", &buffer, &size) >= 0 && size > 0) {
buffer[size - 1] = 0;
bool isBundle = false;
auto rpxInfo = WUHBUtils_GetRPXInfo(completePath.c_str(), BundleSource_FileDescriptor_CafeOS, &fileInfo);
if (rpxInfo == WUHB_UTILS_RESULT_SUCCESS) {
isBundle = true;
request.filesize = ((uint32_t *) &fileInfo.length)[1];
request.fileoffset = ((uint32_t *) &fileInfo.offset)[1];
auto res = -1;
if (WUHBUtils_MountBundle("rcc", completePath.c_str(), BundleSource_FileDescriptor_CafeOS, &res) == WUHB_UTILS_RESULT_SUCCESS && res == 0) {
uint8_t *buffer;
uint32_t bufferSize;
if (WUHBUtils_ReadWholeFile("rcc:/meta/meta.ini", &buffer, &bufferSize) == WUHB_UTILS_RESULT_SUCCESS) {
buffer[bufferSize - 1] = '\0';
if (ini_parse_string((const char *) buffer, parseINIhandler, &gReplacementInfo.rpxReplacementInfo.metaInformation) < 0) {
DEBUG_FUNCTION_LINE("Failed to load and parse meta.ini");
DEBUG_FUNCTION_LINE_ERR("Failed to load and parse meta.ini");
} else {
metaLoaded = true;
}
}
if (buffer) {
free(buffer);
buffer = nullptr;
}
FileReader *reader = nullptr;
if (CheckFile("rcc:/meta/iconTex.tga")) {
std::string path = "rcc:/meta/iconTex.tga";
reader = new FileReader(path);
} else if (CheckFile("rcc:/meta/iconTex.tga.gz")) {
std::string path = "rcc:/meta/iconTex.tga.gz";
reader = new FileReaderCompressed(path);
}
if (reader) {
uint32_t alreadyRead = 0;
uint32_t toRead = ICON_SIZE;
do {
int read = reader->read(reinterpret_cast<uint8_t *>(&gReplacementInfo.rpxReplacementInfo.iconCache[alreadyRead]), toRead);
if (read <= 0) {
break;
}
alreadyRead += read;
toRead -= read;
} while (alreadyRead < ICON_SIZE);
delete reader;
} else {
memset(gReplacementInfo.rpxReplacementInfo.iconCache, 0, sizeof(gReplacementInfo.rpxReplacementInfo.iconCache));
DEBUG_FUNCTION_LINE_ERR("Failed to read whole file meta.ini");
}
buffer = nullptr;
bufferSize = 0;
if (WUHBUtils_ReadWholeFile("rcc:/meta/iconTex.tga", &buffer, &bufferSize) == WUHB_UTILS_RESULT_SUCCESS) {
uint32_t cpySize = ICON_SIZE;
if (bufferSize < cpySize) {
cpySize = bufferSize;
memset(gReplacementInfo.rpxReplacementInfo.iconCache, 0, ICON_SIZE);
}
memcpy(gReplacementInfo.rpxReplacementInfo.iconCache, buffer, cpySize);
free(buffer);
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to read iconTex.tga");
}
auto outRes = 0;
if (WUHBUtils_UnmountBundle("rcc", &outRes) != WUHB_UTILS_RESULT_SUCCESS || outRes != WUHB_UTILS_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to unmount bundle");
}
romfsUnmount("rcc");
}
} else {
if (!(gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_WUHB_BUNDLE &&
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted)) {
if (!gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted) {
memset(gReplacementInfo.rpxReplacementInfo.iconCache, 0, sizeof(gReplacementInfo.rpxReplacementInfo.iconCache));
}
}
@ -207,7 +227,7 @@ bool RL_LoadFromSDOnNextLaunch(const char *bundle_path) {
if (!success) {
gReplacementInfo.rpxReplacementInfo.willRPXBeReplaced = false;
DEBUG_FUNCTION_LINE("Failed to load %s on next restart", request.path);
DEBUG_FUNCTION_LINE_ERR("Failed to load %s on next restart", request.path);
return false;
} else {
gReplacementInfo.rpxReplacementInfo.willRPXBeReplaced = true;
@ -215,27 +235,21 @@ bool RL_LoadFromSDOnNextLaunch(const char *bundle_path) {
DEBUG_FUNCTION_LINE("Launch %s on next restart [size: %08X offset: %08X]", request.path, request.filesize, request.fileoffset);
gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath[0] = '\0';
if (isBundle) {
DEBUG_FUNCTION_LINE("Loaded file is a .wuhb bundle");
gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath[0] = '\0';
DEBUG_FUNCTION_LINE_VERBOSE("Loaded file is a .wuhb bundle");
strncat(gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath,
completePath.c_str(),
sizeof(gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath) - 1);
gReplacementInfo.contentReplacementInfo.mode = CONTENTREDIRECT_FROM_WUHB_BUNDLE;
gReplacementInfo.contentReplacementInfo.replaceSave = true;
} else {
DEBUG_FUNCTION_LINE("Loaded file is no bundle");
DEBUG_FUNCTION_LINE_VERBOSE("Loaded file is no bundle");
gReplacementInfo.rpxReplacementInfo.willRPXBeReplaced = true;
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_WUHB_BUNDLE &&
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted) {
if (gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted) {
// keep the old /vol/content mounted, this way you can reload just the rpx via wiiload
gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath[0] = '\0';
strncat(gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath,
gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath,
sizeof(gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath) - 2);
} else {
gReplacementInfo.contentReplacementInfo.replaceSave = false;
}
}
@ -244,99 +258,89 @@ bool RL_LoadFromSDOnNextLaunch(const char *bundle_path) {
return true;
}
int32_t RL_MountBundle(const char *name, const char *path, BundleSource source) {
return romfsMount(name, path, (RomfsSource) source);
}
std::mutex mutex;
int32_t RL_UnmountBundle(const char *name) {
return romfsUnmount(name);
}
bool RL_UnmountCurrentRunningBundle() {
if (gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted == false) {
return true;
}
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_WUHB_BUNDLE) {
if (gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted) {
DEBUG_FUNCTION_LINE("Unmount /vol/content");
romfsUnmount("rom");
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = false;
OSMemoryBarrier();
return true;
bool RL_DisableContentRedirection() {
std::lock_guard<std::mutex> lock(mutex);
if (contentLayerHandle != 0) {
auto res = ContentRedirection_SetActive(contentLayerHandle, false);
if (res != CONTENT_REDIRECTION_RESULT_SUCCESS) {
return false;
}
}
return false;
}
int32_t RL_FileOpen(const char *name, uint32_t *handle) {
if (handle == nullptr) {
return -1;
if (saveLayerHandle != 0) {
auto res = ContentRedirection_SetActive(saveLayerHandle, false);
if (res != CONTENT_REDIRECTION_RESULT_SUCCESS) {
ContentRedirection_SetActive(contentLayerHandle, true);
return false;
}
}
FileReader *reader;
std::string path = std::string(name);
std::string pathGZ = path + ".gz";
if (CheckFile(path.c_str())) {
reader = new FileReader(path);
} else if (CheckFile(pathGZ.c_str())) {
reader = new FileReaderCompressed(pathGZ);
} else {
return -2;
}
if (reader == nullptr) {
return -3;
}
*handle = (uint32_t) reader;
return 0;
}
int32_t RL_FileRead(uint32_t handle, uint8_t *buffer, uint32_t size) {
auto reader = (FileReader *) handle;
return reader->read(buffer, size);
}
int32_t RL_FileClose(uint32_t handle) {
auto reader = (FileReader *) handle;
delete reader;
return 0;
}
bool RL_FileExists(const char *name) {
std::string checkgz = std::string(name) + ".gz";
return CheckFile(name) || CheckFile(checkgz.c_str());
}
bool RL_RedirectContentWithFallback(const char *newContentPath) {
auto dirHandle = opendir(newContentPath);
if (dirHandle == nullptr) {
return false;
}
closedir(dirHandle);
gReplacementInfo.contentReplacementInfo.replacementPath[0] = '\0';
strncat(gReplacementInfo.contentReplacementInfo.replacementPath, newContentPath, sizeof(gReplacementInfo.contentReplacementInfo.replacementPath) - 1);
gReplacementInfo.contentReplacementInfo.mode = CONTENTREDIRECT_FROM_PATH;
gReplacementInfo.contentReplacementInfo.fallbackOnError = true;
gReplacementInfo.contentReplacementInfo.replaceSave = false;
return true;
}
bool RL_DisableContentRedirection() {
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
gReplacementInfo.contentReplacementInfo.mode = CONTENTREDIRECT_NONE;
return true;
bool RL_EnableContentRedirection() {
std::lock_guard<std::mutex> lock(mutex);
if (contentLayerHandle != 0) {
auto res = ContentRedirection_SetActive(contentLayerHandle, true);
if (res != CONTENT_REDIRECTION_RESULT_SUCCESS) {
return false;
}
}
return false;
if (saveLayerHandle != 0) {
auto res = ContentRedirection_SetActive(saveLayerHandle, true);
if (res != CONTENT_REDIRECTION_RESULT_SUCCESS) {
ContentRedirection_SetActive(contentLayerHandle, false);
return false;
}
}
return true;
}
bool RL_UnmountCurrentRunningBundle() {
std::lock_guard<std::mutex> lock(mutex);
if (gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted == false) {
return true;
}
int outRes = -1;
if (ContentRedirection_RemoveDevice(WUHB_ROMFS_NAME, &outRes) == CONTENT_REDIRECTION_RESULT_SUCCESS) {
if (outRes < 0) {
DEBUG_FUNCTION_LINE_ERR("RemoveDevice \"%s\" failed for ContentRedirection Module", WUHB_ROMFS_NAME);
OSFatal("RL_UnmountCurrentRunningBundle: RemoveDevice \"" WUHB_ROMFS_NAME "\" failed for ContentRedirection Module");
}
} else {
DEBUG_FUNCTION_LINE_ERR("ContentRedirection_RemoveDevice failed");
OSFatal("RL_UnmountCurrentRunningBundle: ContentRedirection_RemoveDevice failed");
}
bool res = true;
if (contentLayerHandle != 0) {
if (ContentRedirection_RemoveFSLayer(contentLayerHandle) != CONTENT_REDIRECTION_RESULT_SUCCESS) {
res = false;
}
contentLayerHandle = 0;
}
if (saveLayerHandle != 0) {
if (ContentRedirection_RemoveFSLayer(saveLayerHandle) != CONTENT_REDIRECTION_RESULT_SUCCESS) {
res = false;
}
saveLayerHandle = 0;
}
romfsUnmount(WUHB_ROMFS_NAME);
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = false;
OSMemoryBarrier();
return res;
}
RPXLoaderVersion RL_GetVersion() {
return RPX_LOADER_MODULE_VERSION;
}
WUMS_EXPORT_FUNCTION(RL_GetVersion);
WUMS_EXPORT_FUNCTION(RL_LoadFromSDOnNextLaunch);
WUMS_EXPORT_FUNCTION(RL_MountBundle);
WUMS_EXPORT_FUNCTION(RL_UnmountBundle);
WUMS_EXPORT_FUNCTION(RL_FileOpen);
WUMS_EXPORT_FUNCTION(RL_FileRead);
WUMS_EXPORT_FUNCTION(RL_FileClose);
WUMS_EXPORT_FUNCTION(RL_FileExists);
WUMS_EXPORT_FUNCTION(RL_RedirectContentWithFallback);
WUMS_EXPORT_FUNCTION(RL_EnableContentRedirection);
WUMS_EXPORT_FUNCTION(RL_DisableContentRedirection);
WUMS_EXPORT_FUNCTION(RL_UnmountCurrentRunningBundle);

View File

@ -18,6 +18,9 @@ typedef struct __attribute((packed)) {
extern function_replacement_data_t rpx_utils_function_replacements[];
extern uint32_t rpx_utils_function_replacements_size;
void RPXLoadingCleanUp();
bool RL_UnmountCurrentRunningBundle();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,4 @@
:NAME homebrew_content_redirection
:TEXT
CRGetVersion

View File

@ -1,7 +1,5 @@
#include "globals.h"
#include <coreinit/filesystem.h>
RPXLoader_ReplacementInformation gReplacementInfo __attribute__((section(".data")));
FSClient *gFSClient __attribute__((section(".data"))) = nullptr;
FSCmdBlock *gFSCmd __attribute__((section(".data"))) = nullptr;
CRLayerHandle contentLayerHandle __attribute__((section(".data"))) = 0;
CRLayerHandle saveLayerHandle __attribute__((section(".data"))) = 0;

View File

@ -1,9 +1,14 @@
#include "utils/utils.h"
#include <content_redirection/redirection.h>
#include <coreinit/filesystem.h>
#include <coreinit/mutex.h>
#include <wums.h>
#include <wut.h>
#define WUHB_ROMFS_NAME "wuhbrom"
#define WUHB_ROMFS_PATH WUHB_ROMFS_NAME ":"
#define WUHB_ROMFS_CONTENT_PATH WUHB_ROMFS_PATH "/content"
typedef struct WUT_PACKED MetaInformation_t {
char shortname[64];
char longname[64];
@ -11,7 +16,7 @@ typedef struct WUT_PACKED MetaInformation_t {
} MetaInformation;
WUT_CHECK_SIZE(MetaInformation_t, 0xC0);
typedef struct BundleMountInformation_t {
typedef struct ContentRedirectionInformation_t {
bool isMounted;
char toMountPath[255];
char mountedPath[255];
@ -29,35 +34,22 @@ typedef struct WUT_PACKED RPXReplacementInfo_t {
// make sure the iconCache is aligned to 0x40
WUT_CHECK_OFFSET(RPXReplacementInfo, 0x100, iconCache);
typedef enum ContentRedirect_Mode {
CONTENTREDIRECT_NONE,
CONTENTREDIRECT_FROM_WUHB_BUNDLE,
CONTENTREDIRECT_FROM_PATH,
} ContentRedirect_Mode;
typedef struct ContentReplacementInfo_t {
ContentRedirect_Mode mode;
BundleMountInformation bundleMountInformation;
char workingDir[255];
char replacementPath[255];
bool replaceSave;
char savePath[255];
bool fallbackOnError;
char replacementPath[0x280];
} ContentReplacementInfo;
typedef struct ContentReplacementWithFallback_t {
char replacementPath[0x280];
} ContentReplacementWithFallback;
typedef struct RPXLoader_ReplacementInformation_t {
RPXReplacementInfo rpxReplacementInfo;
ContentReplacementInfo contentReplacementInfo;
ContentReplacementWithFallback contentReplacementWithFallbackInfo;
} RPXLoader_ReplacementInformation;
extern RPXLoader_ReplacementInformation gReplacementInfo;
extern FSClient *gFSClient;
extern FSCmdBlock *gFSCmd;
extern CRLayerHandle contentLayerHandle;
extern CRLayerHandle saveLayerHandle;

View File

@ -1,60 +1,62 @@
#include <cstring>
#include <malloc.h>
#include <wums.h>
#include "FSDirReplacements.h"
#include "FSFileReplacements.h"
#include "FileUtils.h"
#include "RPXLoading.h"
#include "globals.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include <content_redirection/redirection.h>
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/title.h>
#include <string>
#include <sysapp/title.h>
#include <coreinit/cache.h>
#include <nn/act.h>
#include <romfs_dev.h>
#include <string>
#include <sysapp/title.h>
#include <wuhb_utils/utils.h>
#include <wums.h>
WUMS_MODULE_EXPORT_NAME("homebrew_rpx_loader");
WUMS_USE_WUT_DEVOPTAB();
extern "C" bool CRGetVersion();
extern "C" bool WUU_GetVersion();
WUMS_INITIALIZE() {
initLogging();
DEBUG_FUNCTION_LINE("Patch functions");
// we only patch static functions, we don't need re-patch them at every launch
FunctionPatcherPatchFunction(fs_file_function_replacements, fs_file_function_replacements_size);
FunctionPatcherPatchFunction(fs_dir_function_replacements, fs_dir_function_replacements_size);
FunctionPatcherPatchFunction(rpx_utils_function_replacements, rpx_utils_function_replacements_size);
DEBUG_FUNCTION_LINE("Patch functions finished");
gReplacementInfo = {};
// Call this function to make sure the Content Redirection will be loaded before this module is module.
CRGetVersion();
// Call this function to make sure the WUHBUtils will be loaded before this module is module.
WUU_GetVersion();
// But then use libcontentredirection instead.
ContentRedirectionStatus error;
if ((error = ContentRedirection_Init()) != CONTENT_REDIRECTION_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to init ContentRedirection. Error %d", error);
OSFatal("Failed to init ContentRedirection.");
}
// But then use libwuhbutils instead.
WUHBUtilsStatus error2;
if ((error2 = WUHBUtils_Init()) != WUHB_UTILS_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("RPXLoadingModule: Failed to init WUHBUtils. Error %d", error2);
OSFatal("Failed to init WUHBUtils.");
}
deinitLogging();
}
WUMS_APPLICATION_ENDS() {
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
gReplacementInfo.contentReplacementInfo.mode = CONTENTREDIRECT_NONE;
}
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_WUHB_BUNDLE) {
if (gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted) {
DEBUG_FUNCTION_LINE("Unmount /vol/content");
romfsUnmount("rom");
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = false;
OSMemoryBarrier();
}
}
RL_UnmountCurrentRunningBundle();
gReplacementInfo.rpxReplacementInfo.isRPXReplaced = false;
if (gFSClient) {
FSDelClient(gFSClient, FS_ERROR_FLAG_ALL);
free(gFSClient);
gFSClient = nullptr;
}
free(gFSCmd);
gFSCmd = nullptr;
RPXLoadingCleanUp();
deinitLogging();
}
@ -70,74 +72,75 @@ WUMS_APPLICATION_STARTS() {
gReplacementInfo.rpxReplacementInfo.isRPXReplaced = true;
}
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto fsClient = (FSClient *) memalign(0x20, sizeof(FSClient));
auto fsCmd = (FSCmdBlock *) memalign(0x20, sizeof(FSCmdBlock));
if (_SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_HEALTH_AND_SAFETY) == OSGetTitleID() &&
strlen(gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath) > 0) {
uint32_t currentHash = StringTools::hash(gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath);
if (fsClient == nullptr || fsCmd == nullptr) {
DEBUG_FUNCTION_LINE("Failed to alloc memory for fsclient or fsCmd");
free(fsClient);
free(fsCmd);
} else {
auto rc = FSAddClient(fsClient, FS_ERROR_FLAG_ALL);
if (rc < 0) {
DEBUG_FUNCTION_LINE("Failed to add FSClient");
} else {
FSInitCmdBlock(fsCmd);
gFSClient = fsClient;
gFSCmd = fsCmd;
}
}
return;
}
nn::act::Initialize();
nn::act::PersistentId persistentId = nn::act::GetPersistentId();
nn::act::Finalize();
if (_SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_HEALTH_AND_SAFETY) != OSGetTitleID()) {
DEBUG_FUNCTION_LINE("Set mode to CONTENTREDIRECT_NONE and replaceSave to false");
gReplacementInfo.contentReplacementInfo.mode = CONTENTREDIRECT_NONE;
gReplacementInfo.contentReplacementInfo.replaceSave = false;
OSMemoryBarrier();
} else {
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_WUHB_BUNDLE) {
uint32_t currentHash = StringTools::hash(gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath);
std::string basePath = string_format("/vol/external01/wiiu/apps/save/%08X", currentHash);
std::string common = string_format("fs:/vol/external01/wiiu/apps/save/%08X/common", currentHash);
std::string user = string_format("fs:/vol/external01/wiiu/apps/save/%08X/%08X", currentHash, 0x80000000 | persistentId);
nn::act::Initialize();
nn::act::PersistentId persistentId = nn::act::GetPersistentId();
nn::act::Finalize();
CreateSubfolder(common.c_str());
CreateSubfolder(user.c_str());
DEBUG_FUNCTION_LINE("Created %s and %s", common.c_str(), user.c_str());
std::string basePath = StringTools::strfmt("/vol/external01/wiiu/apps/save/%08X", currentHash);
std::string common = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/common", currentHash);
std::string user = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/%08X", currentHash, 0x80000000 | persistentId);
gReplacementInfo.contentReplacementInfo.savePath[0] = '\0';
strncat(gReplacementInfo.contentReplacementInfo.savePath,
basePath.c_str(),
sizeof(gReplacementInfo.contentReplacementInfo.savePath) - 1);
memset(gReplacementInfo.contentReplacementInfo.workingDir, 0, sizeof(gReplacementInfo.contentReplacementInfo.workingDir));
CreateSubfolder(common.c_str());
CreateSubfolder(user.c_str());
DEBUG_FUNCTION_LINE("Created %s and %s", common.c_str(), user.c_str());
if (romfsMount("rom", gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath, RomfsSource_FileDescriptor_CafeOS) == 0) {
gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath[0] = '\0';
strncat(gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath,
gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath,
sizeof(gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath) - 1);
gReplacementInfo.contentReplacementInfo.replacementPath[0] = '\0';
strncat(gReplacementInfo.contentReplacementInfo.replacementPath,
"rom:/content",
sizeof(gReplacementInfo.contentReplacementInfo.replacementPath) - 1);
DEBUG_FUNCTION_LINE("Mounted %s to /vol/content", gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath);
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = true;
} else {
DEBUG_FUNCTION_LINE("Failed to mount %s", gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath);
if (romfsMount(WUHB_ROMFS_NAME, gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath, RomfsSource_FileDescriptor_CafeOS) == 0) {
auto device = GetDeviceOpTab(WUHB_ROMFS_PATH);
if (device == nullptr || strcmp(device->name, WUHB_ROMFS_NAME) != 0) {
romfsUnmount(WUHB_ROMFS_NAME);
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = false;
DEBUG_FUNCTION_LINE_ERR("DeviceOpTab for %s not found.", WUHB_ROMFS_PATH);
return;
}
int outRes = -1;
if (ContentRedirection_AddDevice(device, &outRes) != CONTENT_REDIRECTION_RESULT_SUCCESS || outRes < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to AddDevice to ContentRedirection");
romfsUnmount(WUHB_ROMFS_NAME);
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = false;
return;
}
auto res = ContentRedirection_AddFSLayer(&contentLayerHandle,
"WUHB Content",
WUHB_ROMFS_CONTENT_PATH,
FS_LAYER_TYPE_CONTENT_REPLACE);
if (res == CONTENT_REDIRECTION_RESULT_SUCCESS) {
res = ContentRedirection_AddFSLayer(&saveLayerHandle,
"WUHB Save",
basePath.c_str(),
FS_LAYER_TYPE_SAVE_REPLACE);
if (res == CONTENT_REDIRECTION_RESULT_SUCCESS) {
gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath[0] = '\0';
strncpy(gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath,
gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath,
sizeof(gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath));
DEBUG_FUNCTION_LINE_VERBOSE("Mounted %s to /vol/content", gReplacementInfo.contentReplacementInfo.bundleMountInformation.mountedPath);
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = true;
gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath[0] = '\0';
OSMemoryBarrier();
return;
} else {
if (contentLayerHandle != 0) {
ContentRedirection_RemoveFSLayer(contentLayerHandle);
contentLayerHandle = 0;
}
DEBUG_FUNCTION_LINE_ERR("ContentRedirection_AddFSLayer had failed for save");
}
} else {
int outRes = -1;
if (ContentRedirection_RemoveDevice(WUHB_ROMFS_PATH, &outRes) != CONTENT_REDIRECTION_RESULT_SUCCESS || res < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to remove device");
}
romfsUnmount(WUHB_ROMFS_PATH);
DEBUG_FUNCTION_LINE_ERR("ContentRedirection_AddFSLayer had failed for content");
}
}
DEBUG_FUNCTION_LINE_ERR("Failed to mount %s", gReplacementInfo.contentReplacementInfo.bundleMountInformation.toMountPath);
gReplacementInfo.contentReplacementInfo.bundleMountInformation.isMounted = false;
OSMemoryBarrier();
}
}

View File

@ -2,7 +2,7 @@
#include "logger.h"
#include <cstring>
int FileReader::read(uint8_t *buffer, uint32_t size) {
int64_t FileReader::read(uint8_t *buffer, uint32_t size) {
if (isReadFromBuffer) {
if (input_buffer == nullptr) {
return -1;
@ -31,7 +31,7 @@ FileReader::FileReader(std::string &path) {
this->isReadFromBuffer = false;
this->file_fd = fd;
} else {
DEBUG_FUNCTION_LINE("Failed to open file %s", path.c_str());
DEBUG_FUNCTION_LINE_ERR("Failed to open file %s", path.c_str());
}
}

View File

@ -15,7 +15,7 @@ public:
virtual ~FileReader();
virtual int read(uint8_t *buffer, uint32_t size);
virtual int64_t read(uint8_t *buffer, uint32_t size);
private:
bool isReadFromBuffer = false;
@ -24,5 +24,5 @@ private:
uint32_t input_pos = 0;
bool isReadFromFile = false;
int file_fd = 0;
int file_fd = -1;
};

View File

@ -1,89 +0,0 @@
#include "FileReaderCompressed.h"
int FileReaderCompressed::read(uint8_t *buffer, uint32_t size) {
if (!initDone) {
return -11;
}
int startValue = this->strm.total_out;
uint32_t newSize = 0;
int ret = 0;
do {
uint32_t nextOut = BUFFER_SIZE;
if (nextOut > size) {
nextOut = size;
}
if (this->strm.avail_in == 0) {
int read_res = FileReader::read(this->zlib_in_buf, BUFFER_SIZE);
if (read_res <= 0) {
break;
}
this->strm.avail_in = read_res;
this->strm.next_in = this->zlib_in_buf;
}
/* run inflate() on input until output buffer not full */
do {
if (nextOut > size - newSize) {
nextOut = size - newSize;
}
this->strm.avail_out = nextOut;
this->strm.next_out = buffer + newSize;
ret = inflate(&this->strm, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR) {
DEBUG_FUNCTION_LINE("Z_STREAM_ERROR");
return 0;
}
switch (ret) {
case Z_NEED_DICT:
DEBUG_FUNCTION_LINE("Z_NEED_DICT");
ret = Z_DATA_ERROR;
[[fallthrough]]; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
DEBUG_FUNCTION_LINE("Z_MEM_ERROR or Z_DATA_ERROR");
(void) inflateEnd(&this->strm);
return ret;
default:
break;
}
newSize = this->strm.total_out - startValue;
if (newSize == size) {
break;
}
nextOut = BUFFER_SIZE;
if (newSize + nextOut >= (size)) {
nextOut = (size) -newSize;
}
} while (this->strm.avail_out == 0 && newSize < (size));
/* done when inflate() says it's done */
} while (ret != Z_STREAM_END && newSize < size);
return newSize;
}
FileReaderCompressed::FileReaderCompressed(std::string &file) : FileReader(file) {
this->initCompressedData();
}
void FileReaderCompressed::initCompressedData() {
/* allocate inflate state */
this->strm.zalloc = Z_NULL;
this->strm.zfree = Z_NULL;
this->strm.opaque = Z_NULL;
this->strm.avail_in = 0;
this->strm.next_in = Z_NULL;
int ret = inflateInit2(&this->strm, MAX_WBITS | 16); //gzip
if (ret != Z_OK) {
DEBUG_FUNCTION_LINE("inflateInit2 failed");
return;
}
initDone = true;
}
FileReaderCompressed::FileReaderCompressed(uint8_t *buffer, uint32_t size) : FileReader(buffer, size) {
this->initCompressedData();
}

View File

@ -1,25 +0,0 @@
#pragma once
#include "FileReader.h"
#include "logger.h"
#include <zlib.h>
#define BUFFER_SIZE 0x20000
class FileReaderCompressed : public FileReader {
public:
FileReaderCompressed(uint8_t *buffer, uint32_t size);
explicit FileReaderCompressed(std::string &file);
~FileReaderCompressed() override = default;
int read(uint8_t *buffer, uint32_t size) override;
private:
bool initDone = false;
alignas(0x40) uint8_t zlib_in_buf[BUFFER_SIZE]{};
z_stream strm{};
void initCompressedData();
};

View File

@ -1,303 +1,14 @@
/***************************************************************************
* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <strings.h>
#include <utils/StringTools.h>
#include <vector>
#include <wchar.h>
#include <wut_types.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 nullptr;
len_rep = strlen(rep);
if (len_rep == 0)
return nullptr; // empty rep causes infinite loop during count
if (!with)
with = (char *) "";
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;
}
#include <stdint.h>
#include <utils/StringTools.h>
/* 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;
}

View File

@ -1,66 +1,21 @@
/***************************************************************************
* 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 <string>
#include <vector>
#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 */
#pragma once
#include "logger.h"
#include <memory>
#include <string>
#include <vector>
#include <wut_types.h>
template<typename... Args>
std::string string_format(const std::string &format, Args... args) {
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
auto size = static_cast<size_t>(size_s);
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
class StringTools {
public:
static uint32_t hash(char *str);
};

View File

@ -1,5 +1,6 @@
#pragma once
#include <coreinit/debug.h>
#include <string.h>
#include <whb/log.h>
@ -7,12 +8,21 @@
extern "C" {
#endif
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
// #define VERBOSE_DEBUG
#ifdef DEBUG
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#ifdef VERBOSE_DEBUG
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) \
do { \
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
#else
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
#endif
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
do { \
@ -34,6 +44,11 @@ extern "C" {
#endif
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) \
do { \
OSReport("## ERROR ## [%23s]%30s@L%04d: ##ERROR## " FMT "\n", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
void initLogging();
void deinitLogging();

4
src/wuhbutils.def Normal file
View File

@ -0,0 +1,4 @@
:NAME homebrew_wuhb_utils
:TEXT
WUU_GetVersion