From 3c740af6ec27af334569c0869fd1fe992630c4b3 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 14:23:55 -0400 Subject: [PATCH] Load and bind mu3io at runtime --- mu3hook/config.c | 17 +++++++ mu3hook/config.h | 7 +++ mu3hook/dllmain.c | 7 +++ mu3hook/io4.c | 19 +++++--- mu3hook/meson.build | 2 + mu3hook/mu3-dll.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ mu3hook/mu3-dll.h | 22 +++++++++ mu3hook/mu3hook.def | 5 ++ 8 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 mu3hook/mu3-dll.c create mode 100644 mu3hook/mu3-dll.h diff --git a/mu3hook/config.c b/mu3hook/config.c index c611131..24086d8 100644 --- a/mu3hook/config.c +++ b/mu3hook/config.c @@ -10,6 +10,22 @@ #include "platform/config.h" +void mu3_dll_config_load( + struct mu3_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"mu3io", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void mu3_hook_config_load( struct mu3_hook_config *cfg, const wchar_t *filename) @@ -20,4 +36,5 @@ void mu3_hook_config_load( platform_config_load(&cfg->platform, filename); aime_config_load(&cfg->aime, filename); gfx_config_load(&cfg->gfx, filename); + mu3_dll_config_load(&cfg->dll, filename); } diff --git a/mu3hook/config.h b/mu3hook/config.h index e6ef4df..6b2afaa 100644 --- a/mu3hook/config.h +++ b/mu3hook/config.h @@ -6,14 +6,21 @@ #include "hooklib/gfx.h" +#include "mu3hook/mu3-dll.h" + #include "platform/config.h" struct mu3_hook_config { struct platform_config platform; struct aime_config aime; struct gfx_config gfx; + struct mu3_dll_config dll; }; +void mu3_dll_config_load( + struct mu3_dll_config *cfg, + const wchar_t *filename); + void mu3_hook_config_load( struct mu3_hook_config *cfg, const wchar_t *filename); diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index 08ca989..c7fe361 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -13,6 +13,7 @@ #include "mu3hook/config.h" #include "mu3hook/io4.h" +#include "mu3hook/mu3-dll.h" #include "mu3hook/unity.h" #include "platform/platform.h" @@ -62,6 +63,12 @@ static DWORD CALLBACK mu3_pre_startup(void) goto fail; } + hr = mu3_dll_init(&mu3_hook_cfg.dll, mu3_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = mu3_io4_hook_init(); if (FAILED(hr)) { diff --git a/mu3hook/io4.c b/mu3hook/io4.c index 5a0b4c0..1fd875a 100644 --- a/mu3hook/io4.c +++ b/mu3hook/io4.c @@ -6,7 +6,7 @@ #include "board/io4.h" -#include "mu3io/mu3io.h" +#include "mu3hook/mu3-dll.h" #include "util/dprintf.h" @@ -20,13 +20,15 @@ HRESULT mu3_io4_hook_init(void) { HRESULT hr; + assert(mu3_dll.init != NULL); + hr = io4_hook_init(&mu3_io4_ops, NULL); if (FAILED(hr)) { return hr; } - return mu3_io_init(); + return mu3_dll.init(); } static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) @@ -37,9 +39,14 @@ static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) int16_t lever; HRESULT hr; + assert(mu3_dll.poll != NULL); + assert(mu3_dll.get_opbtns != NULL); + assert(mu3_dll.get_gamebtns != NULL); + assert(mu3_dll.get_lever != NULL); + memset(state, 0, sizeof(*state)); - hr = mu3_io_poll(); + hr = mu3_dll.poll(); if (FAILED(hr)) { return hr; @@ -50,9 +57,9 @@ static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) right = 0; lever = 0; - mu3_io_get_opbtns(&opbtn); - mu3_io_get_gamebtns(&left, &right); - mu3_io_get_lever(&lever); + mu3_dll.get_opbtns(&opbtn); + mu3_dll.get_gamebtns(&left, &right); + mu3_dll.get_lever(&lever); if (opbtn & MU3_IO_OPBTN_TEST) { state->buttons[0] |= IO4_BUTTON_TEST; diff --git a/mu3hook/meson.build b/mu3hook/meson.build index f777b9d..9931b8b 100644 --- a/mu3hook/meson.build +++ b/mu3hook/meson.build @@ -24,6 +24,8 @@ shared_library( 'dllmain.c', 'io4.c', 'io4.h', + 'mu3-dll.c', + 'mu3-dll.h', 'unity.h', 'unity.c', ], diff --git a/mu3hook/mu3-dll.c b/mu3hook/mu3-dll.c new file mode 100644 index 0000000..15ed6bb --- /dev/null +++ b/mu3hook/mu3-dll.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "mu3hook/mu3-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym mu3_dll_syms[] = { + { + .sym = "mu3_io_init", + .off = offsetof(struct mu3_dll, init), + }, { + .sym = "mu3_io_poll", + .off = offsetof(struct mu3_dll, poll), + }, { + .sym = "mu3_io_get_opbtns", + .off = offsetof(struct mu3_dll, get_opbtns), + }, { + .sym = "mu3_io_get_gamebtns", + .off = offsetof(struct mu3_dll, get_gamebtns), + }, { + .sym = "mu3_io_get_lever", + .off = offsetof(struct mu3_dll, get_lever), + } +}; + +struct mu3_dll mu3_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Ongeki IO: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Ongeki IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "mu3_io_get_api_version"); + + if (get_api_version != NULL) { + mu3_dll.api_version = get_api_version(); + } else { + mu3_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose mu3_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (mu3_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Ongeki IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + mu3_dll.api_version); + + goto end; + } + + sym = mu3_dll_syms; + hr = dll_bind(&mu3_dll, src, &sym, _countof(mu3_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Ongeki IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/mu3hook/mu3-dll.h b/mu3hook/mu3-dll.h new file mode 100644 index 0000000..41f280f --- /dev/null +++ b/mu3hook/mu3-dll.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "mu3io/mu3io.h" + +struct mu3_dll { + uint16_t api_version; + HRESULT (*init)(void); + HRESULT (*poll)(void); + void (*get_opbtns)(uint8_t *opbtn); + void (*get_gamebtns)(uint8_t *left, uint8_t *right); + void (*get_lever)(int16_t *pos); +}; + +struct mu3_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct mu3_dll mu3_dll; + +HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self); diff --git a/mu3hook/mu3hook.def b/mu3hook/mu3hook.def index c819437..b3a1840 100644 --- a/mu3hook/mu3hook.def +++ b/mu3hook/mu3hook.def @@ -12,3 +12,8 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + mu3_io_get_gamebtns + mu3_io_get_lever + mu3_io_get_opbtns + mu3_io_init + mu3_io_poll