diff --git a/idzhook/config.c b/idzhook/config.c index 2987584..c8d551d 100644 --- a/idzhook/config.c +++ b/idzhook/config.c @@ -8,10 +8,27 @@ #include "board/sg-reader.h" #include "idzhook/config.h" +#include "idzhook/idz-dll.h" #include "platform/config.h" #include "platform/platform.h" +void idz_dll_config_load( + struct idz_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"idzio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void idz_hook_config_load( struct idz_hook_config *cfg, const wchar_t *filename) @@ -22,6 +39,7 @@ void idz_hook_config_load( platform_config_load(&cfg->platform, filename); amex_config_load(&cfg->amex, filename); aime_config_load(&cfg->aime, filename); + idz_dll_config_load(&cfg->dll, filename); zinput_config_load(&cfg->zinput, filename); } diff --git a/idzhook/config.h b/idzhook/config.h index a93cb11..a7cf964 100644 --- a/idzhook/config.h +++ b/idzhook/config.h @@ -7,6 +7,7 @@ #include "board/sg-reader.h" +#include "idzhook/idz-dll.h" #include "idzhook/zinput.h" #include "platform/platform.h" @@ -15,9 +16,14 @@ struct idz_hook_config { struct platform_config platform; struct amex_config amex; struct aime_config aime; + struct idz_dll_config dll; struct zinput_config zinput; }; +void idz_dll_config_load( + struct idz_dll_config *cfg, + const wchar_t *filename); + void idz_hook_config_load( struct idz_hook_config *cfg, const wchar_t *filename); diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index d1dd208..fd15f09 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -13,6 +13,7 @@ #include "hooklib/spike.h" #include "idzhook/config.h" +#include "idzhook/idz-dll.h" #include "idzhook/jvs.h" #include "idzhook/zinput.h" @@ -51,6 +52,12 @@ static DWORD CALLBACK idz_pre_startup(void) goto fail; } + hr = idz_dll_init(&idz_hook_cfg.dll, idz_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = amex_hook_init(&idz_hook_cfg.amex, idz_jvs_init); if (FAILED(hr)) { diff --git a/idzhook/idz-dll.c b/idzhook/idz-dll.c new file mode 100644 index 0000000..2ebc75b --- /dev/null +++ b/idzhook/idz-dll.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "idzhook/idz-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym idz_dll_syms[] = { + { + .sym = "idz_io_jvs_init", + .off = offsetof(struct idz_dll, jvs_init), + }, { + .sym = "idz_io_jvs_read_analogs", + .off = offsetof(struct idz_dll, jvs_read_analogs), + }, { + .sym = "idz_io_jvs_read_buttons", + .off = offsetof(struct idz_dll, jvs_read_buttons), + }, { + .sym = "idz_io_jvs_read_shifter", + .off = offsetof(struct idz_dll, jvs_read_shifter), + }, { + .sym = "idz_io_jvs_read_coin_counter", + .off = offsetof(struct idz_dll, jvs_read_coin_counter), + } +}; + +struct idz_dll idz_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 idz_dll_init(const struct idz_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("IDZ IO: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("IDZ IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "idz_io_get_api_version"); + + if (get_api_version != NULL) { + idz_dll.api_version = get_api_version(); + } else { + idz_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose idz_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (idz_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("IDZ IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + idz_dll.api_version); + + goto end; + } + + sym = idz_dll_syms; + hr = dll_bind(&idz_dll, src, &sym, _countof(idz_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("IDZ 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/idzhook/idz-dll.h b/idzhook/idz-dll.h new file mode 100644 index 0000000..e67cb45 --- /dev/null +++ b/idzhook/idz-dll.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "idzio/idzio.h" + +struct idz_dll { + uint16_t api_version; + HRESULT (*jvs_init)(void); + void (*jvs_read_analogs)(struct idz_io_analog_state *out); + void (*jvs_read_buttons)(uint8_t *opbtn, uint8_t *gamebtn); + void (*jvs_read_shifter)(uint8_t *gear); + void (*jvs_read_coin_counter)(uint16_t *total); +}; + +struct idz_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct idz_dll idz_dll; + +HRESULT idz_dll_init(const struct idz_dll_config *cfg, HINSTANCE self); diff --git a/idzhook/idzhook.def b/idzhook/idzhook.def index 43b8ef7..a9b9f74 100644 --- a/idzhook/idzhook.def +++ b/idzhook/idzhook.def @@ -12,3 +12,8 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + idz_io_jvs_init + idz_io_jvs_read_analogs + idz_io_jvs_read_buttons + idz_io_jvs_read_coin_counter + idz_io_jvs_read_shifter diff --git a/idzhook/jvs.c b/idzhook/jvs.c index fce5849..ab2f6aa 100644 --- a/idzhook/jvs.c +++ b/idzhook/jvs.c @@ -8,10 +8,9 @@ #include "board/io3.h" +#include "idzhook/idz-dll.h" #include "idzhook/jvs.h" -#include "idzio/idzio.h" - #include "jvs/jvs-bus.h" #include "util/dprintf.h" @@ -56,9 +55,10 @@ HRESULT idz_jvs_init(struct jvs_node **out) HRESULT hr; assert(out != NULL); + assert(idz_dll.jvs_init != NULL); dprintf("JVS I/O: Starting Initial D Zero backend DLL\n"); - hr = idz_io_jvs_init(); + hr = idz_dll.jvs_init(); if (FAILED(hr)) { dprintf("JVS I/O: Backend error, I/O disconnected; %x\n", (int) hr); @@ -79,13 +79,15 @@ static void idz_jvs_read_switches(void *ctx, struct io3_switch_state *out) uint8_t gear; assert(out != NULL); + assert(idz_dll.jvs_read_buttons != NULL); + assert(idz_dll.jvs_read_shifter != NULL); opbtn = 0; gamebtn = 0; gear = 0; - idz_io_jvs_read_buttons(&opbtn, &gamebtn); - idz_io_jvs_read_shifter(&gear); + idz_dll.jvs_read_buttons(&opbtn, &gamebtn); + idz_dll.jvs_read_shifter(&gear); /* Update gameplay buttons */ @@ -142,9 +144,10 @@ static void idz_jvs_read_analogs( struct idz_io_analog_state state; assert(analogs != NULL); + assert(idz_dll.jvs_read_analogs != NULL); memset(&state, 0, sizeof(state)); - idz_io_jvs_read_analogs(&state); + idz_dll.jvs_read_analogs(&state); if (nanalogs > 0) { analogs[0] = 0x8000 + state.wheel; @@ -164,9 +167,11 @@ static void idz_jvs_read_coin_counter( uint8_t slot_no, uint16_t *out) { + assert(idz_dll.jvs_read_coin_counter != NULL); + if (slot_no > 0) { return; } - idz_io_jvs_read_coin_counter(out); + idz_dll.jvs_read_coin_counter(out); } diff --git a/idzhook/meson.build b/idzhook/meson.build index e27007a..668ad8a 100644 --- a/idzhook/meson.build +++ b/idzhook/meson.build @@ -24,6 +24,8 @@ shared_library( 'config.c', 'config.h', 'dllmain.c', + 'idz-dll.c', + 'idz-dll.h', 'jvs.c', 'jvs.h', 'zinput.c',