From a34f6de704081980d93b27b5947b424c0532c4aa Mon Sep 17 00:00:00 2001 From: praydog Date: Sat, 22 Jun 2024 05:20:37 -0700 Subject: [PATCH 1/2] Framework: Add support for QUIT command --- src/Framework.cpp | 9 +++++++++ src/Framework.hpp | 1 + 2 files changed, 10 insertions(+) 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: From 3d6fb3bb8dd5753f1ad5c53eea97bc5f5186ac79 Mon Sep 17 00:00:00 2001 From: praydog Date: Fri, 5 Jul 2024 04:26:59 -0700 Subject: [PATCH 2/2] Plugins: Add register_inline_hook/unregister_inline_hook --- include/uevr/API.h | 4 +++- src/mods/PluginLoader.cpp | 26 ++++++++++++++++++++++++- src/mods/PluginLoader.hpp | 40 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/include/uevr/API.h b/include/uevr/API.h index 8d1f918c..2b0b3761 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 24 +#define UEVR_PLUGIN_VERSION_MINOR 27 #define UEVR_PLUGIN_VERSION_PATCH 0 #define UEVR_RENDERER_D3D11 0 @@ -207,6 +207,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/mods/PluginLoader.cpp b/src/mods/PluginLoader.cpp index 354f3d66..3634b24e 100644 --- a/src/mods/PluginLoader.cpp +++ b/src/mods/PluginLoader.cpp @@ -86,6 +86,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 { @@ -162,7 +183,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() \ @@ -1566,6 +1589,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 18d7fa32..dda46090 100644 --- a/src/mods/PluginLoader.hpp +++ b/src/mods/PluginLoader.hpp @@ -6,6 +6,8 @@ #include +#include + #include "Mod.hpp" #include "uevr/API.h" @@ -96,6 +98,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{}; @@ -145,4 +171,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}; };