Initial commit

This commit is contained in:
Maschell 2022-08-25 12:58:35 +02:00
commit a89b932085
29 changed files with 1089 additions and 0 deletions

67
.clang-format Normal file
View File

@ -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

65
.github/workflows/ci.yml vendored Normal file
View File

@ -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

36
.github/workflows/pr.yml vendored Normal file
View File

@ -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"

10
.gitignore vendored Normal file
View File

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

6
Dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM wiiuenv/devkitppc:20220806
COPY --from=wiiuenv/libfunctionpatcher:20220724 /artifacts $DEVKITPRO
COPY --from=wiiuenv/wiiumodulesystem:20220724 /artifacts $DEVKITPRO
WORKDIR project

152
Makefile Normal file
View File

@ -0,0 +1,152 @@
#-------------------------------------------------------------------------------
.SUFFIXES:
#-------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/wums/share/wums_rules
WUMS_ROOT := $(DEVKITPRO)/wums
WUT_ROOT := $(DEVKITPRO)/wut
#-------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#-------------------------------------------------------------------------------
TARGET := 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
#-------------------------------------------------------------------------------

37
README.md Normal file
View File

@ -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`

View File

@ -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");
}

View File

@ -0,0 +1,3 @@
#pragma once
void initApplicationEndsHook();

View File

@ -0,0 +1,54 @@
#include "applicationends_function_replacements.h"
#include "globals.h"
#include <coreinit/dynload.h>
#include <coreinit/messagequeue.h>
#include <wums.h>
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);

View File

@ -0,0 +1,18 @@
#pragma once
#include <cstdint>
#include <function_patcher/function_patching.h>
#ifdef __cplusplus
extern "C" {
#endif
extern function_replacement_data_t applicationendshook_function_replacements[];
#ifdef __cplusplus
}
#endif
extern uint32_t applicationendshook_function_replacements_size;

View File

@ -0,0 +1,21 @@
#include "dynload.h"
#include "../globals.h"
#include "dynload_function_replacements.h"
#include "logger.h"
#include <malloc.h>
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");
}

3
source/dynload/dynload.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void initDynload();

View File

@ -0,0 +1,147 @@
#include "dynload_function_replacements.h"
#include "globals.h"
#include "loader_defines.h"
#include "logger.h"
#include <coreinit/dynload.h>
#include <cstring>
#include <wums.h>
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);

View File

@ -0,0 +1,7 @@
#pragma once
#include <cstdint>
#include <function_patcher/function_patching.h>
extern function_replacement_data_t dynload_function_replacements[];
extern uint32_t dynload_function_replacements_size;

View File

@ -0,0 +1,39 @@
#pragma once
#include <cstdint>
// 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

5
source/globals.cpp Normal file
View File

@ -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;

6
source/globals.h Normal file
View File

@ -0,0 +1,6 @@
#include "dynload/loader_defines.h"
#include <wums.h>
extern module_information_t *gModuleData;
extern int32_t gSDMountRefCount;
extern LOADED_RPL *gRPLData;

36
source/logger.c Normal file
View File

@ -0,0 +1,36 @@
#ifdef DEBUG
#include <stdint.h>
#include <whb/log_cafe.h>
#include <whb/log_module.h>
#include <whb/log_udp.h>
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
}

74
source/logger.h Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include <coreinit/debug.h>
#include <string.h>
#include <whb/log.h>
#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

28
source/main.cpp Normal file
View File

@ -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.h>
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();
}

View File

@ -0,0 +1,29 @@
#include "globals.h"
#include "logger.h"
#include "sd_function_replacements.h"
#include <coreinit/filesystem_fsa.h>
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");
}

View File

@ -0,0 +1,3 @@
#pragma once
void initSDRefCount();

View File

@ -0,0 +1,93 @@
#include "sd_function_replacements.h"
#include "globals.h"
#include "logger.h"
#include <coreinit/filesystem_fsa.h>
#include <string_view>
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);

View File

@ -0,0 +1,16 @@
#pragma once
#include <cstdint>
#include <function_patcher/function_patching.h>
#ifdef __cplusplus
extern "C" {
#endif
extern function_replacement_data_t sdrefcount_function_replacements[];
#ifdef __cplusplus
}
#endif
extern uint32_t sdrefcount_function_replacements_size;

View File

@ -0,0 +1,88 @@
#include "sym_function_replacements.h"
#include "globals.h"
#include <cstring>
#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);

View File

@ -0,0 +1,16 @@
#pragma once
#include <cstdint>
#include <function_patcher/function_patching.h>
#ifdef __cplusplus
extern "C" {
#endif
extern function_replacement_data_t symbolname_function_replacements[];
#ifdef __cplusplus
}
#endif
extern uint32_t symbolname_function_replacements_size;

View File

@ -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");
}

View File

@ -0,0 +1,3 @@
#pragma once
void initSymbolNamePatcher();