diff --git a/Makefile b/Makefile index 804aa2a..af7511a 100644 --- a/Makefile +++ b/Makefile @@ -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 #--------------------------------------------------------------------------------- diff --git a/README.md b/README.md index 227d443..323d1f2 100644 --- a/README.md +++ b/README.md @@ -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 `` to get access to the function patcher functions. +After that you can simply include `` 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` diff --git a/include/function_patcher/fpatching_defines.h b/include/function_patcher/fpatching_defines.h index 833b270..6be40ff 100644 --- a/include/function_patcher/fpatching_defines.h +++ b/include/function_patcher/fpatching_defines.h @@ -1,6 +1,24 @@ #pragma once #include +#include + +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, diff --git a/include/function_patcher/function_patching.h b/include/function_patcher/function_patching.h index ef3c7a8..eba025f 100644 --- a/include/function_patcher/function_patching.h +++ b/include/function_patcher/function_patching.h @@ -1,15 +1,110 @@ #pragma once #include "fpatching_defines.h" +#include #include #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.
+ * FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND: The module could not be found. Make sure the module is loaded.
+ * FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT: The module is missing an expected export.
+ * 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.
+ *
+ * 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.
+ * FUNCTION_PATCHER_RESULT_MODULE_NOT_FOUND: The module could not be found. Make sure the module is loaded.
+ * FUNCTION_PATCHER_RESULT_MODULE_MISSING_EXPORT: The module is missing an expected export.
+ * FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT: Invalid version pointer.
+ * 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.
+ * When adding this function it'll be attempted to apply this patch, see `outHasBeenPatched` this initial patch was successful.
+ * If the target could not be patched when adding it (e.g. because the target library is not loaded),
+ * 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
+ * if a the RPL unloads
+ * The function patches will survive an application switches, the only way to deregister it is to call FunctionPatcher_RemoveFunctionPatch
+ * with the PatchedFunctionHandle.
+ *
+ * It is possible to patch the same function multiple times, the function patches will stack.
+ * The most recent patch to a function will be called first. real_xxx inside this patch refers then to the "previous patch"
+ *
+ * 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
+ * has already been patched. See `outHasBeenPatched` if the function actually is already patched.
+ * FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call FunctionPatcher_InitLibrary() before using this function.
+ * FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded FunctionPatcherModule version.
+ * FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT: The given function_data was NULL.
+ * FUNCTION_PATCHER_RESULT_UNSUPPORTED_STRUCT_VERSION: Given function_data was in a unsupported version.
+**/ +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.
+ * It is possible to to restore a function that has been patched multiple times. Other function patches of the function will
+ * stay intact. To achieve this the target function might the restored/patched multiple times and should not be called
+ * until this function returns.
+ *
+ * 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**.
+ * FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call FunctionPatcher_InitLibrary() before using this function.
+ * FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded FunctionPatcherModule version.
+ * FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND: The given handle does not refer to a valid patch.
+**/ +FunctionPatcherStatus FunctionPatcher_RemoveFunctionPatch(PatchedFunctionHandle handle); + +/** + * Check if a function patch is actually applied. Functions only will be applied if the target
+ * library is loaded. + *
+ * 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`.
+ * FUNCTION_PATCHER_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call FunctionPatcher_InitLibrary() before using this function.
+ * FUNCTION_PATCHER_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded FunctionPatcherModule version.
+ * FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT: The given handle was NULL.
+ * FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND: The given handle does not refer to a valid patch.
+**/ +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 } diff --git a/source/logger.h b/source/logger.h new file mode 100644 index 0000000..a784135 --- /dev/null +++ b/source/logger.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +#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) diff --git a/source/patching.def b/source/patching.def deleted file mode 100644 index ccc6cb4..0000000 --- a/source/patching.def +++ /dev/null @@ -1,5 +0,0 @@ -:NAME homebrew_functionpatcher - -:TEXT -FunctionPatcherPatchFunction -FunctionPatcherRestoreFunction \ No newline at end of file diff --git a/source/utils.cpp b/source/utils.cpp new file mode 100644 index 0000000..bb595e9 --- /dev/null +++ b/source/utils.cpp @@ -0,0 +1,180 @@ +#include "logger.h" +#include +#include +#include + +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(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(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(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(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(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(sFPIsFunctionPatched)(handle, outIsFunctionPatched); +}