From 24b82b64e6f92aaae09acbe1348434c3ea246106 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 16 Mar 2019 11:57:17 -0400 Subject: [PATCH] divahook: Add initial Project Diva hook --- divahook/_com10.c | 38 ++++++ divahook/_com10.h | 5 + divahook/divahook.def | 1 + divahook/dllmain.c | 84 +++++++++++++ divahook/jvs.c | 93 ++++++++++++++ divahook/jvs.h | 3 + divahook/meson.build | 28 +++++ divahook/slider-hook.c | 272 +++++++++++++++++++++++++++++++++++++++++ divahook/slider-hook.h | 3 + meson.build | 1 + 10 files changed, 528 insertions(+) create mode 100644 divahook/_com10.c create mode 100644 divahook/_com10.h create mode 100644 divahook/divahook.def create mode 100644 divahook/dllmain.c create mode 100644 divahook/jvs.c create mode 100644 divahook/jvs.h create mode 100644 divahook/meson.build create mode 100644 divahook/slider-hook.c create mode 100644 divahook/slider-hook.h diff --git a/divahook/_com10.c b/divahook/_com10.c new file mode 100644 index 0000000..7eb41b5 --- /dev/null +++ b/divahook/_com10.c @@ -0,0 +1,38 @@ +#include + +#include +#include + +#include "board/sg-reader.h" + +#include "divahook/_com10.h" + +#include "hook/iohook.h" + +static HRESULT com10_handle_irp(struct irp *irp); + +static struct sg_reader com10_reader; + +HRESULT com10_hook_init(void) +{ + HRESULT hr; + + hr = sg_reader_init(&com10_reader, 10); + + if (FAILED(hr)) { + return hr; + } + + return iohook_push_handler(com10_handle_irp); +} + +static HRESULT com10_handle_irp(struct irp *irp) +{ + assert(irp != NULL); + + if (!sg_reader_match_irp(&com10_reader, irp)) { + return iohook_invoke_next(irp); + } + + return sg_reader_handle_irp(&com10_reader, irp); +} diff --git a/divahook/_com10.h b/divahook/_com10.h new file mode 100644 index 0000000..2bedc59 --- /dev/null +++ b/divahook/_com10.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +HRESULT com10_hook_init(void); diff --git a/divahook/divahook.def b/divahook/divahook.def new file mode 100644 index 0000000..9cef741 --- /dev/null +++ b/divahook/divahook.def @@ -0,0 +1 @@ +LIBRARY divahook diff --git a/divahook/dllmain.c b/divahook/dllmain.c new file mode 100644 index 0000000..11d3863 --- /dev/null +++ b/divahook/dllmain.c @@ -0,0 +1,84 @@ +#include + +#include +#include + +#include "amex/ds.h" +#include "amex/eeprom.h" +#include "amex/gpio.h" +#include "amex/jvs.h" +#include "amex/sram.h" + +#include "divahook/_com10.h" +#include "divahook/jvs.h" +#include "divahook/slider-hook.h" + +#include "hook/process.h" + +#include "hooklib/serial.h" + +#include "platform/hwmon.h" +#include "platform/nusec.h" + +#include "util/clock.h" +#include "util/dprintf.h" +#include "util/gfx.h" +#include "util/spike.h" + +static process_entry_t diva_startup; + +static DWORD CALLBACK diva_pre_startup(void) +{ + dprintf("--- Begin diva_pre_startup ---\n"); + + /* Hook Win32 APIs */ + + clock_hook_init(); + serial_hook_init(); + + /* Initialize platform API emulation */ + + hwmon_hook_init(); + nusec_hook_init(); + + /* Initialize AMEX emulation */ + + ds_hook_init(); + eeprom_hook_init(); + gpio_hook_init(); + jvs_hook_init(); + sram_hook_init(); + + /* Initialize Project Diva I/O board emulation */ + + com10_hook_init(); + diva_jvs_init(); + slider_hook_init(); + + /* Initialize debug helpers */ + + spike_hook_init("divaspike.txt"); + + dprintf("--- End diva_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return diva_startup(); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + hr = process_hijack_startup(diva_pre_startup, &diva_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/divahook/jvs.c b/divahook/jvs.c new file mode 100644 index 0000000..b25cbaa --- /dev/null +++ b/divahook/jvs.c @@ -0,0 +1,93 @@ +#include + +#include +#include +#include + +#include "amex/jvs.h" + +#include "board/io3.h" + +#include "jvs/jvs-bus.h" + +#include "util/dprintf.h" + +static void diva_jvs_read_switches(void *ctx, struct io3_switch_state *out); +static uint16_t diva_jvs_consume_coins(void *ctx, uint8_t slot_no); + +static const struct io3_ops diva_jvs_io3_ops = { + .read_switches = diva_jvs_read_switches, + .consume_coins = diva_jvs_consume_coins, +}; + +static struct io3 diva_jvs_io3; +static bool diva_jvs_coin; + +void diva_jvs_init(void) +{ + io3_init(&diva_jvs_io3, NULL, &diva_jvs_io3_ops, NULL); + jvs_attach(&diva_jvs_io3.jvs); +} + +static void diva_jvs_read_switches(void *ctx, struct io3_switch_state *out) +{ + assert(out != NULL); + + /* Update gameplay buttons (P2 JVS input is not even polled) */ + + if (GetAsyncKeyState('S')) { + out->p1 |= 1 << 9; + } + + if (GetAsyncKeyState('F')) { + out->p1 |= 1 << 8; + } + + if (GetAsyncKeyState('J')) { + out->p1 |= 1 << 7; + } + + if (GetAsyncKeyState('L')) { + out->p1 |= 1 << 6; + } + + /* Update start button */ + + if (GetAsyncKeyState(VK_SPACE)) { + out->p1 |= 1 << 15; + } + + /* Update test/service buttons */ + + if (GetAsyncKeyState('1')) { + out->system = 0x80; + } else { + out->system = 0; + } + + if (GetAsyncKeyState('2')) { + out->p1 |= 0x4000; + } +} + +static uint16_t diva_jvs_consume_coins(void *ctx, uint8_t slot_no) +{ + if (slot_no > 0) { + return 0; + } + + if (GetAsyncKeyState('3')) { + if (diva_jvs_coin) { + return 0; + } else { + dprintf("Diva JVS: Coin drop\n"); + diva_jvs_coin = true; + + return 1; + } + } else { + diva_jvs_coin = false; + + return 0; + } +} diff --git a/divahook/jvs.h b/divahook/jvs.h new file mode 100644 index 0000000..1ca7a32 --- /dev/null +++ b/divahook/jvs.h @@ -0,0 +1,3 @@ +#pragma once + +void diva_jvs_init(void); diff --git a/divahook/meson.build b/divahook/meson.build new file mode 100644 index 0000000..0814908 --- /dev/null +++ b/divahook/meson.build @@ -0,0 +1,28 @@ +shared_library( + 'divahook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'divahook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + aimeio_dll, + amex_lib, + board_lib, + jvs_lib, + platform_lib, + util_lib, + ], + sources : [ + '_com10.c', + 'dllmain.c', + 'jvs.c', + 'jvs.h', + 'slider-hook.c', + 'slider-hook.h', + ], +) diff --git a/divahook/slider-hook.c b/divahook/slider-hook.c new file mode 100644 index 0000000..2713715 --- /dev/null +++ b/divahook/slider-hook.c @@ -0,0 +1,272 @@ +#include + +#include +#include +#include +#include +#include + +#include "board/slider-cmd.h" +#include "board/slider-frame.h" + +#include "divahook/slider-hook.h" + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT slider_handle_irp(struct irp *irp); +static HRESULT slider_handle_irp_locked(struct irp *irp); + +static HRESULT slider_req_dispatch(const union slider_req_any *req); +static HRESULT slider_req_reset(void); +static HRESULT slider_req_get_board_info(void); +static HRESULT slider_req_auto_scan_start(void); +static HRESULT slider_req_auto_scan_stop(void); +static HRESULT slider_req_set_led(const struct slider_req_set_led *req); + +static unsigned int __stdcall slider_thread_proc(void *ctx); + +static CRITICAL_SECTION slider_lock; +static struct uart slider_uart; +static uint8_t slider_written_bytes[520]; +static uint8_t slider_readable_bytes[520]; + +static HANDLE slider_thread; +static bool slider_stop; + +void slider_hook_init(void) +{ + InitializeCriticalSection(&slider_lock); + + uart_init(&slider_uart, 11); + slider_uart.written.bytes = slider_written_bytes; + slider_uart.written.nbytes = sizeof(slider_written_bytes); + slider_uart.readable.bytes = slider_readable_bytes; + slider_uart.readable.nbytes = sizeof(slider_readable_bytes); + + iohook_push_handler(slider_handle_irp); +} + +static HRESULT slider_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&slider_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&slider_lock); + hr = slider_handle_irp_locked(irp); + LeaveCriticalSection(&slider_lock); + + return hr; +} + +static HRESULT slider_handle_irp_locked(struct irp *irp) +{ + union slider_req_any req; + struct iobuf req_iobuf; + HRESULT hr; + + hr = uart_handle_irp(&slider_uart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 0 + dprintf("TX Buffer:\n"); + dump_iobuf(&slider_uart.written); +#endif + + req_iobuf.bytes = req.bytes; + req_iobuf.nbytes = sizeof(req.bytes); + req_iobuf.pos = 0; + + hr = slider_frame_decode(&req_iobuf, &slider_uart.written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Diva slider: Deframe error: %x\n", (int) hr); + } + + return hr; + } + +#if 0 + dprintf("Deframe Buffer:\n"); + dump_iobuf(&req_iobuf); +#endif + + hr = slider_req_dispatch(&req); + + if (FAILED(hr)) { + dprintf("Diva slider: Processing error: %x\n", (int) hr); + } + } +} + +static HRESULT slider_req_dispatch(const union slider_req_any *req) +{ + switch (req->hdr.cmd) { + case SLIDER_CMD_RESET: + return slider_req_reset(); + + case SLIDER_CMD_GET_BOARD_INFO: + return slider_req_get_board_info(); + + case SLIDER_CMD_AUTO_SCAN_START: + return slider_req_auto_scan_start(); + + case SLIDER_CMD_AUTO_SCAN_STOP: + return slider_req_auto_scan_stop(); + + case SLIDER_CMD_SET_LED_DIVA: + return slider_req_set_led(&req->set_led); + + default: + dprintf("Unhandled command %02x\n", req->hdr.cmd); + + return S_OK; + } +} + +static HRESULT slider_req_reset(void) +{ + struct slider_hdr resp; + + dprintf("Diva slider: Reset\n"); + + resp.sync = 0xFF; + resp.cmd = SLIDER_CMD_RESET; + resp.nbytes = 0; + + return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT slider_req_get_board_info(void) +{ + struct slider_resp_get_board_info resp; + + dprintf("Diva slider: Get firmware version\n"); + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = SLIDER_FRAME_SYNC; + resp.hdr.cmd = SLIDER_CMD_GET_BOARD_INFO; + resp.hdr.nbytes = sizeof(resp.version); + + memset(resp.version, 0, sizeof(resp.version)); + strcpy_s( + resp.version, + sizeof(resp.version), + "15275 \xA0" "06712\xFF" "\x90"); + + return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT slider_req_auto_scan_start(void) +{ + dprintf("Diva slider: Start slider thread\n"); + + if (slider_thread != NULL) { + dprintf("Thread is already running\n"); + + return S_OK; + } + + slider_thread = (HANDLE) _beginthreadex( + NULL, + 0, + slider_thread_proc, + NULL, + 0, + NULL); + + if (slider_thread == NULL) { + dprintf("Thread launch failed\n"); + } + + /* This message is not acknowledged */ + + return S_OK; +} + +static HRESULT slider_req_auto_scan_stop(void) +{ + struct slider_hdr resp; + + dprintf("Diva slider: Stop slider thread\n"); + + if (slider_thread != NULL) { + slider_stop = true; + LeaveCriticalSection(&slider_lock); + + WaitForSingleObject(slider_thread, INFINITE); + CloseHandle(slider_thread); + slider_thread = NULL; + slider_stop = false; + + dprintf("Diva slider: Thread has terminated\n"); + + EnterCriticalSection(&slider_lock); + } + + resp.sync = SLIDER_FRAME_SYNC; + resp.cmd = SLIDER_CMD_AUTO_SCAN_STOP; + resp.nbytes = 0; + + return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT slider_req_set_led(const struct slider_req_set_led *req) +{ + /* This message is not acknowledged */ + return S_OK; +} + +static unsigned int WINAPI slider_thread_proc(void *ctx) +{ + static const int keys[] = { + 'Q', 'W', 'E', 'R', 'U', 'I', 'O', 'P', + }; + + struct slider_resp_auto_scan resp; + uint8_t pressure; + bool stop; + size_t i; + + for (;;) { + resp.hdr.sync = SLIDER_FRAME_SYNC; + resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN; + resp.hdr.nbytes = sizeof(resp.pressure); + + for (i = 0 ; i < 8 ; i++) { + pressure = GetAsyncKeyState(keys[i]) ? 20 : 0; + memset(&resp.pressure[28 - 4 * i], pressure, 4); + } + + EnterCriticalSection(&slider_lock); + + stop = slider_stop; + + if (!stop) { + slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); + } + + LeaveCriticalSection(&slider_lock); + + if (stop) { + return 0; + } + + Sleep(1); + } +} diff --git a/divahook/slider-hook.h b/divahook/slider-hook.h new file mode 100644 index 0000000..eb429e9 --- /dev/null +++ b/divahook/slider-hook.h @@ -0,0 +1,3 @@ +#pragma once + +void slider_hook_init(void); diff --git a/meson.build b/meson.build index 2df19dd..0169c34 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ subdir('util') subdir('aimeio') subdir('cardhook') subdir('chunihook') +subdir('divahook') subdir('idzhook') subdir('minihook') subdir('zinput')