diff --git a/include/uevr/API.h b/include/uevr/API.h index 41393f84..f56cef89 100644 --- a/include/uevr/API.h +++ b/include/uevr/API.h @@ -36,7 +36,7 @@ SOFTWARE. #define UEVR_OUT #define UEVR_PLUGIN_VERSION_MAJOR 2 -#define UEVR_PLUGIN_VERSION_MINOR 26 +#define UEVR_PLUGIN_VERSION_MINOR 27 #define UEVR_PLUGIN_VERSION_PATCH 0 #define UEVR_RENDERER_D3D11 0 @@ -211,6 +211,8 @@ typedef struct { bool (*is_drawing_ui)(); bool (*remove_callback)(void* cb); unsigned int (*get_persistent_dir)(wchar_t* buffer, unsigned int buffer_size); + int (*register_inline_hook)(void* target, void* dst, void** original); + void (*unregister_inline_hook)(int hook_id); } UEVR_PluginFunctions; typedef struct { diff --git a/src/Framework.cpp b/src/Framework.cpp index 50a26797..865409e8 100644 --- a/src/Framework.cpp +++ b/src/Framework.cpp @@ -920,6 +920,15 @@ void Framework::on_frontend_command(UEVRSharedMemory::Command command) { break; case UEVRSharedMemory::Command::CONFIG_SETUP_ACKNOWLEDGED: m_uevr_shared_memory->data().signal_frontend_config_setup = false; + break; + case UEVRSharedMemory::Command::QUIT: + if (m_wnd != nullptr) { + PostMessageA(m_wnd, WM_CLOSE, 0, 0); + PostMessageA(m_wnd, WM_DESTROY, 0, 0); + PostMessageA(m_wnd, WM_QUIT, 0, 0); + m_terminating = true; + } + break; default: spdlog::error("Unknown frontend command received: {}", (int)command); diff --git a/src/Framework.hpp b/src/Framework.hpp index 400915f9..8367947e 100644 --- a/src/Framework.hpp +++ b/src/Framework.hpp @@ -29,6 +29,7 @@ class UEVRSharedMemory { enum Command { RELOAD_CONFIG = 0, CONFIG_SETUP_ACKNOWLEDGED = 1, + QUIT = 2, }; public: diff --git a/src/mods/PluginLoader.cpp b/src/mods/PluginLoader.cpp index 54ee5078..f04984eb 100644 --- a/src/mods/PluginLoader.cpp +++ b/src/mods/PluginLoader.cpp @@ -88,6 +88,27 @@ unsigned int get_persistent_dir(wchar_t* buffer, unsigned int buffer_size) { return (unsigned int)size; } + +int register_inline_hook(void* target, void* dst, void** original) { + if (target == nullptr || dst == nullptr || original == nullptr) { + return -1; + } + + auto hook = safetyhook::create_inline(target, dst); + + if (!hook) { + spdlog::error("Failed to create inline hook at {:x}", (uintptr_t)target); + return -1; + } + + *original = hook.original(); + + return PluginLoader::get()->add_inline_hook(std::move(hook)); +} + +void unregister_inline_hook(int id) { + PluginLoader::get()->remove_inline_hook(id); +} } namespace uevr { @@ -164,7 +185,9 @@ UEVR_PluginFunctions g_plugin_functions { uevr::log_info, uevr::is_drawing_ui, uevr::remove_callback, - uevr::get_persistent_dir + uevr::get_persistent_dir, + uevr::register_inline_hook, + uevr::unregister_inline_hook }; #define GET_ENGINE_WORLD_RETNULL() \ @@ -1598,6 +1621,7 @@ void PluginLoader::attempt_unload_plugins() { FreeLibrary(pair.second); } + m_inline_hooks.clear(); m_plugins.clear(); } diff --git a/src/mods/PluginLoader.hpp b/src/mods/PluginLoader.hpp index 9aafc4e3..802d99da 100644 --- a/src/mods/PluginLoader.hpp +++ b/src/mods/PluginLoader.hpp @@ -6,6 +6,8 @@ #include +#include + #include "Mod.hpp" #include "uevr/API.h" @@ -98,6 +100,30 @@ class PluginLoader : public Mod { return true; } + size_t add_inline_hook(safetyhook::InlineHook&& hook) { + std::scoped_lock lock{m_mux}; + + auto state = std::make_unique(std::move(hook)); + m_inline_hooks[++m_inline_hook_idx] = std::move(state); + + return m_inline_hook_idx; + } + + void remove_inline_hook(size_t idx) { + std::scoped_lock lock{m_mux}; + + if (!m_inline_hooks.contains(idx)) { + return; + } + + { + std::scoped_lock _{m_inline_hooks[idx]->mux}; + m_inline_hooks[idx]->hook.reset(); + } + + m_inline_hooks.erase(idx); + } + private: std::shared_mutex m_api_cb_mtx; std::vector m_on_present_cbs{}; @@ -149,4 +175,18 @@ class PluginLoader : public Mod { std::map m_plugins{}; std::map m_plugin_load_errors{}; std::map m_plugin_load_warnings{}; + + struct InlineHookState { + InlineHookState(safetyhook::InlineHook&& hook) + : hook{std::move(hook)} + { + } + + safetyhook::InlineHook hook{}; + std::mutex mux{}; + }; + + //std::vector m_inline_hooks{}; + std::unordered_map> m_inline_hooks{}; + size_t m_inline_hook_idx{0}; };