From 97b17a084451ca145dded0ec7db0b1246d920e2e Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Mon, 9 Oct 2023 09:25:30 -0400 Subject: [PATCH] add exvs2hook --- Package.mk | 13 ++++ dist/exvs2/bananatools.ini | 86 +++++++++++++++++++++++++ dist/exvs2/start.bat | 11 ++++ dist/ferrum/bananatools.ini | 2 +- doc/exvs2hook.md | 13 ++++ doc/ferrumhook.md | 3 +- exvs2hook/bngrw.c | 0 exvs2hook/bngrw.h | 0 exvs2hook/config.c | 36 +++++++++++ exvs2hook/config.h | 28 ++++++++ exvs2hook/dllmain.c | 93 +++++++++++++++++++++++++++ exvs2hook/exvs2-dll.c | 100 +++++++++++++++++++++++++++++ exvs2hook/exvs2-dll.h | 20 ++++++ exvs2hook/exvs2hook.def | 7 ++ exvs2hook/jvs.c | 125 ++++++++++++++++++++++++++++++++++++ exvs2hook/jvs.h | 8 +++ exvs2hook/meson.build | 33 ++++++++++ exvs2io/config.c | 23 +++++++ exvs2io/config.h | 21 ++++++ exvs2io/exvs2io.c | 93 +++++++++++++++++++++++++++ exvs2io/exvs2io.def | 7 ++ exvs2io/exvs2io.h | 51 +++++++++++++++ exvs2io/meson.build | 16 +++++ gfxhook/dxgi.c | 27 +++++++- meson.build | 2 + 25 files changed, 812 insertions(+), 6 deletions(-) create mode 100644 dist/exvs2/bananatools.ini create mode 100644 dist/exvs2/start.bat create mode 100644 doc/exvs2hook.md create mode 100644 exvs2hook/bngrw.c create mode 100644 exvs2hook/bngrw.h create mode 100644 exvs2hook/config.c create mode 100644 exvs2hook/config.h create mode 100644 exvs2hook/dllmain.c create mode 100644 exvs2hook/exvs2-dll.c create mode 100644 exvs2hook/exvs2-dll.h create mode 100644 exvs2hook/exvs2hook.def create mode 100644 exvs2hook/jvs.c create mode 100644 exvs2hook/jvs.h create mode 100644 exvs2hook/meson.build create mode 100644 exvs2io/config.c create mode 100644 exvs2io/config.h create mode 100644 exvs2io/exvs2io.c create mode 100644 exvs2io/exvs2io.def create mode 100644 exvs2io/exvs2io.h create mode 100644 exvs2io/meson.build diff --git a/Package.mk b/Package.mk index 2d3765d..ea9a43b 100644 --- a/Package.mk +++ b/Package.mk @@ -20,9 +20,21 @@ $(BUILD_DIR_ZIP)/taiko.zip: $(V)strip $(BUILD_DIR_ZIP)/taiko/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/taiko ; zip -r ../taiko.zip * +$(BUILD_DIR_ZIP)/exvs2.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/exvs2 + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_64)/exvs2hook/exvs2hook.dll \ + $(DIST_DIR)/exvs2/bananatools.ini \ + $(DIST_DIR)/exvs2/start.bat \ + $(BUILD_DIR_ZIP)/exvs2 + $(V)strip $(BUILD_DIR_ZIP)/exvs2/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/exvs2 ; zip -r ../exvs2.zip * + $(BUILD_DIR_ZIP)/doc.zip: \ $(DOC_DIR)/ferrumhook.md \ $(DOC_DIR)/taikohook.md \ + $(DOC_DIR)/exvs2hook.md \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -r $@ $^ @@ -30,6 +42,7 @@ $(BUILD_DIR_ZIP)/doc.zip: \ $(BUILD_DIR_ZIP)/bananatools.zip: \ $(BUILD_DIR_ZIP)/ferrum.zip \ $(BUILD_DIR_ZIP)/taiko.zip \ + $(BUILD_DIR_ZIP)/exvs2.zip \ $(BUILD_DIR_ZIP)/doc.zip \ README.md \ diff --git a/dist/exvs2/bananatools.ini b/dist/exvs2/bananatools.ini new file mode 100644 index 0000000..2d48895 --- /dev/null +++ b/dist/exvs2/bananatools.ini @@ -0,0 +1,86 @@ +; Controls the virtual file system hooks. These redirect file i/o +; requests to a folder specified below, instead of the drive. +; These are all required, even if the game doesn't use one of them. +[vfs] +path= + +[dns] +default=localhost + +; Security dongle emulation, disable if you have a +; real dongle connected that you want to use +[dongle] +enable=1 +; XBoost +serial=284313110001 +; EXVS2 +; serial=281131010001 +; For LM, change the 5th digit to a 1 + +; Set the network environment. Most games seem to want 192.168.123.X +[netenv] +enable=1 +subnet=192.168.85.0 + +[misc] +systemVersion=GX2100-1-NA-SYS0-A06 + +; Graphics hook, may cause crashes in some games +[gfx] +enable=1 +windowed=1 +framed=0 +monitor=0 + +; Control the AMCUS replacement class +[amcus] +enable=1 +game_id=SBUZ +game_cd=GX21 +am_game_ver=4.00 +cacfg_game_ver=29.31 +server_uri=localhost +server_host=localhost + +; Controlls the xinput hooks +[xinput] +enable=1 + +[reader] +enable=1 +; "Enter" key +insert_card=0x0D +access_code=00000000000000000000 + +; JVS config +[jvs] +enable=1 +port=3 + +; Mappings for the najv4 IO board. To disable JVS emulation and use +; a real board, set enable to 0 in the "jvs" section. +[najv4] +; "Home" key +test=0x24 +; "Insert" key +coin=0x2D +; "Delete" key +service=0x2E +; Up arrow +up=0x26 +; Down arrow +down=0x28 +; Left arrow +left=0x25 +; Right arrow +right=0x27 +; Spacebar +start=0x20 +; 1 +btn1=0x31 +; 2 +btn2=0x32 +; 3 +btn3=0x33 +; 4 +btn4=0x34 \ No newline at end of file diff --git a/dist/exvs2/start.bat b/dist/exvs2/start.bat new file mode 100644 index 0000000..f51b412 --- /dev/null +++ b/dist/exvs2/start.bat @@ -0,0 +1,11 @@ +@echo off + +pushd %~dp0 + +REM Comment out this line if you intend to use the AMCUS emulator +start inject.exe -d -k exvs2hook.dll AMCUS\AMAuthd.exe +inject.exe -d -k exvs2hook.dll exvs2_exe_Release.exe + +echo. +echo The game process has terminated +pause \ No newline at end of file diff --git a/dist/ferrum/bananatools.ini b/dist/ferrum/bananatools.ini index c90edb7..8801634 100644 --- a/dist/ferrum/bananatools.ini +++ b/dist/ferrum/bananatools.ini @@ -20,7 +20,7 @@ subnet=192.168.123.0 ; Graphics hook, may cause crashes in some games [gfx] -enable=1 +enable=0 windowed=1 framed=0 monitor=0 diff --git a/doc/exvs2hook.md b/doc/exvs2hook.md new file mode 100644 index 0000000..d21050b --- /dev/null +++ b/doc/exvs2hook.md @@ -0,0 +1,13 @@ +# exvs2hook + +# Supported games + +* Extreme Vs 2 (all versions) +* Extreme Vs 2 XBoost (All Versions) + +## General remarks +* Game requires both at least 1 game clinet and a "Live Monitor" server + * 2 game clients are required for the Live Monitor to function properly. 1 will still work for the game client, but the LM will throw an error. +* AMCUS hook is required to run on Windows 11 from my testing. +* XBoost requires the final octet of it's IP to be between 64 and 90 inclusive. +* Any XInput controller should work, but is untested with these tools. diff --git a/doc/ferrumhook.md b/doc/ferrumhook.md index 8454477..ca4eba0 100644 --- a/doc/ferrumhook.md +++ b/doc/ferrumhook.md @@ -2,7 +2,7 @@ # Supported games -* Pokken Tournamen A** +* Pokken Tournamen (All versions) ## General remarks @@ -14,4 +14,3 @@ ## Known issues * gfxhook doesn't work as the dxgi/dx11 hooks are currently non-functional. -* Networking functionality is untested \ No newline at end of file diff --git a/exvs2hook/bngrw.c b/exvs2hook/bngrw.c new file mode 100644 index 0000000..e69de29 diff --git a/exvs2hook/bngrw.h b/exvs2hook/bngrw.h new file mode 100644 index 0000000..e69de29 diff --git a/exvs2hook/config.c b/exvs2hook/config.c new file mode 100644 index 0000000..d3c737a --- /dev/null +++ b/exvs2hook/config.c @@ -0,0 +1,36 @@ +#include +#include + +#include "exvs2hook/config.h" + +#include "platform/config.h" + +void exvs2_dll_config_load( + struct exvs2_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"exvs2io", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + +void exvs2_hook_config_load( + struct exvs2_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + platform_config_load(&cfg->platform, filename); + exvs2_dll_config_load(&cfg->dll, filename); + gfx_config_load(&cfg->gfx, filename); + qr_config_load(&cfg->qr, filename); + bpreader_config_load(&cfg->reader, filename); +} diff --git a/exvs2hook/config.h b/exvs2hook/config.h new file mode 100644 index 0000000..0f0cb7a --- /dev/null +++ b/exvs2hook/config.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "exvs2hook/exvs2-dll.h" + +#include "platform/config.h" +#include "gfxhook/config.h" +#include "amcus/config.h" +#include "board/config.h" + +struct exvs2_hook_config { + struct platform_config platform; + struct exvs2_dll_config dll; + struct gfx_config gfx; + struct amcus_config amcus; + struct qr_config qr; + struct bpreader_config reader; +}; + +void exvs2_dll_config_load( + struct exvs2_dll_config *cfg, + const wchar_t *filename); + +void exvs2_hook_config_load( + struct exvs2_hook_config *cfg, + const wchar_t *filename); + diff --git a/exvs2hook/dllmain.c b/exvs2hook/dllmain.c new file mode 100644 index 0000000..7601810 --- /dev/null +++ b/exvs2hook/dllmain.c @@ -0,0 +1,93 @@ +#include +#include + +#include "exvs2hook/config.h" +#include "exvs2hook/exvs2-dll.h" +#include "exvs2hook/jvs.h" + +#include "amcus/amcus.h" + +#include "hook/process.h" + +#include "hooklib/serial.h" + +#include "board/bpreader.h" +#include "board/qr.h" + +#include "platform/platform.h" +#include "gfxhook/gfx.h" +#include "gfxhook/dxgi.h" + +#include "util/dprintf.h" + +static HMODULE exvs2_hook_mod; +static process_entry_t exvs2_startup; +static struct exvs2_hook_config exvs2_hook_cfg; + +static DWORD CALLBACK exvs2_pre_startup(void) +{ + HRESULT hr; + + dprintf("--- Begin exvs2_pre_startup ---\n"); + + exvs2_hook_config_load(&exvs2_hook_cfg, L".\\bananatools.ini"); + + serial_hook_init(); + + struct dongle_info dinfo; + dinfo.vid = 0x0B9A; + dinfo.pid = 0x0C20; + wcscpy_s(dinfo.manufacturer, _countof(dinfo.manufacturer), L"UFD 3.0"); + wcscpy_s(dinfo.product, _countof(dinfo.product), L"Silicon-Power8G"); + + hr = platform_hook_init(&exvs2_hook_cfg.platform, PLATFORM_BNA1, exvs2_jvs_init, exvs2_hook_mod, dinfo); + + if (FAILED(hr)) { + ExitProcess(EXIT_FAILURE); + } + + hr = exvs2_dll_init(&exvs2_hook_cfg.dll, exvs2_hook_mod); + + if (FAILED(hr)) { + ExitProcess(EXIT_FAILURE); + } + + hr = bpreader_init(&exvs2_hook_cfg.reader, 4); + + if (FAILED(hr)) { + ExitProcess(EXIT_FAILURE); + } + + hr = amcus_hook_init(&exvs2_hook_cfg.amcus); + + if (FAILED(hr)) { + ExitProcess(EXIT_FAILURE); + } + + gfx_hook_init(&exvs2_hook_cfg.gfx); + gfx_dxgi_hook_init(&exvs2_hook_cfg.gfx, exvs2_hook_mod); + + dprintf("--- End exvs2_pre_startup ---\n"); + + return exvs2_startup(); + +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + exvs2_hook_mod = mod; + + hr = process_hijack_startup(exvs2_pre_startup, &exvs2_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} \ No newline at end of file diff --git a/exvs2hook/exvs2-dll.c b/exvs2hook/exvs2-dll.c new file mode 100644 index 0000000..028c414 --- /dev/null +++ b/exvs2hook/exvs2-dll.c @@ -0,0 +1,100 @@ +#include + +#include +#include + +#include "exvs2hook/exvs2-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym exvs2_dll_syms[] = { + { + .sym = "exvs2_io_jvs_init", + .off = offsetof(struct exvs2_dll, jvs_init), + }, { + .sym = "exvs2_io_jvs_poll", + .off = offsetof(struct exvs2_dll, jvs_poll), + }, { + .sym = "exvs2_io_jvs_read_coin_counter", + .off = offsetof(struct exvs2_dll, jvs_read_coin_counter), + } +}; + +struct exvs2_dll exvs2_dll; + +HRESULT exvs2_dll_init(const struct exvs2_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("exvs2 IO: Failed to load IO DLL: %lx: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("exvs2 IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "exvs2_io_get_api_version"); + + if (get_api_version != NULL) { + exvs2_dll.api_version = get_api_version(); + } else { + exvs2_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose exvs2_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (exvs2_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("exvs2 IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + exvs2_dll.api_version); + + goto end; + } + + sym = exvs2_dll_syms; + hr = dll_bind(&exvs2_dll, src, &sym, _countof(exvs2_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("exvs2 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; +} \ No newline at end of file diff --git a/exvs2hook/exvs2-dll.h b/exvs2hook/exvs2-dll.h new file mode 100644 index 0000000..0a0a6a5 --- /dev/null +++ b/exvs2hook/exvs2-dll.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "exvs2io/exvs2io.h" + +struct exvs2_dll { + uint16_t api_version; + HRESULT (*jvs_init)(void); + HRESULT (*jvs_poll)(uint8_t *opbtn, uint16_t *gamepad); + void (*jvs_read_coin_counter)(uint16_t *coins); +}; + +struct exvs2_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct exvs2_dll exvs2_dll; + +HRESULT exvs2_dll_init(const struct exvs2_dll_config *cfg, HINSTANCE self); diff --git a/exvs2hook/exvs2hook.def b/exvs2hook/exvs2hook.def new file mode 100644 index 0000000..9665d7a --- /dev/null +++ b/exvs2hook/exvs2hook.def @@ -0,0 +1,7 @@ +LIBRARY exvs2hook + +EXPORTS + exvs2_io_get_api_version + exvs2_io_jvs_init + exvs2_io_jvs_poll + exvs2_io_jvs_read_coin_counter \ No newline at end of file diff --git a/exvs2hook/jvs.c b/exvs2hook/jvs.c new file mode 100644 index 0000000..508a4ab --- /dev/null +++ b/exvs2hook/jvs.c @@ -0,0 +1,125 @@ +#include + +#include +#include +#include +#include +#include + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" +#include "hooklib/fdshark.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +#include "board/najv4.h" + +#include "exvs2hook/jvs.h" +#include "exvs2hook/exvs2-dll.h" + +static void exvs2_jvs_read_switches(void *ctx, struct najv4_switch_state *out); +static void exvs2_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out); + +static const struct najv4_ops exvs2_jvs_najv4_ops = { + .read_switches = exvs2_jvs_read_switches, + .read_coin_counter = exvs2_jvs_read_coin_counter, +}; + +static struct najv4 exvs2_jvs_najv4; + +HRESULT exvs2_jvs_init(struct jvs_node **out) +{ + HRESULT hr; + + assert(out != NULL); + assert(exvs2_dll.jvs_init != NULL); + + dprintf("exvs2 JVS: Starting IO backend\n"); + hr = exvs2_dll.jvs_init(); + + if (FAILED(hr)) { + dprintf("exvs2 JVS: Backend error, I/O disconnected: %x\n", (int) hr); + + return hr; + } + + najv4_init(&exvs2_jvs_najv4, NULL, &exvs2_jvs_najv4_ops, NULL); + *out = najv4_to_jvs_node(&exvs2_jvs_najv4); + + return S_OK; +} + +static void exvs2_jvs_read_switches(void *ctx, struct najv4_switch_state *out) +{ + uint8_t opbtn = 0; + uint16_t gamebtn = 0; + + assert(out != NULL); + assert(exvs2_dll.jvs_poll != NULL); + + exvs2_dll.jvs_poll(&opbtn, &gamebtn); + + out->system = 0; + out->p1 = 0; + out->p2 = 0; + + + if (opbtn & EXVS2_IO_OPBTN_TEST) { // Test + out->system = 0x80; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_4) { // Btn4 + out->p1 |= 0x40; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_3) { // Btn3 + out->p1 |= 0x80; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_2) { // Btn2 + out->p1 |= 0x100; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_1) { // Btn1 + out->p1 |= 0x200; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_RIGHT) { // Right + out->p1 |= 0x400; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_LEFT) { // Left + out->p1 |= 0x800; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_DOWN) { // Down + out->p1 |= 0x1000; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_UP) { // Up + out->p1 |= 0x2000; + } + + if (opbtn & EXVS2_IO_OPBTN_SERVICE) { // Service + out->p1 |= 0x4000; + } + + if (gamebtn & EXVS2_IO_GAMEBTN_START) { // Start + out->p1 = 0x8000; + } +} + +static void exvs2_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out) +{ + assert(out != NULL); + assert(exvs2_dll.jvs_read_coin_counter != NULL); + + if (slot_no > 0) { + return; + } + + exvs2_dll.jvs_read_coin_counter(out); +} \ No newline at end of file diff --git a/exvs2hook/jvs.h b/exvs2hook/jvs.h new file mode 100644 index 0000000..e517765 --- /dev/null +++ b/exvs2hook/jvs.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +#include "jvs/jvs-bus.h" + +HRESULT exvs2_jvs_init(struct jvs_node **root); \ No newline at end of file diff --git a/exvs2hook/meson.build b/exvs2hook/meson.build new file mode 100644 index 0000000..7408c98 --- /dev/null +++ b/exvs2hook/meson.build @@ -0,0 +1,33 @@ +shared_library( + 'exvs2hook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'exvs2hook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + exvs2io_lib, + amcus_lib, + platform_lib, + util_lib, + hooklib_lib, + gfxhook_lib, + jvs_lib, + board_lib + ], + sources : [ + 'dllmain.c', + 'config.c', + 'config.h', + 'exvs2-dll.c', + 'exvs2-dll.h', + 'jvs.c', + 'jvs.h', + 'bngrw.c', + 'bngrw.h', + ], +) diff --git a/exvs2io/config.c b/exvs2io/config.c new file mode 100644 index 0000000..86d9a60 --- /dev/null +++ b/exvs2io/config.c @@ -0,0 +1,23 @@ +#include + +#include +#include +#include + +#include "exvs2io/config.h" + +void exvs2_io_najv4_config_load(struct exvs2_najv4_config *cfg, const wchar_t *filename) +{ + cfg->test = GetPrivateProfileIntW(L"najv4", L"test", VK_HOME, filename); + cfg->service = GetPrivateProfileIntW(L"najv4", L"service", VK_DELETE, filename); + cfg->coin = GetPrivateProfileIntW(L"najv4", L"coin", VK_INSERT, filename); + cfg->up = GetPrivateProfileIntW(L"najv4", L"up", VK_UP, filename); + cfg->down = GetPrivateProfileIntW(L"najv4", L"down", VK_DOWN, filename); + cfg->left = GetPrivateProfileIntW(L"najv4", L"left", VK_LEFT, filename); + cfg->right = GetPrivateProfileIntW(L"najv4", L"right", VK_RIGHT, filename); + cfg->start = GetPrivateProfileIntW(L"najv4", L"start", VK_SPACE, filename); + cfg->btn1 = GetPrivateProfileIntW(L"najv4", L"btn1", '1', filename); + cfg->btn2 = GetPrivateProfileIntW(L"najv4", L"btn2", '2', filename); + cfg->btn3 = GetPrivateProfileIntW(L"najv4", L"btn3", '3', filename); + cfg->btn4 = GetPrivateProfileIntW(L"najv4", L"btn4", '4', filename); +} diff --git a/exvs2io/config.h b/exvs2io/config.h new file mode 100644 index 0000000..a981d9e --- /dev/null +++ b/exvs2io/config.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +struct exvs2_najv4_config { + uint8_t test; + uint8_t service; + uint8_t coin; + uint8_t up; + uint8_t down; + uint8_t left; + uint8_t right; + uint8_t start; + uint8_t btn1; + uint8_t btn2; + uint8_t btn3; + uint8_t btn4; +}; + +void exvs2_io_najv4_config_load(struct exvs2_najv4_config *cfg, const wchar_t *filename); diff --git a/exvs2io/exvs2io.c b/exvs2io/exvs2io.c new file mode 100644 index 0000000..2552d41 --- /dev/null +++ b/exvs2io/exvs2io.c @@ -0,0 +1,93 @@ +#include +#include + +#include +#include +#include + +#include "exvs2io/exvs2io.h" +#include "exvs2io/config.h" + +#include "util/dprintf.h" + +static bool exvs2_io_coin = false; +static uint16_t exvs2_coin_ct = 0; +static struct exvs2_najv4_config najv4_cfg; + +uint16_t exvs2_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT exvs2_io_jvs_init(void) +{ + dprintf("exvs2 IO: JVS Init\n"); + exvs2_io_najv4_config_load(&najv4_cfg, L".\\bananatools.ini"); + return S_OK; +} + +HRESULT exvs2_io_jvs_poll(uint8_t *opbtn, uint16_t *gamepad) +{ + *opbtn = 0; + *gamepad = 0; + + if ((GetAsyncKeyState(najv4_cfg.test) & 0x8000)) { + *opbtn |= EXVS2_IO_OPBTN_TEST; + } + + if (GetAsyncKeyState(najv4_cfg.service) & 0x8000) { + *opbtn |= EXVS2_IO_OPBTN_SERVICE; + } + + if (GetAsyncKeyState(najv4_cfg.up) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_UP; + } + + if (GetAsyncKeyState(najv4_cfg.down) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_DOWN; + } + + if (GetAsyncKeyState(najv4_cfg.left) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_LEFT; + } + + if (GetAsyncKeyState(najv4_cfg.right) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_RIGHT; + } + + if (GetAsyncKeyState(najv4_cfg.start) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_START; + } + + if (GetAsyncKeyState(najv4_cfg.btn1) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_1; + } + + if (GetAsyncKeyState(najv4_cfg.btn2) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_2; + } + + if (GetAsyncKeyState(najv4_cfg.btn3) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_3; + } + + if (GetAsyncKeyState(najv4_cfg.btn4) & 0x8000) { + *gamepad |= EXVS2_IO_GAMEBTN_4; + } + + return S_OK; +} + +void exvs2_io_jvs_read_coin_counter(uint16_t *coins) +{ + if (GetAsyncKeyState(najv4_cfg.coin) & 0x8000) { + if (!exvs2_io_coin) { + exvs2_io_coin = true; + exvs2_coin_ct++; + } + } else { + exvs2_io_coin = false; + } + + *coins = exvs2_coin_ct; +} \ No newline at end of file diff --git a/exvs2io/exvs2io.def b/exvs2io/exvs2io.def new file mode 100644 index 0000000..908d950 --- /dev/null +++ b/exvs2io/exvs2io.def @@ -0,0 +1,7 @@ +LIBRARY exvs2hook + +EXPORTS + exvs2_io_get_api_version + exvs2_io_jvs_init + exvs2_io_jvs_poll + exvs2_io_read_coin_counter \ No newline at end of file diff --git a/exvs2io/exvs2io.h b/exvs2io/exvs2io.h new file mode 100644 index 0000000..0bf86ab --- /dev/null +++ b/exvs2io/exvs2io.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include + +#include "exvs2io/config.h" + +enum { + EXVS2_IO_OPBTN_TEST = 0x01, + EXVS2_IO_OPBTN_SERVICE = 0x02, + EXVS2_IO_OPBTN_COIN = 0x20, +}; +enum { + EXVS2_IO_GAMEBTN_1 = 0x1, + EXVS2_IO_GAMEBTN_2 = 0x2, + EXVS2_IO_GAMEBTN_3 = 0x4, + EXVS2_IO_GAMEBTN_4 = 0x8, + EXVS2_IO_GAMEBTN_UP = 0x10, + EXVS2_IO_GAMEBTN_DOWN = 0x20, + EXVS2_IO_GAMEBTN_LEFT = 0x40, + EXVS2_IO_GAMEBTN_RIGHT = 0x80, + EXVS2_IO_GAMEBTN_START = 0x100, +}; + +/* Get the version of the Pokken IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + +uint16_t exvs2_io_get_api_version(void); + +/* Initialize the IO DLL. This is the second function that will be called on + your DLL, after exvs2_io_get_api_version. + + All subsequent calls to this API may originate from arbitrary threads. + + Minimum API version: 0x0100 */ + +HRESULT exvs2_io_jvs_init(void); + +/* Send any queued outputs (of which there are currently none, though this may + change in subsequent API versions) and retrieve any new inputs. + + Minimum API version: 0x0100 */ + +HRESULT exvs2_io_jvs_poll(uint8_t *opbtn, uint16_t *gamepad); + +void exvs2_io_jvs_read_coin_counter(uint16_t *coins); \ No newline at end of file diff --git a/exvs2io/meson.build b/exvs2io/meson.build new file mode 100644 index 0000000..736528e --- /dev/null +++ b/exvs2io/meson.build @@ -0,0 +1,16 @@ +exvs2io_lib = static_library( + 'exvs2io', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + dependencies : [ + xinput_lib, + ], + sources : [ + 'exvs2io.c', + 'exvs2io.h', + 'config.c', + 'config.h', + ], +) diff --git a/gfxhook/dxgi.c b/gfxhook/dxgi.c index 5441576..eca539d 100644 --- a/gfxhook/dxgi.c +++ b/gfxhook/dxgi.c @@ -73,6 +73,7 @@ void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self) memcpy(&gfx_config, cfg, sizeof(*cfg)); hook_table_apply(NULL, "dxgi.dll", dxgi_hooks, _countof(dxgi_hooks)); + hook_table_apply(NULL, "dxgi", dxgi_hooks, _countof(dxgi_hooks)); if (next_CreateDXGIFactory == NULL || next_CreateDXGIFactory1 == NULL) { dxgi = LoadLibraryW(L"dxgi.dll"); @@ -110,14 +111,15 @@ void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self) goto fail; } - /* `CreateDXGIFactory2` was introduced in Windows 8.1 and the original - * Nu runs Windows 8, so do not require it to exist */ } if (self != NULL) { dll_hook_push(self, L"dxgi.dll"); + dll_hook_push(self, L"dxgi"); } + dprintf("DXGI: init\n"); + return; fail: @@ -192,7 +194,7 @@ HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID riid, void **factory) return hr; } - //hr = hook_factory(riid, factory); + hr = hook_factory(riid, factory); if (FAILED(hr)) { return hr; @@ -206,8 +208,10 @@ static HRESULT hook_factory(REFIID riid, void **factory) struct com_proxy *proxy; IDXGIFactory *api_0; IDXGIFactory1 *api_1; + IDXGIFactory2 *api_2; IDXGIFactoryVtbl *vtbl_0; IDXGIFactory1Vtbl *vtbl_1; + IDXGIFactory2Vtbl *vtbl_2; HRESULT hr; api_0 = NULL; @@ -242,6 +246,20 @@ static HRESULT hook_factory(REFIID riid, void **factory) vtbl_1 = proxy->vptr; vtbl_1->CreateSwapChain = my_IDXGIFactory1_CreateSwapChain; + *factory = proxy; + } else if (memcmp(riid, &IID_IDXGIFactory2, sizeof(*riid)) == 0) { + api_2 = *factory; + hr = com_proxy_wrap(&proxy, api_2, sizeof(*api_2->lpVtbl)); + + if (FAILED(hr)) { + dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); + + goto fail; + } + + vtbl_2 = proxy->vptr; + vtbl_2->CreateSwapChain = my_IDXGIFactory2_CreateSwapChain; + *factory = proxy; } @@ -255,6 +273,9 @@ fail: if (api_1 != NULL) { IDXGIFactory1_Release(api_1); } + if (api_2 != NULL) { + IDXGIFactory2_Release(api_2); + } return hr; } diff --git a/meson.build b/meson.build index d79ac63..6ffb123 100644 --- a/meson.build +++ b/meson.build @@ -56,6 +56,8 @@ subdir('amcus') subdir('ferrumio') subdir('taikoio') +subdir('exvs2io') subdir('taikohook') subdir('ferrumhook') +subdir('exvs2hook')