Update the lib to dynamically use the FunctionPatcherModule

This commit is contained in:
Maschell 2023-01-06 09:10:47 +01:00
parent 1eecf95065
commit 2188fb5e5b
7 changed files with 326 additions and 34 deletions

View File

@ -70,7 +70,6 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
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 +87,7 @@ endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(DEFFILES:.def=.o) $(SFILES:.s=.o) $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
export OFILES_SRC := $(SFILES:.s=.o) $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
@ -99,10 +98,10 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
.PHONY: all dist-bin dist-src dist install clean
#---------------------------------------------------------------------------------
all: lib/libfunctionpatcher.a share/libfunctionpatcher.ld
all: lib/libfunctionpatcher.a
dist-bin: all
@tar --exclude=*~ -cjf libfunctionpatcher-$(VERSION).tar.bz2 include lib share
@tar --exclude=*~ -cjf libfunctionpatcher-$(VERSION).tar.bz2 include lib
dist-src:
@tar --exclude=*~ -cjf libfunctionpatcher-src-$(VERSION).tar.bz2 include source Makefile
@ -114,18 +113,14 @@ install: dist-bin
bzip2 -cd libfunctionpatcher-$(VERSION).tar.bz2 | tar -xf - -C $(DESTDIR)$(DEVKITPRO)/wums
lib:
@[ -d $@ ] || mkdir -p $@
share:
@[ -d $@ ] || mkdir -p $@
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
release:
@[ -d $@ ] || mkdir -p $@
share/libfunctionpatcher.ld :$(SOURCES) $(INCLUDES) | share release
mv $(CURDIR)/release/*.ld $(CURDIR)/$@
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
lib/libfunctionpatcher.a :$(SOURCES) $(INCLUDES) | lib release
@$(shell [ ! -d lib ] && mkdir -p lib)
@$(shell [ ! -d release ] && mkdir -p release)
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DNDEBUG=1 -O2 -s" \
DEPSDIR=$(CURDIR)/release \
@ -149,12 +144,6 @@ $(OUTPUT) : $(OFILES)
$(OFILES_SRC) : $(HFILES)
#---------------------------------------------------------------------------------
%.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
#---------------------------------------------------------------------------------

View File

@ -6,20 +6,13 @@ Requires [wut](https://github.com/devkitPro/wut) for building.
Install via `make install`.
## Usage
When linking, make sure to add the `libfunctionpatcher.ld` file to the LDFLAGS.
Example:
```
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld
```
Make also sure to define
```
WUMS_ROOT := $(DEVKITPRO)/wums
```
and add `-lfunctionpatcher` to `LIBS` and `$(WUMS_ROOT)` to `LIBDIRS`.
After that you can simply include `<function_patcher/function_patching.h>` to get access to the function patcher functions.
After that you can simply include `<function_patcher/function_patching.h>` to get access to the function patcher functions after calling `FunctionPatcher_InitLibrary()`.
## Use this lib in Dockerfiles.
A prebuilt version of this lib can found on dockerhub. To use it for your projects, add this to your Dockerfile.
@ -32,5 +25,4 @@ Replace [tag] with a tag you want to use, a list of tags can be found [here](htt
It's highly recommended to pin the version to the **latest date** instead of using `latest`.
## Format the code via docker
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source ./include -i`

View File

@ -1,6 +1,24 @@
#pragma once
#include <stdint.h>
#include <wut.h>
typedef enum FunctionPatcherStatus {
FUNCTION_PATCHER_RESULT_SUCCESS = 0,
FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND = -0x1,
FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT = -0x2,
FUNCTION_PATCHER_RESULT_UNSUPPORTED_VERSION = -0x3,
FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT = -0x10,
FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND = -0x11,
FUNCTION_PATCHER_RESULT_UNSUPPORTED_STRUCT_VERSION = -0x12,
FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED = -0x20,
FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND = -0x21,
FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR = -0x1000,
} FunctionPatcherStatus;
typedef uint32_t FunctionPatcherAPIVersion;
#define FUNCTION_PATCHER_MODULE_API_VERSION_ERROR 0xFFFFFFFF
typedef enum function_replacement_library_type_t {
LIBRARY_AVM,

View File

@ -1,15 +1,110 @@
#pragma once
#include "fpatching_defines.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle);
/**
* Returns a FunctionPatcherStatus as a string
* @param status
* @return String representation of a given status
**/
const char *FunctionPatcher_GetStatusStr(FunctionPatcherStatus status);
extern bool FunctionPatcherRestoreFunction(PatchedFunctionHandle outHandle);
/**
* This function has to be called before any other function of this lib (except FunctionPatcher_GetVersion) can be used.
*
* @return FUNCTION_PATCHER_RESULT_SUCCESS: The library has been initialized successfully. Other functions can now be used.<br>
* FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND: The module could not be found. Make sure the module is loaded.<br>
* FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT: The module is missing an expected export.<br>
* FUNCTION_PATCHER_RESULT_UNSUPPORTED_API_VERSION: The version of the loaded module is not compatible with this version of the lib.
**/
FunctionPatcherStatus FunctionPatcher_InitLibrary();
/**
* Deinitializes the RPXLoader lib
* @return FUNCTION_PATCHER_RESULT_SUCCESS or FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR
*/
FunctionPatcherStatus FunctionPatcher_DeInitLibrary();
/**
* Retrieves the API Version of the loaded FunctionPatcherModule.<br>
* <br>
* Requires FunctionPatcher API version 2 or higher
* @param outVersion pointer to the variable where the version will be stored.
*
* @return FUNCTION_PATCHER_RESULT_SUCCESS: The API version has been store in the version ptr.<br>
* FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND: The module could not be found. Make sure the module is loaded.<br>
* FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT: The module is missing an expected export.<br>
* FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT: Invalid version pointer.<br>
* FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR: Retrieving the module version failed.
**/
FunctionPatcherStatus FunctionPatcher_GetVersion(FunctionPatcherAPIVersion *outVersion);
/**
* Registers a patch of a function. You can use the `REPLACE_FUNCTION_XXX` macro for filling the function_replacement_data_t struct. <br>
* When adding this function it'll be attempted to apply this patch, see `outHasBeenPatched` this initial patch was successful. <br>
* If the target could not be patched when adding it (e.g. because the target library is not loaded), <br>
* the function will be patched as soon as the RPL of the library is loaded (via OSDynLoad_XXX or due to a application switch) and unloaded <br>
* if a the RPL unloads <br>
* The function patches will survive an application switches, the only way to deregister it is to call FunctionPatcher_RemoveFunctionPatch <br>
* with the PatchedFunctionHandle. <br>
* <br>
* It is possible to patch the same function multiple times, the function patches will stack. <br>
* The most recent patch to a function will be called first. real_xxx inside this patch refers then to the "previous patch"<br>
* <br>
* Requires FunctionPatcher API version 2 or higher
* @param function_data Provides information about which function should be patched in which manner. See `REPLACE_FUNCTION_XXX` macros.
* @param outHandle (optional) stores a handle of the function patch on success
* @param outHasBeenPatched (optional) stores is the initial function **patch** was successful
* @return FUNCTION_PATCHER_RESULT_SUCCESS: The function function patch has been **added**. This does **NOT** mean the function <br>
* has already been patched. See `outHasBeenPatched` if the function actually is already patched. <br>
* FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call FunctionPatcher_InitLibrary() before using this function.<br>
* FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded FunctionPatcherModule version.<br>
* FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT: The given function_data was NULL.<br>
* FUNCTION_PATCHER_RESULT_UNSUPPORTED_STRUCT_VERSION: Given function_data was in a unsupported version.<br>
**/
FunctionPatcherStatus FunctionPatcher_AddFunctionPatch(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle, bool *outHasBeenPatched);
/**
* Remove a given function patch by handle. If the function is currently patched, the function will be restored. <br>
* It is possible to to restore a function that has been patched multiple times. Other function patches of the function will <br>
* stay intact. To achieve this the target function might the restored/patched multiple times and should not be called <br>
* until this function returns. <br>
* <br>
* Requires FunctionPatcher API version 2 or higher
* @param handle The function patch that should be removed.
* @return FUNCTION_PATCHER_RESULT_SUCCESS: The function function patch has been **removed**.<br>
* FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call FunctionPatcher_InitLibrary() before using this function.<br>
* FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded FunctionPatcherModule version.<br>
* FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND: The given handle does not refer to a valid patch.<br>
**/
FunctionPatcherStatus FunctionPatcher_RemoveFunctionPatch(PatchedFunctionHandle handle);
/**
* Check if a function patch is actually applied. Functions only will be applied if the target <br>
* library is loaded.
* <br>
* Requires FunctionPatcher API version 2 or higher
* @param handle Handle of the function patch to check.
* @param outIsFunctionPatched Stores the result on success.
* @return FUNCTION_PATCHER_RESULT_SUCCESS: The result has been stored in `outIsFunctionPatched`.<br>
* FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call FunctionPatcher_InitLibrary() before using this function.<br>
* FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded FunctionPatcherModule version.<br>
* FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT: The given handle was NULL.<br>
* FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND: The given handle does not refer to a valid patch.<br>
**/
FunctionPatcherStatus FunctionPatcher_IsFunctionPatched(PatchedFunctionHandle handle, bool *outIsFunctionPatched);
WUT_DEPRECATED("Please use FunctionPatcher_AddFunctionPatch instead")
bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle);
WUT_DEPRECATED("Please use FunctionPatcher_RemoveFunctionPatch instead")
bool FunctionPatcherRestoreFunction(PatchedFunctionHandle handle);
#ifdef __cplusplus
}

23
source/logger.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <coreinit/debug.h>
#include <cstring>
#define __FILENAME__ ({ \
const char *__filename = __FILE__; \
const char *__pos = strrchr(__filename, '/'); \
if (!__pos) __pos = strrchr(__filename, '\\'); \
__pos ? __pos + 1 : __filename; \
})
#define LOG_APP_TYPE "L"
#define LOG_APP_NAME "libfunctionpatcher"
#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)
#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 DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARNING## ", "\n", FMT, ##ARGS)

View File

@ -1,5 +0,0 @@
:NAME homebrew_functionpatcher
:TEXT
FunctionPatcherPatchFunction
FunctionPatcherRestoreFunction

180
source/utils.cpp Normal file
View File

@ -0,0 +1,180 @@
#include "logger.h"
#include <coreinit/debug.h>
#include <coreinit/dynload.h>
#include <function_patcher/function_patching.h>
static OSDynLoad_Module sModuleHandle = nullptr;
// Introduced in API version 2.
static FunctionPatcherStatus (*sFPGetVersion)(FunctionPatcherAPIVersion *) = nullptr;
static FunctionPatcherStatus (*sFPAddFunctionPatch)(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle, bool *outHasBeenPatched) = nullptr;
static FunctionPatcherStatus (*sFPRemoveFunctionPatch)(PatchedFunctionHandle handle) = nullptr;
static FunctionPatcherStatus (*sFPIsFunctionPatched)(PatchedFunctionHandle handle, bool *outIsPatched) = nullptr;
// Deprecated functions
static bool (*sFunctionPatcherPatchFunction)(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle) = nullptr;
static bool (*sFunctionPatcherRestoreFunction)(PatchedFunctionHandle handle) = nullptr;
static FunctionPatcherAPIVersion sFunctionPatcherVersion = FUNCTION_PATCHER_MODULE_API_VERSION_ERROR;
const char *FunctionPatcher_GetStatusStr(FunctionPatcherStatus status) {
switch (status) {
case FUNCTION_PATCHER_RESULT_SUCCESS:
return "FUNCTION_PATCHER_RESULT_SUCCESS";
case FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND:
return "FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND";
case FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT:
return "FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT";
case FUNCTION_PATCHER_RESULT_UNSUPPORTED_VERSION:
return "FUNCTION_PATCHER_RESULT_UNSUPPORTED_VERSION";
case FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT:
return "FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT";
case FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED:
return "FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED";
case FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND:
return "FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND";
case FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR:
return "FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR";
case FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND:
return "FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND";
case FUNCTION_PATCHER_RESULT_UNSUPPORTED_STRUCT_VERSION:
return "FUNCTION_PATCHER_RESULT_UNSUPPORTED_STRUCT_VERSION";
}
return "FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR";
}
FunctionPatcherStatus FunctionPatcher_InitLibrary() {
if (OSDynLoad_Acquire("homebrew_functionpatcher", &sModuleHandle) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_ERR("OSDynLoad_Acquire failed.");
return FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND;
}
if (OSDynLoad_FindExport(sModuleHandle, FALSE, "FPGetVersion", (void **) &sFPGetVersion) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_ERR("FindExport FPGetVersion failed.");
return FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT;
}
auto res = FunctionPatcher_GetVersion(&sFunctionPatcherVersion);
if (res != FUNCTION_PATCHER_RESULT_SUCCESS) {
sFunctionPatcherVersion = FUNCTION_PATCHER_MODULE_API_VERSION_ERROR;
return FUNCTION_PATCHER_RESULT_UNSUPPORTED_VERSION;
}
if (OSDynLoad_FindExport(sModuleHandle, FALSE, "FPAddFunctionPatch", (void **) &sFPAddFunctionPatch) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_ERR("FindExport FPAddFunctionPatch failed.");
sFPAddFunctionPatch = nullptr;
}
if (OSDynLoad_FindExport(sModuleHandle, FALSE, "FPRemoveFunctionPatch", (void **) &sFPRemoveFunctionPatch) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_ERR("FindExport FPRemoveFunctionPatch failed.");
sFPRemoveFunctionPatch = nullptr;
}
if (OSDynLoad_FindExport(sModuleHandle, FALSE, "FPIsFunctionPatched", (void **) &sFPIsFunctionPatched) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_ERR("FindExport FPIsFunctionPatched failed.");
sFPIsFunctionPatched = nullptr;
}
return FUNCTION_PATCHER_RESULT_SUCCESS;
}
FunctionPatcherStatus FunctionPatcher_DeInitLibrary() {
sFPAddFunctionPatch = nullptr;
sFPRemoveFunctionPatch = nullptr;
sFPIsFunctionPatched = nullptr;
sFunctionPatcherVersion = FUNCTION_PATCHER_MODULE_API_VERSION_ERROR;
OSDynLoad_Release(sModuleHandle);
sModuleHandle = nullptr;
return FUNCTION_PATCHER_RESULT_SUCCESS;
}
bool FunctionPatcherPatchFunctionDecl(function_replacement_data_t *, PatchedFunctionHandle *);
bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle) {
if (sFunctionPatcherPatchFunction == nullptr) {
if (OSDynLoad_Acquire("homebrew_functionpatcher", &sModuleHandle) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_Acquire failed.");
return false;
}
if (OSDynLoad_FindExport(sModuleHandle, FALSE, "FunctionPatcherPatchFunction", (void **) &sFunctionPatcherPatchFunction) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("FindExport FunctionPatcherPatchFunction failed.");
return false;
}
}
return reinterpret_cast<decltype(&FunctionPatcherPatchFunctionDecl)>(sFunctionPatcherPatchFunction)(function_data, outHandle);
}
bool FunctionPatcherRestoreFunctionDecl(PatchedFunctionHandle);
bool FunctionPatcherRestoreFunction(PatchedFunctionHandle handle) {
if (sFunctionPatcherRestoreFunction == nullptr) {
if (OSDynLoad_Acquire("homebrew_functionpatcher", &sModuleHandle) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_Acquire failed.");
return false;
}
if (OSDynLoad_FindExport(sModuleHandle, FALSE, "FunctionPatcherRestoreFunction", (void **) &sFunctionPatcherRestoreFunction) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("FindExport FunctionPatcherRestoreFunction failed.");
return false;
}
}
return reinterpret_cast<decltype(&FunctionPatcherRestoreFunctionDecl)>(sFunctionPatcherRestoreFunction)(handle);
}
FunctionPatcherStatus FunctionPatcher_GetVersion(FunctionPatcherAPIVersion *outVersion) {
if (sFPGetVersion == nullptr) {
if (OSDynLoad_Acquire("homebrew_functionpatcher", &sModuleHandle) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("OSDynLoad_Acquire failed.");
return FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND;
}
if (OSDynLoad_FindExport(sModuleHandle, FALSE, "FPGetVersion", (void **) &sFPGetVersion) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_WARN("FindExport FPGetVersion failed.");
return FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT;
}
}
return reinterpret_cast<decltype(&FunctionPatcher_GetVersion)>(sFPGetVersion)(outVersion);
}
FunctionPatcherStatus FunctionPatcher_AddFunctionPatch(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle, bool *outHasBeenPatched) {
if (sFunctionPatcherVersion == FUNCTION_PATCHER_MODULE_API_VERSION_ERROR) {
return FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED;
}
if (sFPAddFunctionPatch == nullptr || sFunctionPatcherVersion < 2) {
return FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND;
}
if (function_data == nullptr) {
return FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT;
}
return reinterpret_cast<decltype(&FunctionPatcher_AddFunctionPatch)>(sFPAddFunctionPatch)(function_data, outHandle, outHasBeenPatched);
}
FunctionPatcherStatus FunctionPatcher_RemoveFunctionPatch(PatchedFunctionHandle handle) {
if (sFunctionPatcherVersion == FUNCTION_PATCHER_MODULE_API_VERSION_ERROR) {
return FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED;
}
if (sFPRemoveFunctionPatch == nullptr || sFunctionPatcherVersion < 2) {
return FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND;
}
if (handle == 0) {
return FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT;
}
return reinterpret_cast<decltype(&FunctionPatcher_RemoveFunctionPatch)>(sFPRemoveFunctionPatch)(handle);
}
FunctionPatcherStatus FunctionPatcher_IsFunctionPatched(PatchedFunctionHandle handle, bool *outIsFunctionPatched) {
if (sFunctionPatcherVersion == FUNCTION_PATCHER_MODULE_API_VERSION_ERROR) {
return FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED;
}
if (sFPIsFunctionPatched == nullptr || sFunctionPatcherVersion < 2) {
return FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND;
}
if (handle == 0 || outIsFunctionPatched == nullptr) {
return FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT;
}
return reinterpret_cast<decltype(&FunctionPatcher_IsFunctionPatched)>(sFPIsFunctionPatched)(handle, outIsFunctionPatched);
}