From 3090b9a0293a1f926f737ac3e37c06f3787946ea Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 8 Feb 2025 23:19:49 +0100 Subject: [PATCH] feat: Add bt d3d9 hook specific imgui overlay (#318) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now, we focus on internal overlays for bemanitools that hook into an existing d3d9 context. The current abstraction is fairly thin and should be fine as a start. Implementations are called “components” and hook up with a single frame_update function to execute and logic and drawing updates with imgui --- Module.mk | 1 + src/main/imgui-bt/Module.mk | 10 ++ src/main/imgui-bt/cimgui.h | 10 ++ src/main/imgui-bt/component.h | 12 +++ src/main/imgui-bt/imgui-d3d9-hook.c | 143 ++++++++++++++++++++++++++++ src/main/imgui-bt/imgui-d3d9-hook.h | 13 +++ 6 files changed, 189 insertions(+) create mode 100644 src/main/imgui-bt/Module.mk create mode 100644 src/main/imgui-bt/cimgui.h create mode 100644 src/main/imgui-bt/component.h create mode 100644 src/main/imgui-bt/imgui-d3d9-hook.c create mode 100644 src/main/imgui-bt/imgui-d3d9-hook.h diff --git a/Module.mk b/Module.mk index 6d62824..edd5f82 100644 --- a/Module.mk +++ b/Module.mk @@ -161,6 +161,7 @@ include src/main/iidxio-ezusb2/Module.mk include src/main/iidxio/Module.mk include src/main/iidxiotest/Module.mk include src/main/imgui/Module.mk +include src/main/imgui-bt/Module.mk include src/main/inject/Module.mk include src/main/jbio-magicbox/Module.mk include src/main/jbio-p4io/Module.mk diff --git a/src/main/imgui-bt/Module.mk b/src/main/imgui-bt/Module.mk new file mode 100644 index 0000000..fcf1676 --- /dev/null +++ b/src/main/imgui-bt/Module.mk @@ -0,0 +1,10 @@ +libs += imgui-bt + +libs_imgui-bt := \ + hook \ + hooklib \ + util \ + imgui \ + +src_imgui-bt := \ + imgui-d3d9-hook.c \ diff --git a/src/main/imgui-bt/cimgui.h b/src/main/imgui-bt/cimgui.h new file mode 100644 index 0000000..75b8f3e --- /dev/null +++ b/src/main/imgui-bt/cimgui.h @@ -0,0 +1,10 @@ +#ifndef IMGUI_BT_CIMGUI_H +#define IMGUI_BT_CIMGUI_H + +// Comprehensive header file for including cimgui correctly for integration in this project +#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS +#include "imgui/cimgui.h" +#include "imgui/cimgui_impl_dx9.h" +#include "imgui/cimgui_impl_win32.h" + +#endif \ No newline at end of file diff --git a/src/main/imgui-bt/component.h b/src/main/imgui-bt/component.h new file mode 100644 index 0000000..fc479a8 --- /dev/null +++ b/src/main/imgui-bt/component.h @@ -0,0 +1,12 @@ +#ifndef IMGUI_BT_COMPONENT_H +#define IMGUI_BT_COMPONENT_H + +#include "imgui-bt/cimgui.h" + +typedef void (*imgui_bt_component_frame_update_t)(ImGuiContext *ctx); + +typedef struct imgui_bt_component { + imgui_bt_component_frame_update_t frame_update; +} imgui_bt_component_t; + +#endif diff --git a/src/main/imgui-bt/imgui-d3d9-hook.c b/src/main/imgui-bt/imgui-d3d9-hook.c new file mode 100644 index 0000000..6f230fc --- /dev/null +++ b/src/main/imgui-bt/imgui-d3d9-hook.c @@ -0,0 +1,143 @@ +#define LOG_MODULE "imgui-d3d9-hook" + +#include "hook/d3d9.h" +#include "hook/table.h" + +#include "imgui-bt/cimgui.h" +#include "imgui-bt/component.h" + +#include "util/defs.h" +#include "util/log.h" +#include "util/mem.h" + +typedef LRESULT WINAPI (*RegisterClassA_t)(WNDCLASSA *lpwcx); +typedef LRESULT WINAPI (*WndProc_t)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +static LRESULT WINAPI _imgui_d3d9_hook_RegisterClassA(WNDCLASSA *lpwcx); + +static bool _imgui_d3d9_hook_initialized; +static ImGuiContext *_imgui_d3d9_hook_imgui_ctx; +static RegisterClassA_t _imgui_d3d9_hook_original_RegisterClassA; +static WndProc_t _imgui_d3d9_hook_original_wndproc; +static imgui_bt_component_t *_imgui_d3d9_hook_components; +static size_t _imgui_d3d9_hook_component_count; + +static const struct hook_symbol _imgui_d3d9_hook_syms[] = { + {.name = "RegisterClassA", + .patch = _imgui_d3d9_hook_RegisterClassA, + .link = (void **) &_imgui_d3d9_hook_original_RegisterClassA}, +}; + +CIMGUI_API LRESULT igImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +static LRESULT WINAPI _imgui_d3d9_hook_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result; + + result = igImplWin32_WndProcHandler(hWnd, msg, wParam, lParam); + + if (result) { + return result; + } + + return _imgui_d3d9_hook_original_wndproc(hWnd, msg, wParam, lParam); +} + +static LRESULT WINAPI _imgui_d3d9_hook_RegisterClassA(WNDCLASSA *lpwcx) +{ + log_info("Swapping out WndProc handler for wrapper handler in RegisterClassA %s", lpwcx->lpszClassName); + + _imgui_d3d9_hook_original_wndproc = lpwcx->lpfnWndProc; + lpwcx->lpfnWndProc = _imgui_d3d9_hook_WndProc; + + return _imgui_d3d9_hook_original_RegisterClassA(lpwcx); +} + +void imgui_d3d9_hook_init(const imgui_bt_component_t *components, size_t component_count) +{ + ImGuiIO *io; + ImGuiStyle* style; + + log_assert(components); + log_assert(component_count > 0); + + _imgui_d3d9_hook_components = (imgui_bt_component_t *) xmalloc(component_count * sizeof(imgui_bt_component_t)); + memcpy(_imgui_d3d9_hook_components, components, component_count * sizeof(imgui_bt_component_t)); + _imgui_d3d9_hook_component_count = component_count; + + _imgui_d3d9_hook_imgui_ctx = igCreateContext(NULL); + io = igGetIO(); + + io->ConfigFlags = ImGuiConfigFlags_NavEnableKeyboard + | ImGuiConfigFlags_DockingEnable + | ImGuiConfigFlags_ViewportsEnable; + + io->MouseDrawCursor = true; + io->IniFilename = NULL; + io->FontAllowUserScaling = true; + + hook_table_apply(NULL, "user32.dll", _imgui_d3d9_hook_syms, lengthof(_imgui_d3d9_hook_syms)); + + // Setup style + style = igGetStyle(); + style->Colors[ImGuiCol_WindowBg] = (ImVec4){0.0f, 0.0f, 0.0f, 1.0f}; + style->Colors[ImGuiCol_PlotLines] = (ImVec4){1.0f, 1.0f, 0.0f, 1.0f}; + + _imgui_d3d9_hook_initialized = true; +} + +void imgui_d3d9_hook_fini() +{ + hook_table_revert(NULL, "user32.dll", _imgui_d3d9_hook_syms, lengthof(_imgui_d3d9_hook_syms)); + + igImplDX9_Shutdown(); + igImplWin32_Shutdown(); + igDestroyContext(NULL); + + _imgui_d3d9_hook_initialized = false; +} + +HRESULT imgui_hook_d3d9_irp_handler(struct hook_d3d9_irp *irp) +{ + HRESULT hr; + + log_assert(irp); + + if (!_imgui_d3d9_hook_initialized) { + return hook_d3d9_irp_invoke_next(irp); + } + + switch (irp->op) { + case HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE: + hr = hook_d3d9_irp_invoke_next(irp); + + if (hr == S_OK) { + igImplWin32_Init(irp->args.ctx_create_device.hwnd); + igImplDX9_Init(*irp->args.ctx_create_device.pdev); + } + + return hr; + + case HOOK_D3D9_IRP_OP_DEV_BEGIN_SCENE: + igImplDX9_NewFrame(); + igImplWin32_NewFrame(); + igNewFrame(); + + for (size_t i = 0; i < _imgui_d3d9_hook_component_count; i++) { + _imgui_d3d9_hook_components[i].frame_update(_imgui_d3d9_hook_imgui_ctx); + } + + return hook_d3d9_irp_invoke_next(irp); + + case HOOK_D3D9_IRP_OP_DEV_END_SCENE: + igRender(); + igImplDX9_RenderDrawData(igGetDrawData()); + + return hook_d3d9_irp_invoke_next(irp); + + default: + return hook_d3d9_irp_invoke_next(irp); + } + + log_fatal("Illegal state"); +} \ No newline at end of file diff --git a/src/main/imgui-bt/imgui-d3d9-hook.h b/src/main/imgui-bt/imgui-d3d9-hook.h new file mode 100644 index 0000000..59e0f0e --- /dev/null +++ b/src/main/imgui-bt/imgui-d3d9-hook.h @@ -0,0 +1,13 @@ +#ifndef IMGUI_D3D9_HOOK_H +#define IMGUI_D3D9_HOOK_H + +#include "hook/d3d9.h" + +#include "imgui-bt/component.h" + +void imgui_d3d9_hook_init(const imgui_bt_component_t *components, size_t component_count); +void imgui_d3d9_hook_fini(); + +HRESULT imgui_hook_d3d9_irp_handler(struct hook_d3d9_irp *irp); + +#endif \ No newline at end of file