Files
2025-05-18 13:04:45 +08:00

499 lines
19 KiB
C

// Copyright 2011-2020 Molecular Matters GmbH, all rights reserved.
#pragma once
#if LC_VERSION == 1
/******************************************************************************/
/* HOOKS */
/******************************************************************************/
// concatenates two preprocessor tokens, even when the tokens themselves are macros
#define LPP_CONCATENATE_HELPER_HELPER(_a, _b) _a##_b
#define LPP_CONCATENATE_HELPER(_a, _b) LPP_CONCATENATE_HELPER_HELPER(_a, _b)
#define LPP_CONCATENATE(_a, _b) LPP_CONCATENATE_HELPER(_a, _b)
// generates a unique identifier inside a translation unit
#define LPP_IDENTIFIER(_identifier) LPP_CONCATENATE(_identifier, __LINE__)
// custom section names for hooks
#define LPP_PREPATCH_SECTION ".lpp_prepatch_hooks"
#define LPP_POSTPATCH_SECTION ".lpp_postpatch_hooks"
#define LPP_COMPILE_START_SECTION ".lpp_compile_start_hooks"
#define LPP_COMPILE_SUCCESS_SECTION ".lpp_compile_success_hooks"
#define LPP_COMPILE_ERROR_SECTION ".lpp_compile_error_hooks"
#define LPP_COMPILE_ERROR_MESSAGE_SECTION ".lpp_compile_error_message_hooks"
// BEGIN EPIC MOD - Add the ability for pre and post compile notifications
#define LPP_PRECOMPILE_SECTION ".lpp_precompile_hooks"
#define LPP_POSTCOMPILE_SECTION ".lpp_postcompile_hooks"
// END EPIC MOD
// register a pre-patch hook in a custom section
#define LPP_PREPATCH_HOOK(_function) \
__pragma(section(LPP_PREPATCH_SECTION, read)) __declspec(allocate(LPP_PREPATCH_SECTION)) extern void (*LPP_IDENTIFIER(lpp_prepatch_hook_function))(void); \
__pragma(section(LPP_PREPATCH_SECTION, read)) __declspec(allocate(LPP_PREPATCH_SECTION)) void (*LPP_IDENTIFIER(lpp_prepatch_hook_function))(void) = &_function
// register a post-patch hook in a custom section
#define LPP_POSTPATCH_HOOK(_function) \
__pragma(section(LPP_POSTPATCH_SECTION, read)) __declspec(allocate(LPP_POSTPATCH_SECTION)) extern void (*LPP_IDENTIFIER(lpp_postpatch_hook_function))(void); \
__pragma(section(LPP_POSTPATCH_SECTION, read)) __declspec(allocate(LPP_POSTPATCH_SECTION)) void (*LPP_IDENTIFIER(lpp_postpatch_hook_function))(void) = &_function
// register a compile start hook in a custom section
#define LPP_COMPILE_START_HOOK(_function) \
__pragma(section(LPP_COMPILE_START_SECTION, read)) __declspec(allocate(LPP_COMPILE_START_SECTION)) extern void (*LPP_IDENTIFIER(lpp_compile_start_hook_function))(void); \
__pragma(section(LPP_COMPILE_START_SECTION, read)) __declspec(allocate(LPP_COMPILE_START_SECTION)) void (*LPP_IDENTIFIER(lpp_compile_start_hook_function))(void) = &_function
// register a compile success hook in a custom section
#define LPP_COMPILE_SUCCESS_HOOK(_function) \
__pragma(section(LPP_COMPILE_SUCCESS_SECTION, read)) __declspec(allocate(LPP_COMPILE_SUCCESS_SECTION)) extern void (*LPP_IDENTIFIER(lpp_compile_success_hook_function))(void); \
__pragma(section(LPP_COMPILE_SUCCESS_SECTION, read)) __declspec(allocate(LPP_COMPILE_SUCCESS_SECTION)) void (*LPP_IDENTIFIER(lpp_compile_success_hook_function))(void) = &_function
// register a compile error hook in a custom section
#define LPP_COMPILE_ERROR_HOOK(_function) \
__pragma(section(LPP_COMPILE_ERROR_SECTION, read)) __declspec(allocate(LPP_COMPILE_ERROR_SECTION)) extern void (*LPP_IDENTIFIER(lpp_compile_error_hook_function))(void); \
__pragma(section(LPP_COMPILE_ERROR_SECTION, read)) __declspec(allocate(LPP_COMPILE_ERROR_SECTION)) void (*LPP_IDENTIFIER(lpp_compile_error_hook_function))(void) = &_function
// register a compile error message hook in a custom section
#define LPP_COMPILE_ERROR_MESSAGE_HOOK(_function) \
__pragma(section(LPP_COMPILE_ERROR_MESSAGE_SECTION, read)) __declspec(allocate(LPP_COMPILE_ERROR_MESSAGE_SECTION)) extern void (*LPP_IDENTIFIER(lpp_compile_error_message_hook_function))(const wchar_t*); \
__pragma(section(LPP_COMPILE_ERROR_MESSAGE_SECTION, read)) __declspec(allocate(LPP_COMPILE_ERROR_MESSAGE_SECTION)) void (*LPP_IDENTIFIER(lpp_compile_error_message_hook_function))(const wchar_t*) = &_function
// BEGIN EPIC MOD - Add the ability for pre and post compile notifications
// register a pre-compile hook in a custom section
#define LPP_PRECOMPILE_HOOK(_function) \
__pragma(section(LPP_PRECOMPILE_SECTION, read)) \
__declspec(allocate(LPP_PRECOMPILE_SECTION)) extern void (*LPP_IDENTIFIER(lpp_precompile_hook_function))(void) = &_function
// register a post-compile hook in a custom section
#define LPP_POSTCOMPILE_HOOK(_function) \
__pragma(section(LPP_POSTCOMPILE_SECTION, read)) \
__declspec(allocate(LPP_POSTCOMPILE_SECTION)) extern void (*LPP_IDENTIFIER(lpp_postcompile_hook_function))(void) = &_function
// END EPIC MOD
/******************************************************************************/
/* HELPERS */
/******************************************************************************/
#ifdef __cplusplus
# define LPP_NS_BEGIN namespace lpp {
# define LPP_NS_END }
# define LPP_API inline
# define LPP_CAST(_type) reinterpret_cast<_type>
# define LPP_NULL nullptr
#else
# define LPP_NS_BEGIN
# define LPP_NS_END
# define LPP_API static inline
# define LPP_CAST(_type) (_type)
# define LPP_NULL NULL
#endif
// helper macros to call a function in a DLL with an arbitrary signature without a compiler warning
#define LPP_CALL1(_module, _functionName, _returnType, _args1) (LPP_CAST(_returnType (__cdecl*)(_args1))(LPP_CAST(uintptr_t)(GetProcAddress(_module, _functionName))))
#define LPP_CALL2(_module, _functionName, _returnType, _args1, _args2) (LPP_CAST(_returnType (__cdecl*)(_args1, _args2))(LPP_CAST(uintptr_t)(GetProcAddress(_module, _functionName))))
#define LPP_CALL3(_module, _functionName, _returnType, _args1, _args2, _args3) (LPP_CAST(_returnType (__cdecl*)(_args1, _args2, _args3))(LPP_CAST(uintptr_t)(GetProcAddress(_module, _functionName))))
#define LPP_CALL4(_module, _functionName, _returnType, _args1, _args2, _args3, _args4) (LPP_CAST(_returnType (__cdecl*)(_args1, _args2, _args3, _args4))(LPP_CAST(uintptr_t)(GetProcAddress(_module, _functionName))))
// linker pseudo-variable representing the DOS header of the module we're being compiled into:
// Raymond Chen: https://blogs.msdn.microsoft.com/oldnewthing/20041025-00/?p=37483
// BEGIN EPIC MOD
//EXTERN_C IMAGE_DOS_HEADER __ImageBase;
// END EPIC MOD
/******************************************************************************/
/* API */
/******************************************************************************/
// version string
#define LPP_VERSION "1.6.10"
// macros to temporarily enable/disable optimizations
#ifdef __clang__
# define LPP_ENABLE_OPTIMIZATIONS _Pragma("clang optimize on")
# define LPP_DISABLE_OPTIMIZATIONS _Pragma("clang optimize off")
#else
# define LPP_ENABLE_OPTIMIZATIONS __pragma(optimize("", on))
# define LPP_DISABLE_OPTIMIZATIONS __pragma(optimize("", off))
#endif
LPP_NS_BEGIN
// BEGIN EPIC MOD
#if 0
// starts up Live++. must be called after loading the Live++ DLL, before registering a process group.
LPP_API void lppStartup(HMODULE livePP)
{
return LPP_CALL1(livePP, "LppStartup", void, void)();
}
// shuts down Live++. must be called before unloading the Live++ DLL.
LPP_API void lppShutdown(HMODULE livePP)
{
return LPP_CALL1(livePP, "LppShutdown", void, void)();
}
// retrieves the version number of the DLL.
LPP_API const char* lppGetVersion(HMODULE livePP)
{
return LPP_CALL1(livePP, "LppGetVersion", const char*, void)();
}
// checks whether the API and DLL versions match.
// returns 1 on success, 0 on failure.
LPP_API int lppCheckVersion(HMODULE livePP)
{
return LPP_CALL1(livePP, "LppCheckVersion", int, const char*)(LPP_VERSION);
}
// registers a Live++ process group.
LPP_API void lppRegisterProcessGroup(HMODULE livePP, const char* groupName)
{
LPP_CALL1(livePP, "LppRegisterProcessGroup", void, const char*)(groupName);
}
// calls the synchronization point.
LPP_API void lppSyncPoint(HMODULE livePP)
{
LPP_CALL1(livePP, "LppSyncPoint", void, void)();
}
// waits until the operation identified by the given token is finished.
LPP_API void lppWaitForToken(HMODULE livePP, void* token)
{
LPP_CALL1(livePP, "LppWaitForToken", void, void*)(token);
}
// triggers a recompile.
LPP_API void lppTriggerRecompile(HMODULE livePP)
{
LPP_CALL1(livePP, "LppTriggerRecompile", void, void)();
}
// logs a message in the Live+ UI.
LPP_API void lppLogMessage(HMODULE livePP, const wchar_t* message)
{
LPP_CALL1(livePP, "LppLogMessage", void, const wchar_t*)(message);
}
// builds a patch using the given object files.
LPP_API void lppBuildPatch(HMODULE livePP, const wchar_t* moduleNames[], const wchar_t* objPaths[], const wchar_t* amalgamatedObjPaths[], unsigned int count)
{
LPP_CALL4(livePP, "LppBuildPatch", void, const wchar_t*[], const wchar_t*[], const wchar_t*[], unsigned int)(moduleNames, objPaths, amalgamatedObjPaths, count);
}
// installs Live++'s exception handler.
LPP_API void lppInstallExceptionHandler(HMODULE livePP)
{
LPP_CALL1(livePP, "LppInstallExceptionHandler", void, void)();
}
// makes Live++ listen to changed .obj files compiled by an external build system.
LPP_API void lppUseExternalBuildSystem(HMODULE livePP)
{
LPP_CALL1(livePP, "LppUseExternalBuildSystem", void, void)();
}
// triggers a restart.
LPP_API void lppTriggerRestart(HMODULE livePP)
{
LPP_CALL1(livePP, "LppTriggerRestart", int, void)();
}
// returns 1 if the calling process wants to be restarted.
LPP_API int lppWantsRestart(HMODULE livePP)
{
return LPP_CALL1(livePP, "LppWantsRestart", int, void)();
}
#endif
// END EPIC MOD
enum RestartBehaviour
{
// BEGIN EPIC MODS - Use UE codepath for termination to ensure logs are flushed and session analytics are sent
LPP_RESTART_BEHAVIOR_REQUEST_EXIT, // FPlatforMisc::RequestExit(true)
// END EPIC MODS
LPP_RESTART_BEHAVIOUR_DEFAULT_EXIT, // ExitProcess()
LPP_RESTART_BEHAVIOUR_EXIT_WITH_FLUSH, // exit()
LPP_RESTART_BEHAVIOUR_EXIT, // _Exit()
LPP_RESTART_BEHAVIOUR_INSTANT_TERMINATION // TerminateProcess
};
// BEGIN EPIC MOD
#if 0
// restarts the calling process, does not return.
LPP_API void lppRestart(HMODULE livePP, enum RestartBehaviour behaviour, unsigned int exitCode)
{
LPP_CALL2(livePP, "LppRestart", void, enum RestartBehaviour, unsigned int)(behaviour, exitCode);
}
// asynchronously enables Live++ for just the given .exe or .dll, but not its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppEnableModuleAsync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
return LPP_CALL1(livePP, "LppEnableModule", void*, const wchar_t*)(nameOfExeOrDll);
}
// asynchronously enables Live++ for all given .exes and .dlls, but not its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppEnableModulesAsync(HMODULE livePP, const wchar_t* namesOfExeOrDll[], unsigned int count)
{
return LPP_CALL2(livePP, "LppEnableModules", void*, const wchar_t*[], unsigned int)(namesOfExeOrDll, count);
}
// asynchronously enables Live++ for the given .exe or .dll and all its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppEnableAllModulesAsync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
return LPP_CALL1(livePP, "LppEnableAllModules", void*, const wchar_t*)(nameOfExeOrDll);
}
// asynchronously enables Live++ for just the .exe or .dll this function is being called from, but not its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppEnableCallingModuleAsync(HMODULE livePP)
{
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileNameW(LPP_CAST(HMODULE)(&__ImageBase), path, MAX_PATH);
return lppEnableModuleAsync(livePP, path);
}
// asynchronously enables Live++ for the .exe or .dll this function is being called from and all its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppEnableAllCallingModulesAsync(HMODULE livePP)
{
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileNameW(LPP_CAST(HMODULE)(&__ImageBase), path, MAX_PATH);
return lppEnableAllModulesAsync(livePP, path);
}
// synchronously enables Live++ for just the given .exe or .dll, but not its import modules.
LPP_API void lppEnableModuleSync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
void* token = lppEnableModuleAsync(livePP, nameOfExeOrDll);
lppWaitForToken(livePP, token);
}
// synchronously enables Live++ for all given .exes and .dlls, but not its import modules.
LPP_API void lppEnableModulesSync(HMODULE livePP, const wchar_t* namesOfExeOrDll[], unsigned int count)
{
void* token = lppEnableModulesAsync(livePP, namesOfExeOrDll, count);
lppWaitForToken(livePP, token);
}
// synchronously enables Live++ for the given .exe or .dll and all its import modules.
LPP_API void lppEnableAllModulesSync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
void* token = lppEnableAllModulesAsync(livePP, nameOfExeOrDll);
lppWaitForToken(livePP, token);
}
// synchronously enables Live++ for just the .exe or .dll this function is being called from, but not its import modules.
LPP_API void lppEnableCallingModuleSync(HMODULE livePP)
{
void* token = lppEnableCallingModuleAsync(livePP);
lppWaitForToken(livePP, token);
}
// synchronously enables Live++ for the .exe or .dll this function is being called from and all its import modules.
LPP_API void lppEnableAllCallingModulesSync(HMODULE livePP)
{
void* token = lppEnableAllCallingModulesAsync(livePP);
lppWaitForToken(livePP, token);
}
// asynchronously disables Live++ for just the given .exe or .dll, but not its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppDisableModuleAsync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
return LPP_CALL1(livePP, "LppDisableModule", void*, const wchar_t*)(nameOfExeOrDll);
}
// asynchronously disables Live++ for all given .exes and .dlls, but not its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppDisableModulesAsync(HMODULE livePP, const wchar_t* namesOfExeOrDll[], unsigned int count)
{
return LPP_CALL2(livePP, "LppDisableModules", void*, const wchar_t*[], unsigned int)(namesOfExeOrDll, count);
}
// asynchronously disables Live++ for the given .exe or .dll and all its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppDisableAllModulesAsync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
return LPP_CALL1(livePP, "LppDisableAllModules", void*, const wchar_t*)(nameOfExeOrDll);
}
// asynchronously disables Live++ for just the .exe or .dll this function is being called from, but not its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppDisableCallingModuleAsync(HMODULE livePP)
{
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileNameW(LPP_CAST(HMODULE)(&__ImageBase), path, MAX_PATH);
return lppDisableModuleAsync(livePP, path);
}
// asynchronously disables Live++ for the .exe or .dll this function is being called from and all its import modules.
// returns a token that can be waited upon using lppWaitForToken().
LPP_API void* lppDisableAllCallingModulesAsync(HMODULE livePP)
{
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileNameW(LPP_CAST(HMODULE)(&__ImageBase), path, MAX_PATH);
return lppDisableAllModulesAsync(livePP, path);
}
// synchronously disables Live++ for just the given .exe or .dll, but not its import modules.
LPP_API void lppDisableModuleSync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
void* token = lppDisableModuleAsync(livePP, nameOfExeOrDll);
lppWaitForToken(livePP, token);
}
// synchronously disables Live++ for all given .exes and .dlls, but not its import modules.
LPP_API void lppDisableModulesSync(HMODULE livePP, const wchar_t* namesOfExeOrDll[], unsigned int count)
{
void* token = lppDisableModulesAsync(livePP, namesOfExeOrDll, count);
lppWaitForToken(livePP, token);
}
// synchronously disables Live++ for the given .exe or .dll and all its import modules.
LPP_API void lppDisableAllModulesSync(HMODULE livePP, const wchar_t* nameOfExeOrDll)
{
void* token = lppDisableAllModulesAsync(livePP, nameOfExeOrDll);
lppWaitForToken(livePP, token);
}
// synchronously disables Live++ for just the .exe or .dll this function is being called from, but not its import modules.
LPP_API void lppDisableCallingModuleSync(HMODULE livePP)
{
void* token = lppDisableCallingModuleAsync(livePP);
lppWaitForToken(livePP, token);
}
// synchronously disables Live++ for the .exe or .dll this function is being called from and all its import modules.
LPP_API void lppDisableAllCallingModulesSync(HMODULE livePP)
{
void* token = lppDisableAllCallingModulesAsync(livePP);
lppWaitForToken(livePP, token);
}
// sets any boolean API setting. check the documentation for a full list of available settings.
LPP_API void lppApplySettingBool(HMODULE livePP, const char* settingName, int value)
{
LPP_CALL2(livePP, "LppApplySettingBool", void, const char*, int)(settingName, value);
}
// sets any integer API setting. check the documentation for a full list of available settings.
LPP_API void lppApplySettingInt(HMODULE livePP, const char* settingName, int value)
{
LPP_CALL2(livePP, "LppApplySettingInt", void, const char*, int)(settingName, value);
}
// sets any string API setting. check the documentation for a full list of available settings.
LPP_API void lppApplySettingString(HMODULE livePP, const char* settingName, const wchar_t* value)
{
LPP_CALL2(livePP, "LppApplySettingString", void, const char*, const wchar_t*)(settingName, value);
}
// loads the correct 32-bit/64-bit DLL (based on architecture), checks if the versions match, and registers a Live++ process group.
LPP_API HMODULE lppLoadAndRegister(const wchar_t* pathWithoutTrailingSlash, const char* groupName)
{
wchar_t path[1024] = { 0 };
// we deliberately do not use memcpy or strcpy here, as that could force more #includes in the user's code
unsigned int index = 0u;
while (*pathWithoutTrailingSlash != L'\0')
{
if (index >= 1023u)
{
// no space left in buffer for this character and a null terminator
return LPP_NULL;
}
path[index++] = *pathWithoutTrailingSlash++;
}
#ifdef _WIN64
const wchar_t* dllName = L"/x64/LPP_x64.dll";
#else
const wchar_t* dllName = L"/x86/LPP_x86.dll";
#endif
while (*dllName != L'\0')
{
if (index >= 1023u)
{
// no space left in buffer for this character and a null terminator
return LPP_NULL;
}
path[index++] = *dllName++;
}
path[index++] = L'\0';
HMODULE livePP = LoadLibraryW(path);
if (livePP)
{
if (!lppCheckVersion(livePP))
{
// version mismatch detected
FreeLibrary(livePP);
return LPP_NULL;
}
lppStartup(livePP);
lppRegisterProcessGroup(livePP, groupName);
}
return livePP;
}
#endif
// END EPIC MOD
LPP_NS_END
#undef LPP_CALL1
#undef LPP_CALL2
#undef LPP_CALL3
#undef LPP_CALL4
#endif // LC_VERSION