From 94e9edb5a06dc87017f7fe4669c8da9d5c7d6a07 Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 31 May 2021 12:58:22 -0400 Subject: [PATCH] Load and bind chuniio at runtime --- chunihook/chuni-dll.c | 121 ++++++++++++++++++++++++++++++++++++++++ chunihook/chuni-dll.h | 25 +++++++++ chunihook/chunihook.def | 9 +++ chunihook/config.c | 17 ++++++ chunihook/config.h | 5 ++ chunihook/dllmain.c | 6 ++ chunihook/jvs.c | 13 +++-- chunihook/meson.build | 2 + chunihook/slider.c | 22 +++++--- 9 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 chunihook/chuni-dll.c create mode 100644 chunihook/chuni-dll.h diff --git a/chunihook/chuni-dll.c b/chunihook/chuni-dll.c new file mode 100644 index 0000000..3750c86 --- /dev/null +++ b/chunihook/chuni-dll.c @@ -0,0 +1,121 @@ +#include + +#include +#include + +#include "chunihook/chuni-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym chuni_dll_syms[] = { + { + .sym = "chuni_io_jvs_init", + .off = offsetof(struct chuni_dll, jvs_init), + }, { + .sym = "chuni_io_jvs_poll", + .off = offsetof(struct chuni_dll, jvs_poll), + }, { + .sym = "chuni_io_jvs_read_coin_counter", + .off = offsetof(struct chuni_dll, jvs_read_coin_counter), + }, { + .sym = "chuni_io_jvs_set_coin_blocker", + .off = offsetof(struct chuni_dll, jvs_set_coin_blocker), + }, { + .sym = "chuni_io_slider_init", + .off = offsetof(struct chuni_dll, slider_init), + }, { + .sym = "chuni_io_slider_start", + .off = offsetof(struct chuni_dll, slider_start), + }, { + .sym = "chuni_io_slider_stop", + .off = offsetof(struct chuni_dll, slider_stop), + }, { + .sym = "chuni_io_slider_set_leds", + .off = offsetof(struct chuni_dll, slider_set_leds), + } +}; + +struct chuni_dll chuni_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 chuni_dll_init(const struct chuni_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("Chunithm IO: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Chunithm IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "chuni_io_get_api_version"); + + if (get_api_version != NULL) { + chuni_dll.api_version = get_api_version(); + } else { + chuni_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose chuni_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (chuni_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Chunithm IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + chuni_dll.api_version); + + goto end; + } + + sym = chuni_dll_syms; + hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Chunithm 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/chunihook/chuni-dll.h b/chunihook/chuni-dll.h new file mode 100644 index 0000000..e4a24f8 --- /dev/null +++ b/chunihook/chuni-dll.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "chuniio/chuniio.h" + +struct chuni_dll { + uint16_t api_version; + HRESULT (*jvs_init)(void); + void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); + void (*jvs_read_coin_counter)(uint16_t *total); + void (*jvs_set_coin_blocker)(bool open); + HRESULT (*slider_init)(void); + void (*slider_start)(chuni_io_slider_callback_t callback); + void (*slider_stop)(void); + void (*slider_set_leds)(const uint8_t *rgb); +}; + +struct chuni_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct chuni_dll chuni_dll; + +HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self); diff --git a/chunihook/chunihook.def b/chunihook/chunihook.def index 9da4d7f..5b310a5 100644 --- a/chunihook/chunihook.def +++ b/chunihook/chunihook.def @@ -12,3 +12,12 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + chuni_io_get_api_version + chuni_io_jvs_init + chuni_io_jvs_poll + chuni_io_jvs_read_coin_counter + chuni_io_jvs_set_coin_blocker + chuni_io_slider_init + chuni_io_slider_set_leds + chuni_io_slider_start + chuni_io_slider_stop diff --git a/chunihook/config.c b/chunihook/config.c index 66b3dc4..3016445 100644 --- a/chunihook/config.c +++ b/chunihook/config.c @@ -18,6 +18,22 @@ #include "platform/config.h" #include "platform/platform.h" +void chuni_dll_config_load( + struct chuni_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"chuniio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void slider_config_load(struct slider_config *cfg, const wchar_t *filename) { assert(cfg != NULL); @@ -39,5 +55,6 @@ void chuni_hook_config_load( amex_config_load(&cfg->amex, filename); aime_config_load(&cfg->aime, filename); gfx_config_load(&cfg->gfx, filename); + chuni_dll_config_load(&cfg->dll, filename); slider_config_load(&cfg->slider, filename); } diff --git a/chunihook/config.h b/chunihook/config.h index 8d9e66d..d3c094a 100644 --- a/chunihook/config.h +++ b/chunihook/config.h @@ -7,6 +7,7 @@ #include "board/sg-reader.h" +#include "chunihook/chuni-dll.h" #include "chunihook/slider.h" #include "hooklib/gfx.h" @@ -18,9 +19,13 @@ struct chuni_hook_config { struct amex_config amex; struct aime_config aime; struct gfx_config gfx; + struct chuni_dll_config dll; struct slider_config slider; }; +void chuni_dll_config_load( + struct chuni_dll_config *cfg, + const wchar_t *filename); void slider_config_load(struct slider_config *cfg, const wchar_t *filename); void chuni_hook_config_load( struct chuni_hook_config *cfg, diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index 5ffa691..f2dff5e 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -75,6 +75,12 @@ static DWORD CALLBACK chuni_pre_startup(void) goto fail; } + hr = chuni_dll_init(&chuni_hook_cfg.dll, chuni_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = amex_hook_init(&chuni_hook_cfg.amex, chunithm_jvs_init); if (FAILED(hr)) { diff --git a/chunihook/jvs.c b/chunihook/jvs.c index a036fb7..a57c2a3 100644 --- a/chunihook/jvs.c +++ b/chunihook/jvs.c @@ -9,7 +9,7 @@ #include "board/io3.h" -#include "chuniio/chuniio.h" +#include "chunihook/chuni-dll.h" #include "jvs/jvs-bus.h" @@ -47,9 +47,10 @@ HRESULT chunithm_jvs_init(struct jvs_node **out) HRESULT hr; assert(out != NULL); + assert(chuni_dll.jvs_init != NULL); - dprintf("JVS I/O: Starting Chunithm backend DLL\n"); - hr = chuni_io_jvs_init(); + dprintf("JVS I/O: Starting IO backend\n"); + hr = chuni_dll.jvs_init(); if (FAILED(hr)) { dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr); @@ -70,11 +71,12 @@ static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out) size_t i; assert(out != NULL); + assert(chuni_dll.jvs_poll != NULL); opbtn = 0; beams = 0; - chuni_io_jvs_poll(&opbtn, &beams); + chuni_dll.jvs_poll(&opbtn, &beams); out->system = 0x00; out->p1 = 0x0000; @@ -105,10 +107,11 @@ static void chunithm_jvs_read_coin_counter( uint16_t *out) { assert(out != NULL); + assert(chuni_dll.jvs_read_coin_counter != NULL); if (slot_no > 0) { return; } - chuni_io_jvs_read_coin_counter(out); + chuni_dll.jvs_read_coin_counter(out); } diff --git a/chunihook/meson.build b/chunihook/meson.build index 9279c38..e2dfa08 100644 --- a/chunihook/meson.build +++ b/chunihook/meson.build @@ -20,6 +20,8 @@ shared_library( util_lib, ], sources : [ + 'chuni-dll.c', + 'chuni-dll.h', 'config.c', 'config.h', 'dllmain.c', diff --git a/chunihook/slider.c b/chunihook/slider.c index f4a2bfd..753a608 100644 --- a/chunihook/slider.c +++ b/chunihook/slider.c @@ -9,10 +9,9 @@ #include "board/slider-cmd.h" #include "board/slider-frame.h" +#include "chunihook/chuni-dll.h" #include "chunihook/slider.h" -#include "chuniio/chuniio.h" - #include "hook/iobuf.h" #include "hook/iohook.h" @@ -41,6 +40,7 @@ static uint8_t slider_readable_bytes[520]; HRESULT slider_hook_init(const struct slider_config *cfg) { assert(cfg != NULL); + assert(chuni_dll.slider_init != NULL); if (!cfg->enable) { return S_FALSE; @@ -81,11 +81,11 @@ static HRESULT slider_handle_irp_locked(struct irp *irp) HRESULT hr; if (irp->op == IRP_OP_OPEN) { - dprintf("Chunithm slider: Starting backend DLL\n"); - hr = chuni_io_slider_init(); + dprintf("Chunithm slider: Starting backend\n"); + hr = chuni_dll.slider_init(); if (FAILED(hr)) { - dprintf("Chunithm slider: Backend DLL error: %x\n", (int) hr); + dprintf("Chunithm slider: Backend error: %x\n", (int) hr); return hr; } @@ -189,8 +189,10 @@ static HRESULT slider_req_get_board_info(void) static HRESULT slider_req_auto_scan_start(void) { + assert(chuni_dll.slider_start != NULL); + dprintf("Chunithm slider: Start slider notifications\n"); - chuni_io_slider_start(slider_res_auto_scan); + chuni_dll.slider_start(slider_res_auto_scan); /* This message is not acknowledged */ @@ -201,6 +203,8 @@ static HRESULT slider_req_auto_scan_stop(void) { struct slider_hdr resp; + assert(chuni_dll.slider_stop != NULL); + dprintf("Chunithm slider: Stop slider notifications\n"); /* IO DLL worker thread might attempt to invoke the callback (which needs @@ -209,7 +213,7 @@ static HRESULT slider_req_auto_scan_stop(void) situation. */ LeaveCriticalSection(&slider_lock); - chuni_io_slider_stop(); + chuni_dll.slider_stop(); EnterCriticalSection(&slider_lock); resp.sync = SLIDER_FRAME_SYNC; @@ -221,7 +225,9 @@ static HRESULT slider_req_auto_scan_stop(void) static HRESULT slider_req_set_led(const struct slider_req_set_led *req) { - chuni_io_slider_set_leds(req->payload.rgb); + assert(chuni_dll.slider_set_leds != NULL); + + chuni_dll.slider_set_leds(req->payload.rgb); /* This message is not acknowledged */