commit a89b932085443182b86276147889ba2685836e33 Author: Maschell Date: Thu Aug 25 12:58:35 2022 +0200 Initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..56cc685 --- /dev/null +++ b/.clang-format @@ -0,0 +1,67 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9d2db0c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: CI-Release + +on: + push: + branches: + - main + +jobs: + clang-format: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source + build-binary: + runs-on: ubuntu-18.04 + needs: clang-format + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wms" + deploy-binary: + needs: build-binary + runs-on: ubuntu-18.04 + steps: + - name: Get environment variables + id: get_repository_name + run: | + echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//") >> $GITHUB_ENV + echo DATETIME=$(echo $(date '+%Y%m%d-%H%M%S')) >> $GITHUB_ENV + - uses: actions/download-artifact@master + with: + name: binary + - name: zip artifact + run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + draft: false + prerelease: true + body: | + Not a stable release: + ${{ github.event.head_commit.message }} + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_content_type: application/zip \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..d57cd59 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,36 @@ +name: CI-PR + +on: [pull_request] + +jobs: + clang-format: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source + check-build-with-logging: + runs-on: ubuntu-18.04 + needs: clang-format + steps: + - uses: actions/checkout@v2 + - name: build binary with logging + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make DEBUG=VERBOSE + docker run --rm -v ${PWD}:/project builder make clean + docker run --rm -v ${PWD}:/project builder make DEBUG=1 + build-binary: + runs-on: ubuntu-18.04 + needs: clang-format + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wms" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba57c28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.cbp +*.elf +*.layout +*.rpx +build/ +*.save-failed +.idea/ +cmake-build-debug/ +CMakeLists.txt +*.wms diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0ae10ef --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM wiiuenv/devkitppc:20220806 + +COPY --from=wiiuenv/libfunctionpatcher:20220724 /artifacts $DEVKITPRO +COPY --from=wiiuenv/wiiumodulesystem:20220724 /artifacts $DEVKITPRO + +WORKDIR project \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e90fb4 --- /dev/null +++ b/Makefile @@ -0,0 +1,152 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wums/share/wums_rules + +WUMS_ROOT := $(DEVKITPRO)/wums +WUT_ROOT := $(DEVKITPRO)/wut +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := AromaBaseModule +BUILD := build +SOURCES := source \ + source/applicationendshook \ + source/dynload \ + source/sdrefcount \ + source/symbolnamepatcher +DATA := data +INCLUDES := source + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -Wall -Wextra -O3 -ffunction-sections\ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ + +CXXFLAGS := $(CFLAGS) -std=c++20 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld $(WUMSSPECS) + +ifeq ($(DEBUG),1) +CXXFLAGS += -DDEBUG -g +CFLAGS += -DDEBUG -g +endif + +ifeq ($(DEBUG),VERBOSE) +CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g +CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g +endif + +LIBS := -lwums -lwut -lfunctionpatcher + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +#------------------------------------------------------------------------------- + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).wms $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wms + +$(OUTPUT).wms : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..5005be3 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +[![CI-Release](https://github.com/wiiu-env/AromaBaseModule/actions/workflows/ci.yml/badge.svg)](https://github.com/wiiu-env/AromaBaseModule/actions/workflows/ci.yml) + +## Usage +(`[ENVIRONMENT]` is a placeholder for the actual environment name.) + +1. Copy the file `AromaBaseModule.wms` into `sd:/wiiu/environments/[ENVIRONMENT]/modules`. +2. Requires the [WUMSLoader](https://github.com/wiiu-env/WUMSLoader) in `sd:/wiiu/environments/[ENVIRONMENT]/modules/setup`. + +## Buildflags + +### Logging +Building via `make` only logs errors (via OSReport). To enable logging via the [LoggingModule](https://github.com/wiiu-env/LoggingModule) set `DEBUG` to `1` or `VERBOSE`. + +`make` Logs errors only (via OSReport). +`make DEBUG=1` Enables information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule). +`make DEBUG=VERBOSE` Enables verbose information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule). + +If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present, it'll fallback to UDP (Port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging. + +## 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 aromabasemodule-builder + +# make +docker run -it --rm -v ${PWD}:/project aromabasemodule-builder make + +# make clean +docker run -it --rm -v ${PWD}:/project aromabasemodule-builder make clean +``` + +## Format the code via docker + +`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source -i` \ No newline at end of file diff --git a/source/applicationendshook/applicationendhook.cpp b/source/applicationendshook/applicationendhook.cpp new file mode 100644 index 0000000..8c9bf4c --- /dev/null +++ b/source/applicationendshook/applicationendhook.cpp @@ -0,0 +1,14 @@ +#include "applicationendhook.h" +#include "applicationends_function_replacements.h" +#include "logger.h" + + +void initApplicationEndsHook() { + DEBUG_FUNCTION_LINE("Patch ApplicationEndsHook functions"); + for (uint32_t i = 0; i < applicationendshook_function_replacements_size; i++) { + if (!FunctionPatcherPatchFunction(&applicationendshook_function_replacements[i], nullptr)) { + OSFatal("AromaBaseModule: Failed to patch ApplicationEndsHook function"); + } + } + DEBUG_FUNCTION_LINE("Patch ApplicationEndsHook functions finished"); +} diff --git a/source/applicationendshook/applicationendhook.h b/source/applicationendshook/applicationendhook.h new file mode 100644 index 0000000..7b5ce0d --- /dev/null +++ b/source/applicationendshook/applicationendhook.h @@ -0,0 +1,3 @@ +#pragma once + +void initApplicationEndsHook(); \ No newline at end of file diff --git a/source/applicationendshook/applicationends_function_replacements.cpp b/source/applicationendshook/applicationends_function_replacements.cpp new file mode 100644 index 0000000..9104b0f --- /dev/null +++ b/source/applicationendshook/applicationends_function_replacements.cpp @@ -0,0 +1,54 @@ +#include "applicationends_function_replacements.h" +#include "globals.h" + +#include +#include +#include + +static uint32_t lastData0 = 0; + +void CallHook(wums_hook_type_t type) { + for (uint32_t i = 0; i < gModuleData->number_modules; i++) { + auto *curModule = &gModuleData->modules[i]; + for (uint32_t j = 0; j < curModule->number_hook_entries; j++) { + auto hook_entry = &curModule->hook_entries[j]; + auto hook_type = (wums_hook_type_t) hook_entry->type; + if (hook_type == type) { + if ((type == WUMS_HOOK_INIT_WRAPPER || type == WUMS_HOOK_FINI_WRAPPER) && curModule->skipInitFini) { + continue; + } + const void *target = (const void *) hook_entry->target; + ((void (*)())((uint32_t *) target))(); + } + } + } +} + +DECL_FUNCTION(void, _Exit, uint32_t status) { + CallHook(WUMS_HOOK_APPLICATION_ENDS); + CallHook(WUMS_HOOK_FINI_WUT_SOCKETS); + CallHook(WUMS_HOOK_FINI_WUT_DEVOPTAB); + real__Exit(status); +} + +DECL_FUNCTION(uint32_t, OSReceiveMessage, OSMessageQueue *queue, OSMessage *message, uint32_t flags) { + uint32_t res = real_OSReceiveMessage(queue, message, flags); + if (queue == OSGetSystemMessageQueue()) { + if (message != nullptr && res) { + if (lastData0 != message->args[0]) { + if (message->args[0] == 0xD1E0D1E0) { + CallHook(WUMS_HOOK_APPLICATION_REQUESTS_EXIT); + } + } + lastData0 = message->args[0]; + } + } + return res; +} + +function_replacement_data_t applicationendshook_function_replacements[] = { + REPLACE_FUNCTION(OSReceiveMessage, LIBRARY_COREINIT, OSReceiveMessage), + REPLACE_FUNCTION(_Exit, LIBRARY_COREINIT, _Exit), +}; + +uint32_t applicationendshook_function_replacements_size = sizeof(applicationendshook_function_replacements) / sizeof(function_replacement_data_t); \ No newline at end of file diff --git a/source/applicationendshook/applicationends_function_replacements.h b/source/applicationendshook/applicationends_function_replacements.h new file mode 100644 index 0000000..201fb93 --- /dev/null +++ b/source/applicationendshook/applicationends_function_replacements.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern function_replacement_data_t applicationendshook_function_replacements[]; + + +#ifdef __cplusplus +} +#endif + + +extern uint32_t applicationendshook_function_replacements_size; \ No newline at end of file diff --git a/source/dynload/dynload.cpp b/source/dynload/dynload.cpp new file mode 100644 index 0000000..bb4877e --- /dev/null +++ b/source/dynload/dynload.cpp @@ -0,0 +1,21 @@ +#include "dynload.h" +#include "../globals.h" +#include "dynload_function_replacements.h" +#include "logger.h" +#include + +void initDynload() { + gRPLData = (LOADED_RPL *) malloc(sizeof(LOADED_RPL) * gModuleData->number_modules); + if (!gRPLData) { + DEBUG_FUNCTION_LINE_ERR("Failed to allocate gRPLData"); + OSFatal("AromaBaseModule: Failed to allocate gRPLData"); + } + + DEBUG_FUNCTION_LINE("Patch functions for dynload patches"); + for (uint32_t i = 0; i < dynload_function_replacements_size; i++) { + if (!FunctionPatcherPatchFunction(&dynload_function_replacements[i], nullptr)) { + OSFatal("AromaBaseModule: Failed to patch function for dynload patches"); + } + } + DEBUG_FUNCTION_LINE("Patch functions finished for dynload patches"); +} \ No newline at end of file diff --git a/source/dynload/dynload.h b/source/dynload/dynload.h new file mode 100644 index 0000000..87eb9a3 --- /dev/null +++ b/source/dynload/dynload.h @@ -0,0 +1,3 @@ +#pragma once + +void initDynload(); \ No newline at end of file diff --git a/source/dynload/dynload_function_replacements.cpp b/source/dynload/dynload_function_replacements.cpp new file mode 100644 index 0000000..bfa1cc4 --- /dev/null +++ b/source/dynload/dynload_function_replacements.cpp @@ -0,0 +1,147 @@ +#include "dynload_function_replacements.h" +#include "globals.h" +#include "loader_defines.h" +#include "logger.h" +#include +#include +#include + +DECL_FUNCTION(OSDynLoad_Error, OSDynLoad_Acquire, char const *name, OSDynLoad_Module *outModule) { + DEBUG_FUNCTION_LINE_VERBOSE("Looking for module %s", name); + for (uint32_t i = 0; i < gModuleData->number_modules; i++) { + if (strcmp(name, gModuleData->modules[i].module_export_name) == 0) { + *outModule = (OSDynLoad_Module) (0x13370000 + i); + return OS_DYNLOAD_OK; + } + } + + OSDynLoad_Error result = real_OSDynLoad_Acquire(name, outModule); + if (result == OS_DYNLOAD_OK) { + return OS_DYNLOAD_OK; + } + + return result; +} + +DECL_FUNCTION(OSDynLoad_Error, OSDynLoad_FindExport, OSDynLoad_Module module, BOOL isData, char const *name, void **outAddr) { + OSDynLoad_Error result = real_OSDynLoad_FindExport(module, isData, name, outAddr); + if (result == OS_DYNLOAD_OK) { + return OS_DYNLOAD_OK; + } + + if (((uint32_t) module & 0xFFFF0000) == 0x13370000) { + uint32_t moduleHandle = ((uint32_t) module) & 0x0000FFFF; + if (moduleHandle >= gModuleData->number_modules) { + DEBUG_FUNCTION_LINE_ERR("Invalid module handle was encoded in OSDynLoad_Module %d (%08X)", moduleHandle, module); + return result; + } + + auto *curModule = &gModuleData->modules[moduleHandle]; + DEBUG_FUNCTION_LINE_VERBOSE("Try to find export %s in module %s", name, curModule->module_export_name); + for (uint32_t i = 0; i < curModule->number_export_entries; i++) { + auto *curExport = &curModule->export_entries[i]; + if (strcmp(name, curExport->name) == 0) { + if (isData && curExport->type == WUMS_FUNCTION_EXPORT) { + DEBUG_FUNCTION_LINE_ERR("Requested data Export but only found a function export"); + return OS_DYNLOAD_INVALID_MODULE_NAME; + } + *outAddr = (void *) curExport->address; + DEBUG_FUNCTION_LINE_VERBOSE("SUCCESS! Set outAddr to %08X. It's from module %s function %s", + curExport->address, + curModule->module_export_name, + curExport->name); + return OS_DYNLOAD_OK; + } + } + } + return result; +} + +RPLFileInfo_v4_2 fileInfoBuffer; + +DECL_FUNCTION(LOADED_RPL *, LiFindRPLByName, char *name) { + for (uint32_t i = 0; i < gModuleData->number_modules; i++) { + auto *curModule = &gModuleData->modules[i]; + if (strcmp(name, curModule->module_export_name) == 0) { + fileInfoBuffer.tlsModuleIndex = 0; + gRPLData[i].fileInfoBuffer = &fileInfoBuffer; // will be copied to the LiImportTracking array + gRPLData[i].loadStateFlags = 0x0; + gRPLData[i].entrypoint = 0x1; //needs to be != 0; + gRPLData[i].funcExports = (Export *) (FUNCTION_EXPORT_MASK + i); + gRPLData[i].numFuncExports = 1; + gRPLData[i].dataExports = (Export *) (DATA_EXPORT_MASK + i); + gRPLData[i].numDataExports = 1; + return &gRPLData[i]; + } + } + return real_LiFindRPLByName(name); +} + +DECL_FUNCTION(uint32_t, __OSDynLoad_InternalAcquire, char *name, void *out, uint32_t u1, uint32_t u2, uint32_t u3) { + for (uint32_t i = 0; i < gModuleData->number_modules; i++) { + auto *curModule = &gModuleData->modules[i]; + if (strcmp(name, curModule->module_export_name) == 0) { + return 0; + } + } + return real___OSDynLoad_InternalAcquire(name, out, u1, u2, u3); +} + +Export ourExportThing; + +DECL_FUNCTION(Export *, LiBinSearchExport, Export *exports, int numExports, char *name) { + auto isFunc = (((uint32_t) exports) & 0xFFFF0000) == FUNCTION_EXPORT_MASK; + auto isData = (((uint32_t) exports) & 0xFFFF0000) == DATA_EXPORT_MASK; + if (isFunc || isData) { + uint32_t moduleHandle = ((uint32_t) exports) & 0x0000FFFF; + if (moduleHandle > gModuleData->number_modules) { + DEBUG_FUNCTION_LINE_LOADER_ERR("Invalid module handle was encoded in Export %d (%08X)", moduleHandle, exports); + return nullptr; + } + + auto *curModule = &gModuleData->modules[moduleHandle]; + DEBUG_FUNCTION_LINE_LOADER_VERBOSE("Try to find export %s in module %s", name, curModule->module_export_name); + for (uint32_t i = 0; i < curModule->number_export_entries; i++) { + auto *curExport = &curModule->export_entries[i]; + if (strcmp(name, curExport->name) == 0) { + if ((isFunc && curExport->type == WUMS_FUNCTION_EXPORT) || (isData && curExport->type == WUMS_DATA_EXPORT)) { + ourExportThing.value = curExport->address; + ourExportThing.name = 0; + return &ourExportThing; + } + } + } + return nullptr; + } + return real_LiBinSearchExport(exports, numExports, name); +} + +DECL_FUNCTION(int32_t, LiFixupRelocOneRPL, LOADED_RPL *rpl, void *imports, uint32_t unknown) { + auto rplAddress = (uint32_t) rpl; + if (rplAddress >= (uint32_t) &gRPLData[0] && rplAddress < (uint32_t) &gRPLData[gModuleData->number_modules]) { + // Skip if this is our fake RPL + return 0; + } + return real_LiFixupRelocOneRPL(rpl, imports, unknown); +} + +DECL_FUNCTION(int32_t, sCheckOne, LOADED_RPL *rpl) { + auto rplAddress = (uint32_t) rpl; + if (rplAddress >= (uint32_t) &gRPLData[0] && rplAddress < (uint32_t) &gRPLData[gModuleData->number_modules]) { + // Skip if this is our fake RPL + return 0; + } + return real_sCheckOne(rpl); +} + +function_replacement_data_t dynload_function_replacements[] = { + REPLACE_FUNCTION_VIA_ADDRESS(__OSDynLoad_InternalAcquire, 0x32029054, 0x101C400 + 0x0cc54), + REPLACE_FUNCTION_VIA_ADDRESS(LiFindRPLByName, 0x32004BC4, 0x01004bc4), + REPLACE_FUNCTION_VIA_ADDRESS(LiBinSearchExport, 0x320002f8, 0x010002f8), + REPLACE_FUNCTION_VIA_ADDRESS(sCheckOne, 0x32007294, 0x01007294), + REPLACE_FUNCTION_VIA_ADDRESS(LiFixupRelocOneRPL, 0x320059f0, 0x010059f0), + REPLACE_FUNCTION(OSDynLoad_Acquire, LIBRARY_COREINIT, OSDynLoad_Acquire), + REPLACE_FUNCTION(OSDynLoad_FindExport, LIBRARY_COREINIT, OSDynLoad_FindExport), +}; + +uint32_t dynload_function_replacements_size = sizeof(dynload_function_replacements) / sizeof(function_replacement_data_t); \ No newline at end of file diff --git a/source/dynload/dynload_function_replacements.h b/source/dynload/dynload_function_replacements.h new file mode 100644 index 0000000..54d8b68 --- /dev/null +++ b/source/dynload/dynload_function_replacements.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +extern function_replacement_data_t dynload_function_replacements[]; +extern uint32_t dynload_function_replacements_size; \ No newline at end of file diff --git a/source/dynload/loader_defines.h b/source/dynload/loader_defines.h new file mode 100644 index 0000000..834a7ac --- /dev/null +++ b/source/dynload/loader_defines.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +// see https://github.com/decaf-emu/decaf-emu/tree/43366a34e7b55ab9d19b2444aeb0ccd46ac77dea/src/libdecaf/src/cafe/loader +struct LiImportTracking { + uint32_t numExports; + struct Export *exports; + uint32_t tlsModuleIndex; + struct LOADED_RPL *rpl; +}; + +struct Export { + uint32_t value; + uint32_t name; +}; + +struct RPLFileInfo_v4_2 { + char field_0x0[88]; + uint16_t tlsModuleIndex; + char field_0x5a[6]; +}; + +struct LOADED_RPL { + char u1[88]; + struct RPLFileInfo_v4_2 *fileInfoBuffer; + char u2[20]; + uint32_t loadStateFlags; + uint32_t entrypoint; + char u3[132]; + uint32_t numFuncExports; + Export *funcExports; + uint32_t numDataExports; + Export *dataExports; + char u4[12]; +}; + +#define FUNCTION_EXPORT_MASK 0x13370000 +#define DATA_EXPORT_MASK 0x13380000 \ No newline at end of file diff --git a/source/globals.cpp b/source/globals.cpp new file mode 100644 index 0000000..95daff6 --- /dev/null +++ b/source/globals.cpp @@ -0,0 +1,5 @@ +#include "globals.h" + +module_information_t *gModuleData __attribute__((section(".data"))) = NULL; +int32_t gSDMountRefCount __attribute__((section(".data"))) = 0; +LOADED_RPL *gRPLData __attribute__((section(".data"))) = nullptr; \ No newline at end of file diff --git a/source/globals.h b/source/globals.h new file mode 100644 index 0000000..2e577a4 --- /dev/null +++ b/source/globals.h @@ -0,0 +1,6 @@ +#include "dynload/loader_defines.h" +#include + +extern module_information_t *gModuleData; +extern int32_t gSDMountRefCount; +extern LOADED_RPL *gRPLData; \ No newline at end of file diff --git a/source/logger.c b/source/logger.c new file mode 100644 index 0000000..f700806 --- /dev/null +++ b/source/logger.c @@ -0,0 +1,36 @@ +#ifdef DEBUG +#include +#include +#include +#include + +uint32_t moduleLogInit = false; +uint32_t cafeLogInit = false; +uint32_t udpLogInit = false; +#endif // DEBUG + +void initLogging() { +#ifdef DEBUG + if (!(moduleLogInit = WHBLogModuleInit())) { + cafeLogInit = WHBLogCafeInit(); + udpLogInit = WHBLogUdpInit(); + } +#endif // DEBUG +} + +void deinitLogging() { +#ifdef DEBUG + if (moduleLogInit) { + WHBLogModuleDeinit(); + moduleLogInit = false; + } + if (cafeLogInit) { + WHBLogCafeDeinit(); + cafeLogInit = false; + } + if (udpLogInit) { + WHBLogUdpDeinit(); + udpLogInit = false; + } +#endif // DEBUG +} \ No newline at end of file diff --git a/source/logger.h b/source/logger.h new file mode 100644 index 0000000..df62734 --- /dev/null +++ b/source/logger.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_APP_TYPE "M" +#define LOG_APP_NAME "application_ends" + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", FMT, ##ARGS) + +#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS) + +#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \ + do { \ + LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \ + } while (0) + +#ifdef DEBUG + +#ifdef VERBOSE_DEBUG +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "", "", FMT, ##ARGS); +#define DEBUG_FUNCTION_LINE_LOADER_VERBOSE(FMT, ARGS...) LOG_EX_DEFAULT(((void (*)(const char *, ...))((uint32_t *) 0x010028d0)), "", "\n", FMT, ##ARGS) +#else +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_LOADER_VERBOSE(FMT, ARGS...) while (0) +#endif + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "## WARN## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS); + +#else + +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_LOADER_VERBOSE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "## WARN## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS); + +#endif + +#define DEBUG_FUNCTION_LINE_LOADER_ERR(FMT, ARGS...) LOG_EX_DEFAULT(((void (*)(const char *, ...))((uint32_t *) 0x010028d0)), "##ERROR## ", "\n", FMT, ##ARGS) + +void initLogging(); + +void deinitLogging(); + +#ifdef __cplusplus +} +#endif diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..75ad3cc --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,28 @@ +#include "applicationendshook/applicationendhook.h" +#include "dynload/dynload.h" +#include "globals.h" +#include "logger.h" +#include "sdrefcount/refcount.h" +#include "symbolnamepatcher/symbolname.h" +#include + +WUMS_MODULE_EXPORT_NAME("homebrew_basemodule"); +WUMS_MODULE_SKIP_INIT_FINI(); + +WUMS_INITIALIZE(args) { + initLogging(); + gModuleData = args.module_information; + if (gModuleData == nullptr) { + OSFatal("AromaBaseModule: Failed to get gModuleData pointer."); + } + if (gModuleData->version != MODULE_INFORMATION_VERSION) { + OSFatal("AromaBaseModule: The module information struct version does not match."); + } + + initApplicationEndsHook(); + initSDRefCount(); + initSymbolNamePatcher(); + initDynload(); + + deinitLogging(); +} diff --git a/source/sdrefcount/refcount.cpp b/source/sdrefcount/refcount.cpp new file mode 100644 index 0000000..1d0ae11 --- /dev/null +++ b/source/sdrefcount/refcount.cpp @@ -0,0 +1,29 @@ +#include "globals.h" +#include "logger.h" +#include "sd_function_replacements.h" +#include + +void initSDRefCount() { + FSAInit(); + auto client = FSAAddClient(nullptr); + gSDMountRefCount = 0; + if (client > 0) { + auto res = FSAMount(client, "/dev/sdcard01", "/vol/external01", (FSAMountFlags) 0, nullptr, 0); + if (res == FS_ERROR_ALREADY_EXISTS) { + DEBUG_FUNCTION_LINE("SDCard is already mounted, set ref counter to 16"); + gSDMountRefCount = 16; + } else { + DEBUG_FUNCTION_LINE("Set ref counter to 0"); + FSAUnmount(client, "/vol/external01", FSA_UNMOUNT_FLAG_BIND_MOUNT); + } + FSADelClient(res); + } + + DEBUG_FUNCTION_LINE("Patch SDRefCount functions"); + for (uint32_t i = 0; i < sdrefcount_function_replacements_size; i++) { + if (!FunctionPatcherPatchFunction(&sdrefcount_function_replacements[i], nullptr)) { + OSFatal("AromaBaseModule: Failed to patch function for sd ref counting"); + } + } + DEBUG_FUNCTION_LINE("Patch SDRefCount functions finished"); +} \ No newline at end of file diff --git a/source/sdrefcount/refcount.h b/source/sdrefcount/refcount.h new file mode 100644 index 0000000..c1a508a --- /dev/null +++ b/source/sdrefcount/refcount.h @@ -0,0 +1,3 @@ +#pragma once + +void initSDRefCount(); \ No newline at end of file diff --git a/source/sdrefcount/sd_function_replacements.cpp b/source/sdrefcount/sd_function_replacements.cpp new file mode 100644 index 0000000..8e08ddd --- /dev/null +++ b/source/sdrefcount/sd_function_replacements.cpp @@ -0,0 +1,93 @@ +#include "sd_function_replacements.h" +#include "globals.h" +#include "logger.h" +#include +#include + +DECL_FUNCTION(void, __PPCExit, uint32_t u1) { + if (gSDMountRefCount > 0) { + DEBUG_FUNCTION_LINE_WARN("SDCard is still mounted. Ref counter: %d\n", gSDMountRefCount); + FSAInit(); + auto client = FSAAddClient(nullptr); + if (client) { + auto res = FSAUnmount(client, "/vol/external01/", FSA_UNMOUNT_FLAG_BIND_MOUNT); + DEBUG_FUNCTION_LINE_WARN("Unmount res %d %s", res, FSAGetStatusStr(res)); + } + gSDMountRefCount = 0; + } else { + DEBUG_FUNCTION_LINE_WARN("Refcount %d", gSDMountRefCount); + } + real___PPCExit(u1); +} + +DECL_FUNCTION(FSStatus, FSMount, FSClient *client, FSCmdBlock *cmd, FSMountSource *source, char *target, uint32_t bytes, FSErrorFlag errorMask) { + if (std::string_view(target) == "/vol/external01") { + if (gSDMountRefCount > 0) { + gSDMountRefCount++; + return FS_STATUS_OK; + } + DEBUG_FUNCTION_LINE_WARN("Do real mount for /vol/external01"); + auto res = real_FSMount(client, cmd, source, target, bytes, errorMask); + if (res == FS_STATUS_OK) { + gSDMountRefCount++; + } + return res; + } + + + return real_FSMount(client, cmd, source, target, bytes, errorMask); +} + +DECL_FUNCTION(FSStatus, FSUnmount, FSClient *client, FSCmdBlock *cmd, const char *target, FSErrorFlag errorMask) { + if (std::string_view(target) == "/vol/external01") { + gSDMountRefCount--; + if (gSDMountRefCount <= 0) { + gSDMountRefCount = 0; + DEBUG_FUNCTION_LINE_WARN("Do real unmount for /vol/external01"); + return real_FSUnmount(client, cmd, target, errorMask); + } + return FS_STATUS_OK; + } + return real_FSUnmount(client, cmd, target, errorMask); +} + +DECL_FUNCTION(FSError, FSAMount, FSAClientHandle client, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len) { + if (std::string_view(target) == "/vol/external01") { + if (gSDMountRefCount > 0) { + gSDMountRefCount++; + return FS_ERROR_OK; + } + DEBUG_FUNCTION_LINE_WARN("Do real mount for /vol/external01"); + auto res = real_FSAMount(client, source, target, flags, arg_buf, arg_len); + if (res == FS_ERROR_OK || res == FS_ERROR_ALREADY_EXISTS) { + gSDMountRefCount++; + } + return res; + } + + return real_FSAMount(client, source, target, flags, arg_buf, arg_len); +} + +DECL_FUNCTION(FSError, FSAUnmount, FSAClientHandle client, const char *mountedTarget, FSAUnmountFlags flags) { + if (std::string_view(mountedTarget) == "/vol/external01") { + gSDMountRefCount--; + if (gSDMountRefCount <= 0) { + DEBUG_FUNCTION_LINE_WARN("Do real unmount for /vol/external01"); + auto res = real_FSAUnmount(client, mountedTarget, flags); + gSDMountRefCount = 0; + return res; + } + return FS_ERROR_OK; + } + + return real_FSAUnmount(client, mountedTarget, flags); +} + +function_replacement_data_t sdrefcount_function_replacements[] = { + REPLACE_FUNCTION(__PPCExit, LIBRARY_COREINIT, __PPCExit), + REPLACE_FUNCTION(FSMount, LIBRARY_COREINIT, FSMount), + REPLACE_FUNCTION(FSUnmount, LIBRARY_COREINIT, FSUnmount), + REPLACE_FUNCTION(FSAMount, LIBRARY_COREINIT, FSAMount), + REPLACE_FUNCTION(FSAUnmount, LIBRARY_COREINIT, FSAUnmount)}; + +uint32_t sdrefcount_function_replacements_size = sizeof(sdrefcount_function_replacements) / sizeof(function_replacement_data_t); \ No newline at end of file diff --git a/source/sdrefcount/sd_function_replacements.h b/source/sdrefcount/sd_function_replacements.h new file mode 100644 index 0000000..d367e26 --- /dev/null +++ b/source/sdrefcount/sd_function_replacements.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern function_replacement_data_t sdrefcount_function_replacements[]; + +#ifdef __cplusplus +} +#endif + +extern uint32_t sdrefcount_function_replacements_size; \ No newline at end of file diff --git a/source/symbolnamepatcher/sym_function_replacements.cpp b/source/symbolnamepatcher/sym_function_replacements.cpp new file mode 100644 index 0000000..246194c --- /dev/null +++ b/source/symbolnamepatcher/sym_function_replacements.cpp @@ -0,0 +1,88 @@ +#include "sym_function_replacements.h" +#include "globals.h" +#include + +#pragma GCC push_options +#pragma GCC optimize("O0") + +DECL_FUNCTION(uint32_t, SC17_FindClosestSymbol, + uint32_t addr, + uint32_t *outDistance, + char *symbolNameBuffer, + uint32_t symbolNameBufferLength, + char *moduleNameBuffer, + uint32_t moduleNameBufferLength) { + for (uint32_t i = 0; i < gModuleData->number_modules; i++) { + auto *module = &gModuleData->modules[i]; + if (addr < module->startAddress || addr >= module->endAddress) { + continue; + } + + strncpy(moduleNameBuffer, module->module_export_name, moduleNameBufferLength); + + if (module->function_symbol_entries != nullptr && module->number_function_symbols > 1) { + for (uint32_t j = 0; j < module->number_function_symbols - 1; j++) { + auto symbolData = &module->function_symbol_entries[j]; + auto symbolDataNext = &module->function_symbol_entries[j + 1]; + if (j == module->number_function_symbols - 2 || (addr >= (uint32_t) symbolData->address && addr < (uint32_t) symbolDataNext->address)) { + strncpy(symbolNameBuffer, symbolData->name, moduleNameBufferLength); + if (outDistance) { + *outDistance = addr - (uint32_t) symbolData->address; + } + return 0; + } + } + } + + strncpy(symbolNameBuffer, ".text", symbolNameBufferLength); + + if (outDistance) { + *outDistance = addr - (uint32_t) module->startAddress; + } + + return 0; + } + return real_SC17_FindClosestSymbol(addr, outDistance, symbolNameBuffer, symbolNameBufferLength, moduleNameBuffer, moduleNameBufferLength); +} + +DECL_FUNCTION(uint32_t, KiGetAppSymbolName, uint32_t addr, char *buffer, int32_t bufSize) { + for (uint32_t i = 0; i < gModuleData->number_modules; i++) { + auto *module = &gModuleData->modules[i]; + if (addr < module->startAddress || addr >= module->endAddress) { + continue; + } + + auto moduleNameLen = strlen(module->module_export_name); + int32_t spaceLeftInBuffer = (int32_t) bufSize - (int32_t) moduleNameLen - 1; + if (spaceLeftInBuffer < 0) { + spaceLeftInBuffer = 0; + } + strncpy(buffer, module->module_export_name, bufSize); + + if (module->function_symbol_entries != nullptr && module->number_function_symbols > 1) { + for (uint32_t j = 0; j < module->number_function_symbols - 1; j++) { + auto symbolData = &module->function_symbol_entries[j]; + auto symbolDataNext = &module->function_symbol_entries[j + 1]; + if (j == module->number_function_symbols - 2 || (addr >= (uint32_t) symbolData->address && addr < (uint32_t) symbolDataNext->address)) { + if (spaceLeftInBuffer > 2) { + buffer[moduleNameLen] = '|'; + buffer[moduleNameLen + 1] = '\0'; + strncpy(buffer + moduleNameLen + 1, symbolData->name, spaceLeftInBuffer - 1); + } + return (uint32_t) symbolData->address; + } + } + } + return addr; + } + + return real_KiGetAppSymbolName(addr, buffer, bufSize); +} +#pragma GCC pop_options + +function_replacement_data_t symbolname_function_replacements[] = { + REPLACE_FUNCTION_VIA_ADDRESS(SC17_FindClosestSymbol, 0xfff10218, 0xfff10218), + REPLACE_FUNCTION_VIA_ADDRESS(KiGetAppSymbolName, 0xfff0e3a0, 0xfff0e3a0), +}; + +uint32_t symbolname_function_replacements_size = sizeof(symbolname_function_replacements) / sizeof(function_replacement_data_t); \ No newline at end of file diff --git a/source/symbolnamepatcher/sym_function_replacements.h b/source/symbolnamepatcher/sym_function_replacements.h new file mode 100644 index 0000000..4566b6c --- /dev/null +++ b/source/symbolnamepatcher/sym_function_replacements.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern function_replacement_data_t symbolname_function_replacements[]; + +#ifdef __cplusplus +} +#endif + +extern uint32_t symbolname_function_replacements_size; \ No newline at end of file diff --git a/source/symbolnamepatcher/symbolname.cpp b/source/symbolnamepatcher/symbolname.cpp new file mode 100644 index 0000000..a1a9399 --- /dev/null +++ b/source/symbolnamepatcher/symbolname.cpp @@ -0,0 +1,13 @@ +#include "globals.h" +#include "logger.h" +#include "sym_function_replacements.h" + +void initSymbolNamePatcher() { + DEBUG_FUNCTION_LINE("Patch SymbolNamePatcher functions"); + for (uint32_t i = 0; i < symbolname_function_replacements_size; i++) { + if (!FunctionPatcherPatchFunction(&symbolname_function_replacements[i], nullptr)) { + OSFatal("AromaBaseModule: Failed to patch SymbolNamePatcher function"); + } + } + DEBUG_FUNCTION_LINE("Patch SymbolNamePatcher functions finished"); +} \ No newline at end of file diff --git a/source/symbolnamepatcher/symbolname.h b/source/symbolnamepatcher/symbolname.h new file mode 100644 index 0000000..c9843bc --- /dev/null +++ b/source/symbolnamepatcher/symbolname.h @@ -0,0 +1,3 @@ +#pragma once + +void initSymbolNamePatcher(); \ No newline at end of file