diff --git a/Package.mk b/Package.mk index 4562bf1..bb8335c 100644 --- a/Package.mk +++ b/Package.mk @@ -118,6 +118,26 @@ $(BUILD_DIR_ZIP)/mercury.zip: $(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip * +$(BUILD_DIR_ZIP)/chusan.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/chusan + $(V)mkdir -p $(BUILD_DIR_ZIP)/chusan/DEVICE + $(V)cp $(DIST_DIR)/chusan/segatools.ini \ + $(DIST_DIR)/chusan/start.bat \ + $(BUILD_DIR_ZIP)/chusan + $(V)cp $(BUILD_DIR_32)/chusanhook/chusanhook.dll \ + $(BUILD_DIR_ZIP)/chusan/chusanhook_x86.dll + $(V)cp $(BUILD_DIR_64)/chusanhook/chusanhook.dll \ + $(BUILD_DIR_ZIP)/chusan/chusanhook_x64.dll + $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_ZIP)/chusan/inject_x86.exe + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_ZIP)/chusan/inject_x64.exe + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/chusan/DEVICE + for x in exe dll; do strip $(BUILD_DIR_ZIP)/chusan/*.$$x; done + $(V)cd $(BUILD_DIR_ZIP)/chusan ; zip -r ../chusan.zip * $(BUILD_DIR_ZIP)/mu3.zip: $(V)echo ... $@ @@ -152,6 +172,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \ $(BUILD_DIR_ZIP)/idac.zip \ $(BUILD_DIR_ZIP)/swdc.zip \ $(BUILD_DIR_ZIP)/mercury.zip \ + $(BUILD_DIR_ZIP)/chusan.zip \ $(BUILD_DIR_ZIP)/mu3.zip \ CHANGELOG.md \ README.md \ diff --git a/board/led1509306-cmd.h b/board/led1509306-cmd.h new file mode 100644 index 0000000..78019dc --- /dev/null +++ b/board/led1509306-cmd.h @@ -0,0 +1,45 @@ +#pragma once + +#include "board/led1509306-frame.h" + +enum { + LED_15093_06_CMD_RESET = 0x10, + LED_15093_06_CMD_SET_TIMEOUT = 0x11, + LED_15093_06_CMD_SET_DISABLE_RESPONSE = 0x14, + LED_15093_06_CMD_SET_LED = 0x82, + LED_15093_06_CMD_SET_LED_COUNT = 0x86, + LED_15093_06_CMD_BOARD_INFO = 0xF0, + LED_15093_06_CMD_BOARD_STATUS = 0xF1, + LED_15093_06_CMD_FW_SUM = 0xF2, + LED_15093_06_CMD_PROTOCOL_VER = 0xF3, + LED_15093_06_CMD_BOOTLOADER = 0xFD, +}; + +struct led1509306_req_any { + struct led1509306_hdr hdr; + uint8_t cmd; + uint8_t payload[256]; +}; + +struct led1509306_resp_any { + struct led1509306_hdr hdr; + uint8_t status; + uint8_t cmd; + uint8_t report; + uint8_t data[32]; +}; + +struct led1509306_resp_board_info { + struct led1509306_hdr hdr; + uint8_t status; + uint8_t cmd; + uint8_t report; + struct { + char board_num[8]; + uint8_t _0a; + char chip_num[5]; + uint8_t _ff; + uint8_t fw_ver; + // may be some more data after this that isn't checked + } data; +}; \ No newline at end of file diff --git a/board/led1509306-frame.c b/board/led1509306-frame.c new file mode 100644 index 0000000..8e11e8c --- /dev/null +++ b/board/led1509306-frame.c @@ -0,0 +1,194 @@ +#include + +#include +#include +#include +#include + +#include "board/led1509306-frame.h" + +#include "hook/iobuf.h" + +static void led1509306_frame_sync(struct iobuf *src); +static HRESULT led1509306_frame_accept(const struct iobuf *dest); +static HRESULT led1509306_frame_encode_byte(struct iobuf *dest, uint8_t byte); + +/* Frame structure: + + [0] Sync byte (0xE0) + [1] Destination address + [2] Source Address + [3] Length of data/payload + [4] Data/payload + For requests (host to board): + [0] Command + ... Payload + For responses (board to host): + [0] Status + [1] Command + [2] Report + ... Payload + [n] Checksum: Sum of all prior bytes (excluding sync byte) + + Byte stuffing: + + 0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */ + +static void led1509306_frame_sync(struct iobuf *src) +{ + size_t i; + + for (i = 0 ; i < src->pos && src->bytes[i] != 0xE0 ; i++); + + src->pos -= i; + memmove(&src->bytes[0], &src->bytes[i], i); +} + +static HRESULT led1509306_frame_accept(const struct iobuf *dest) +{ + uint8_t checksum; + size_t i; + + if (dest->pos < 3 || dest->pos != dest->bytes[3] + 5) { + return S_FALSE; + } + + checksum = 0; + + for (i = 1 ; i < dest->pos - 1 ; i++) { + checksum += dest->bytes[i]; + } + + //dprintf("LED checksum %02x, expected %02x\n", checksum, dest->bytes[dest->pos - 1]); + + if (checksum != dest->bytes[dest->pos - 1]) { + return HRESULT_FROM_WIN32(ERROR_CRC); + } + + return S_OK; +} + +HRESULT led1509306_frame_decode(struct iobuf *dest, struct iobuf *src) +{ + uint8_t byte; + bool escape; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(src != NULL); + assert(src->bytes != NULL || src->nbytes == 0); + assert(src->pos <= src->nbytes); + + led1509306_frame_sync(src); + + dest->pos = 0; + escape = false; + + for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) { + /* Step the FSM to unstuff another byte */ + + byte = src->bytes[i]; + + if (dest->pos >= dest->nbytes) { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } else if (i == 0) { + dest->bytes[dest->pos++] = byte; + } else if (byte == 0xE0) { + hr = E_FAIL; + } else if (byte == 0xD0) { + if (escape) { + hr = E_FAIL; + } + + escape = true; + } else if (escape) { + dest->bytes[dest->pos++] = byte + 1; + escape = false; + } else { + dest->bytes[dest->pos++] = byte; + } + + /* Try to accept the packet we've built up so far */ + + if (SUCCEEDED(hr)) { + hr = led1509306_frame_accept(dest); + } + } + + /* Handle FSM terminal state */ + + if (hr != S_FALSE) { + /* Frame was either accepted or rejected, remove it from src */ + memmove(&src->bytes[0], &src->bytes[i], src->pos - i); + src->pos -= i; + } + + return hr; +} + +HRESULT led1509306_frame_encode( + struct iobuf *dest, + const void *ptr, + size_t nbytes) +{ + const uint8_t *src; + uint8_t checksum; + uint8_t byte; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(ptr != NULL); + + src = ptr; + + assert(nbytes >= 3 && src[0] == 0xE0 && src[3] + 4 == nbytes); + + if (dest->pos >= dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xE0; + checksum = 0; + // dprintf("%02x ", 0xe0); + + for (i = 1 ; i < nbytes ; i++) { + byte = src[i]; + checksum += byte; + // dprintf("%02x ", byte); + + hr = led1509306_frame_encode_byte(dest, byte); + + if (FAILED(hr)) { + return hr; + } + } + // dprintf("%02x \n", checksum); + + return led1509306_frame_encode_byte(dest, checksum); +} + +static HRESULT led1509306_frame_encode_byte(struct iobuf *dest, uint8_t byte) +{ + if (byte == 0xE0 || byte == 0xD0) { + if (dest->pos + 2 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xD0; + dest->bytes[dest->pos++] = byte - 1; + } else { + if (dest->pos + 1 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = byte; + } + + return S_OK; +} diff --git a/board/led1509306-frame.h b/board/led1509306-frame.h new file mode 100644 index 0000000..a39493e --- /dev/null +++ b/board/led1509306-frame.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include +#include + +#include "hook/iobuf.h" + +enum { + LED_15093_06_FRAME_SYNC = 0xE0, +}; + +struct led1509306_hdr { + uint8_t sync; + uint8_t dest_adr; + uint8_t src_adr; + uint8_t nbytes; +}; + +HRESULT led1509306_frame_decode(struct iobuf *dest, struct iobuf *src); + +HRESULT led1509306_frame_encode( + struct iobuf *dest, + const void *ptr, + size_t nbytes); diff --git a/board/meson.build b/board/meson.build index d463d9a..df6ce92 100644 --- a/board/meson.build +++ b/board/meson.build @@ -20,6 +20,9 @@ board_lib = static_library( 'io3.h', 'io4.c', 'io4.h', + 'led1509306-cmd.h', + 'led1509306-frame.c', + 'led1509306-frame.h', 'sg-cmd.c', 'sg-cmd.h', 'sg-frame.c', diff --git a/chunihook/config.c b/chunihook/config.c index 0f049b2..afc7fae 100644 --- a/chunihook/config.c +++ b/chunihook/config.c @@ -43,6 +43,35 @@ void slider_config_load(struct slider_config *cfg, const wchar_t *filename) cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename); } +void led1509306_config_load(struct led1509306_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + wchar_t tmpstr[16]; + + memset(cfg->board_number, ' ', sizeof(cfg->board_number)); + memset(cfg->chip_number, ' ', sizeof(cfg->chip_number)); + + cfg->enable = GetPrivateProfileIntW(L"ledstrip", L"enable", 1, filename); + cfg->fw_ver = GetPrivateProfileIntW(L"ledstrip", L"fw_ver", 0x90, filename); + cfg->fw_sum = GetPrivateProfileIntW(L"ledstrip", L"fw_sum", 0xadf7, filename); + + GetPrivateProfileStringW(L"ledstrip", L"board_number", L"15093-06", tmpstr, _countof(tmpstr), filename); + size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number)); + for (int i = n; i < sizeof(cfg->board_number); i++) + { + cfg->board_number[i] = ' '; + } + + GetPrivateProfileStringW(L"ledstrip", L"chip_number", L"6710 ", tmpstr, _countof(tmpstr), filename); + n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number)); + for (int i = n; i < sizeof(cfg->chip_number); i++) + { + cfg->chip_number[i] = ' '; + } +} + void chuni_hook_config_load( struct chuni_hook_config *cfg, const wchar_t *filename) @@ -58,4 +87,5 @@ void chuni_hook_config_load( gfx_config_load(&cfg->gfx, filename); chuni_dll_config_load(&cfg->dll, filename); slider_config_load(&cfg->slider, filename); + led1509306_config_load(&cfg->led1509306, filename); } diff --git a/chunihook/config.h b/chunihook/config.h index 7740822..d7a392b 100644 --- a/chunihook/config.h +++ b/chunihook/config.h @@ -9,6 +9,7 @@ #include "chunihook/chuni-dll.h" #include "chunihook/slider.h" +#include "chunihook/led1509306.h" #include "gfxhook/gfx.h" @@ -21,6 +22,7 @@ struct chuni_hook_config { struct gfx_config gfx; struct chuni_dll_config dll; struct slider_config slider; + struct led1509306_config led1509306; }; void chuni_dll_config_load( diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index 5350eb9..5303378 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -9,6 +9,7 @@ #include "chunihook/config.h" #include "chunihook/jvs.h" #include "chunihook/slider.h" +#include "chunihook/led1509306.h" #include "chuniio/chuniio.h" @@ -96,6 +97,12 @@ static DWORD CALLBACK chuni_pre_startup(void) goto fail; } + hr = led1509306_hook_init(&chuni_hook_cfg.led1509306); + + if (FAILED(hr)) { + goto fail; + } + hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, chuni_hook_mod); if (FAILED(hr)) { diff --git a/chunihook/jvs.c b/chunihook/jvs.c index 8c630d4..85c2076 100644 --- a/chunihook/jvs.c +++ b/chunihook/jvs.c @@ -101,13 +101,13 @@ static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out) out->p1 = 0x0000; out->p2 = 0x0000; - if (opbtn & 0x01) { + if (opbtn & CHUNI_IO_OPBTN_TEST) { out->system = 0x80; } else { out->system = 0x00; } - if (opbtn & 0x02) { + if (opbtn & CHUNI_IO_OPBTN_SERVICE) { out->p1 |= 0x4000; } diff --git a/chunihook/led1509306.c b/chunihook/led1509306.c new file mode 100644 index 0000000..16a3b32 --- /dev/null +++ b/chunihook/led1509306.c @@ -0,0 +1,377 @@ +#include + +#include +#include +#include +#include +#include + +#include "board/led1509306-cmd.h" +#include "board/led1509306-frame.h" + +#include "chunihook/led1509306.h" + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT led1509306_handle_irp(struct irp *irp); +static HRESULT led1509306_handle_irp_locked(int board, struct irp *irp); + +static HRESULT led1509306_req_dispatch(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_reset(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_get_board_info(int board); +static HRESULT led1509306_req_get_fw_sum(int board); +static HRESULT led1509306_req_get_protocol_ver(int board); +static HRESULT led1509306_req_get_board_status(int board); +static HRESULT led1509306_req_set_led(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_set_disable_response(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_set_timeout(int board, const struct led1509306_req_any *req); + +static char led1509306_board_num[8]; +static char led1509306_chip_num[5]; +static uint8_t led1509306_fw_ver; +static uint16_t led1509306_fw_sum; +static uint8_t led1509306_board_adr = 2; +static uint8_t led1509306_host_adr = 1; + +#define led1509306_nboards 2 + +typedef struct { + CRITICAL_SECTION lock; + struct uart boarduart; + uint8_t written_bytes[520]; + uint8_t readable_bytes[520]; + bool enable_response; +} _led1509306_per_board_vars; + +_led1509306_per_board_vars led1509306_per_board_vars[led1509306_nboards]; + +HRESULT led1509306_hook_init(const struct led1509306_config *cfg) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + memcpy(led1509306_board_num, cfg->board_number, sizeof(led1509306_board_num)); + memcpy(led1509306_chip_num, cfg->chip_number, sizeof(led1509306_chip_num)); + led1509306_fw_ver = cfg->fw_ver; + led1509306_fw_sum = cfg->fw_sum; + + for (int i = 0; i < led1509306_nboards; i++) + { + _led1509306_per_board_vars *v = &led1509306_per_board_vars[i]; + + InitializeCriticalSection(&v->lock); + + uart_init(&v->boarduart, 10 + i); + v->boarduart.written.bytes = v->written_bytes; + v->boarduart.written.nbytes = sizeof(v->written_bytes); + v->boarduart.readable.bytes = v->readable_bytes; + v->boarduart.readable.nbytes = sizeof(v->readable_bytes); + + v->enable_response = true; + } + + return iohook_push_handler(led1509306_handle_irp); +} + +static HRESULT led1509306_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + for (int i = 0; i < led1509306_nboards; i++) + { + _led1509306_per_board_vars *v = &led1509306_per_board_vars[i]; + struct uart *boarduart = &v->boarduart; + + if (uart_match_irp(boarduart, irp)) + { + CRITICAL_SECTION lock = v->lock; + + EnterCriticalSection(&lock); + hr = led1509306_handle_irp_locked(i, irp); + LeaveCriticalSection(&lock); + + return hr; + } + } + + return iohook_invoke_next(irp); +} + +static HRESULT led1509306_handle_irp_locked(int board, struct irp *irp) +{ + struct led1509306_req_any req; + struct iobuf req_iobuf; + HRESULT hr; + + struct uart *boarduart = &led1509306_per_board_vars[board].boarduart; + + hr = uart_handle_irp(boarduart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 0 + dprintf("TX Buffer:\n"); + dump_iobuf(&boarduart->written); +#endif + + req_iobuf.bytes = (byte*)&req; + req_iobuf.nbytes = sizeof(req.hdr) + sizeof(req.cmd) + sizeof(req.payload); + req_iobuf.pos = 0; + + hr = led1509306_frame_decode(&req_iobuf, &boarduart->written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Chunithm LED Strip: Deframe error: %x\n", (int) hr); + } + + return hr; + } + +#if 0 + dprintf("Deframe Buffer:\n"); + dump_iobuf(&req_iobuf); +#endif + + hr = led1509306_req_dispatch(board, &req); + + if (FAILED(hr)) { + dprintf("Chunithm LED Strip: Processing error: %x\n", (int) hr); + } + } +} + +static HRESULT led1509306_req_dispatch(int board, const struct led1509306_req_any *req) +{ + switch (req->cmd) { + case LED_15093_06_CMD_RESET: + return led1509306_req_reset(board, req); + + case LED_15093_06_CMD_BOARD_INFO: + return led1509306_req_get_board_info(board); + + case LED_15093_06_CMD_FW_SUM: + return led1509306_req_get_fw_sum(board); + + case LED_15093_06_CMD_PROTOCOL_VER: + return led1509306_req_get_protocol_ver(board); + + case LED_15093_06_CMD_BOARD_STATUS: + return led1509306_req_get_board_status(board); + + case LED_15093_06_CMD_SET_LED: + return led1509306_req_set_led(board, req); + + case LED_15093_06_CMD_SET_DISABLE_RESPONSE: + return led1509306_req_set_disable_response(board, req); + + case LED_15093_06_CMD_SET_TIMEOUT: + return led1509306_req_set_timeout(board, req); + + default: + dprintf("Chunithm LED Strip: Unhandled command %02x\n", req->cmd); + + return S_OK; + } +} + +static HRESULT led1509306_req_reset(int board, const struct led1509306_req_any *req) +{ + dprintf("Chunithm LED Strip: Reset (board %u, type %02x)\n", board, req->payload[0]); + + if (req->payload[0] != 0xd9) + dprintf("Chunithm LED Strip: Warning -- Unknown reset type %02x\n", req->payload[0]); + + led1509306_per_board_vars[board].enable_response = true; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_RESET; + resp.report = 1; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_board_info(int board) +{ + dprintf("Chunithm LED Strip: Get board info (board %u)\n", board); + + struct led1509306_resp_board_info resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = sizeof(resp.data) + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_BOARD_INFO; + resp.report = 1; + + memcpy(resp.data.board_num, led1509306_board_num, sizeof(resp.data.board_num)); + resp.data._0a = 0x0a; + memcpy(resp.data.chip_num, led1509306_chip_num, sizeof(resp.data.chip_num)); + resp.data._ff = 0xff; + resp.data.fw_ver = led1509306_fw_ver; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_fw_sum(int board) +{ + dprintf("Chunithm LED Strip: Get firmware checksum (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 2 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_FW_SUM; + resp.report = 1; + + resp.data[0] = (led1509306_fw_sum >> 8) & 0xff; + resp.data[1] = led1509306_fw_sum & 0xff; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_protocol_ver(int board) +{ + dprintf("Chunithm LED Strip: Get protocol version (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_PROTOCOL_VER; + resp.report = 1; + + resp.data[0] = 1; + resp.data[1] = 1; + resp.data[2] = 4; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_board_status(int board) +{ + dprintf("Chunithm LED Strip: Get board status (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 4 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_BOARD_STATUS; + resp.report = 1; + + resp.data[0] = 0; + resp.data[1] = 0; + resp.data[2] = 0; + resp.data[3] = 0; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_led(int board, const struct led1509306_req_any *req) +{ + // dprintf("Chunithm LED Strip: Set LED (board %u)\n", board); + + if (!led1509306_per_board_vars[board].enable_response) + return S_OK; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_LED; + resp.report = 1; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_disable_response(int board, const struct led1509306_req_any *req) +{ + dprintf("Chunithm LED Strip: Disable LED responses (board %u)\n", board); + + led1509306_per_board_vars[board].enable_response = !req->payload[0]; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 1 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_DISABLE_RESPONSE; + resp.report = 1; + + resp.data[0] = req->payload[0]; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_timeout(int board, const struct led1509306_req_any *req) +{ + dprintf("Chunithm LED Strip: Set timeout (board %u)\n", board); + + // not actually implemented, but respond correctly anyway + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 2 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_TIMEOUT; + resp.report = 1; + + resp.data[0] = req->payload[0]; + resp.data[1] = req->payload[1]; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} diff --git a/chunihook/led1509306.h b/chunihook/led1509306.h new file mode 100644 index 0000000..03dc62a --- /dev/null +++ b/chunihook/led1509306.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include + +struct led1509306_config { + bool enable; + bool cvt_port; + char board_number[8]; + char chip_number[5]; + uint8_t fw_ver; + uint16_t fw_sum; +}; + +HRESULT led1509306_hook_init(const struct led1509306_config *cfg); diff --git a/chunihook/meson.build b/chunihook/meson.build index 3f4a35d..b4c464d 100644 --- a/chunihook/meson.build +++ b/chunihook/meson.build @@ -30,5 +30,7 @@ shared_library( 'jvs.h', 'slider.c', 'slider.h', + 'led1509306.c', + 'led1509306.h', ], ) diff --git a/chuniio/chuniio.c b/chuniio/chuniio.c index 4fffe69..86938be 100644 --- a/chuniio/chuniio.c +++ b/chuniio/chuniio.c @@ -3,10 +3,13 @@ #include #include #include +#include #include "chuniio/chuniio.h" #include "chuniio/config.h" +#include "util/dprintf.h" + static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx); static bool chuni_io_coin; @@ -15,6 +18,7 @@ static uint8_t chuni_io_hand_pos; static HANDLE chuni_io_slider_thread; static bool chuni_io_slider_stop_flag; static struct chuni_io_config chuni_io_cfg; +static HANDLE chuni_io_slider_led_port; uint16_t chuni_io_get_api_version(void) { @@ -50,27 +54,48 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams) { size_t i; - if (GetAsyncKeyState(chuni_io_cfg.vk_test)) { - *opbtn |= 0x01; /* Test */ + if (GetAsyncKeyState(chuni_io_cfg.vk_test) & 0x8000) { + *opbtn |= CHUNI_IO_OPBTN_TEST; } - if (GetAsyncKeyState(chuni_io_cfg.vk_service)) { - *opbtn |= 0x02; /* Service */ + if (GetAsyncKeyState(chuni_io_cfg.vk_service) & 0x8000) { + *opbtn |= CHUNI_IO_OPBTN_SERVICE; } - if (GetAsyncKeyState(chuni_io_cfg.vk_ir)) { - if (chuni_io_hand_pos < 6) { - chuni_io_hand_pos++; + if (GetAsyncKeyState(chuni_io_cfg.vk_coin) & 0x8000) { + if (!chuni_io_coin) { + chuni_io_coin = true; + *opbtn |= CHUNI_IO_OPBTN_COIN; } } else { - if (chuni_io_hand_pos > 0) { - chuni_io_hand_pos--; - } + chuni_io_coin = false; } - for (i = 0 ; i < 6 ; i++) { - if (chuni_io_hand_pos > i) { - *beams |= (1 << i); + if (chuni_io_cfg.vk_ir_emu) { + // Use emulated AIR + if (GetAsyncKeyState(chuni_io_cfg.vk_ir_emu)) { + if (chuni_io_hand_pos < 6) { + chuni_io_hand_pos++; + } + } else { + if (chuni_io_hand_pos > 0) { + chuni_io_hand_pos--; + } + } + + for (i = 0 ; i < 6 ; i++) { + if (chuni_io_hand_pos > i) { + *beams |= (1 << i); + } + } + } else { + // Use actual AIR + // IR format is beams[5:0] = {b5,b6,b3,b4,b1,b2}; + for (i = 0 ; i < 3 ; i++) { + if (GetAsyncKeyState(chuni_io_cfg.vk_ir[i*2]) & 0x8000) + *beams |= (1 << (i*2+1)); + if (GetAsyncKeyState(chuni_io_cfg.vk_ir[i*2+1]) & 0x8000) + *beams |= (1 << (i*2)); } } } @@ -82,6 +107,8 @@ HRESULT chuni_io_slider_init(void) void chuni_io_slider_start(chuni_io_slider_callback_t callback) { + BOOL status; + if (chuni_io_slider_thread != NULL) { return; } @@ -93,6 +120,39 @@ void chuni_io_slider_start(chuni_io_slider_callback_t callback) callback, 0, NULL); + + chuni_io_slider_led_port = CreateFileW(chuni_io_cfg.led_com, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (chuni_io_slider_led_port == INVALID_HANDLE_VALUE) + dprintf("Chunithm LEDs: Failed to open COM port (Attempted on %S)\n", chuni_io_cfg.led_com); + else + dprintf("Chunithm LEDs: COM Port Success!\n"); + + DCB dcb_serial_params = { 0 }; + dcb_serial_params.DCBlength = sizeof(dcb_serial_params); + status = GetCommState(chuni_io_slider_led_port, &dcb_serial_params); + + dcb_serial_params.BaudRate = CBR_115200; // Setting BaudRate = 115200 + dcb_serial_params.ByteSize = 8; // Setting ByteSize = 8 + dcb_serial_params.StopBits = ONESTOPBIT;// Setting StopBits = 1 + dcb_serial_params.Parity = NOPARITY; // Setting Parity = None + SetCommState(chuni_io_slider_led_port, &dcb_serial_params); + + COMMTIMEOUTS timeouts = { 0 }; + timeouts.ReadIntervalTimeout = 50; // in milliseconds + timeouts.ReadTotalTimeoutConstant = 50; // in milliseconds + timeouts.ReadTotalTimeoutMultiplier = 10; // in milliseconds + timeouts.WriteTotalTimeoutConstant = 50; // in milliseconds + timeouts.WriteTotalTimeoutMultiplier = 10; // in milliseconds + + SetCommTimeouts(chuni_io_slider_led_port, &timeouts); + } void chuni_io_slider_stop(void) @@ -107,10 +167,34 @@ void chuni_io_slider_stop(void) CloseHandle(chuni_io_slider_thread); chuni_io_slider_thread = NULL; chuni_io_slider_stop_flag = false; + + dprintf("Chunithm LEDs: Closing COM port\n"); + CloseHandle(chuni_io_slider_led_port); } void chuni_io_slider_set_leds(const uint8_t *rgb) { + if (chuni_io_slider_led_port != INVALID_HANDLE_VALUE) + { + char led_buffer[100]; + DWORD bytes_to_write; // No of bytes to write into the port + DWORD bytes_written = 0; // No of bytes written to the port + bytes_to_write = sizeof(led_buffer); + BOOL status; + + led_buffer[0] = 0xAA; + led_buffer[1] = 0xAA; + memcpy(led_buffer+2, rgb, sizeof(uint8_t) * 96); + led_buffer[98] = 0xDD; + led_buffer[99] = 0xDD; + + status = WriteFile(chuni_io_slider_led_port, // Handle to the Serial port + led_buffer, // Data to be written to the port + bytes_to_write, //No of bytes to write + &bytes_written, //Bytes written + NULL); + } + } static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) diff --git a/chuniio/chuniio.h b/chuniio/chuniio.h index 2a24600..08a30ca 100644 --- a/chuniio/chuniio.h +++ b/chuniio/chuniio.h @@ -15,6 +15,12 @@ #include #include +enum { + CHUNI_IO_OPBTN_TEST = 0x01, + CHUNI_IO_OPBTN_SERVICE = 0x02, + CHUNI_IO_OPBTN_COIN = 0x04, +}; + /* Get the version of the Chunithm 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 diff --git a/chuniio/config.c b/chuniio/config.c index 7365f2c..580a056 100644 --- a/chuniio/config.c +++ b/chuniio/config.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "chuniio/config.h" @@ -17,20 +18,35 @@ static const int chuni_io_default_cells[] = { 'S', 'S', 'S', 'S', }; +static const int chuni_io_default_ir[] = { + '4', '5', '6', '7', '8', '9' +}; + void chuni_io_config_load( struct chuni_io_config *cfg, const wchar_t *filename) { wchar_t key[16]; int i; + wchar_t port_input[6]; assert(cfg != NULL); assert(filename != NULL); + // Technically it's io4 but leave this for compatibility with old configs. cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); - cfg->vk_ir = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename); + cfg->vk_ir_emu = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename); + + for (i = 0 ; i < 6 ; i++) { + swprintf_s(key, _countof(key), L"ir%i", i + 1); + cfg->vk_ir[i] = GetPrivateProfileIntW( + L"ir", + key, + chuni_io_default_ir[i], + filename); + } for (i = 0 ; i < 32 ; i++) { swprintf_s(key, _countof(key), L"cell%i", i + 1); @@ -40,4 +56,8 @@ void chuni_io_config_load( chuni_io_default_cells[i], filename); } + + GetPrivateProfileStringW(L"slider", L"ledport", L"COM2", port_input, 6, filename); + wcsncpy(cfg->led_com, L"\\\\.\\", 4); + wcsncat_s(cfg->led_com, 11, port_input, 6); } diff --git a/chuniio/config.h b/chuniio/config.h index fa2e2b0..b615804 100644 --- a/chuniio/config.h +++ b/chuniio/config.h @@ -7,8 +7,10 @@ struct chuni_io_config { uint8_t vk_test; uint8_t vk_service; uint8_t vk_coin; - uint8_t vk_ir; + uint8_t vk_ir_emu; + uint8_t vk_ir[6]; uint8_t vk_cell[32]; + wchar_t led_com[12]; }; void chuni_io_config_load( diff --git a/chusanhook/chuni-dll.c b/chusanhook/chuni-dll.c new file mode 100644 index 0000000..c946043 --- /dev/null +++ b/chusanhook/chuni-dll.c @@ -0,0 +1,118 @@ +#include + +#include +#include + +#include "chusanhook/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_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: %lx: %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/chusanhook/chuni-dll.h b/chusanhook/chuni-dll.h new file mode 100644 index 0000000..ecd88ee --- /dev/null +++ b/chusanhook/chuni-dll.h @@ -0,0 +1,24 @@ +#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); + 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/chusanhook/chusanhook.def b/chusanhook/chusanhook.def new file mode 100644 index 0000000..5039c7d --- /dev/null +++ b/chusanhook/chusanhook.def @@ -0,0 +1,22 @@ +LIBRARY chusanhook + +EXPORTS + Direct3DCreate9 + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll + amDllVideoClose @2 + 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_slider_init + chuni_io_slider_set_leds + chuni_io_slider_start + chuni_io_slider_stop diff --git a/chusanhook/config.c b/chusanhook/config.c new file mode 100644 index 0000000..7b290f3 --- /dev/null +++ b/chusanhook/config.c @@ -0,0 +1,122 @@ +#include +#include + +#include "board/config.h" + +#include "hooklib/config.h" +#include "hooklib/dvd.h" + +#include "gfxhook/config.h" + +#include "platform/config.h" + +#include "chusanhook/config.h" + +// Check windows +#if _WIN32 || _WIN64 + #if _WIN64 + #define ENV64BIT + #else + #define ENV32BIT + #endif +#endif + +// Check GCC +#if __GNUC__ + #if __x86_64__ || __ppc64__ + #define ENV64BIT + #else + #define ENV32BIT + #endif +#endif + +void chuni_dll_config_load( + struct chuni_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + // Workaround for x64/x86 external IO dlls + // path32 for 32bit, path64 for 64bit + // for else.. is that possible? idk + + #if defined(ENV32BIT) + GetPrivateProfileStringW( + L"chuniio", + L"path32", + L"", + cfg->path, + _countof(cfg->path), + filename); + #elif defined(ENV64BIT) + GetPrivateProfileStringW( + L"chuniio", + L"path64", + L"", + cfg->path, + _countof(cfg->path), + filename); + #else + #error "Unknown environment" + #endif + +} + +void slider_config_load(struct slider_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename); +} + +void led1509306_config_load(struct led1509306_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + wchar_t tmpstr[16]; + + memset(cfg->board_number, ' ', sizeof(cfg->board_number)); + memset(cfg->chip_number, ' ', sizeof(cfg->chip_number)); + + cfg->enable = GetPrivateProfileIntW(L"ledstrip", L"enable", 1, filename); + cfg->cvt_port = GetPrivateProfileIntW(L"ledstrip", L"cvt_port", 0, filename); + cfg->fw_ver = GetPrivateProfileIntW(L"ledstrip", L"fw_ver", 0x90, filename); + cfg->fw_sum = GetPrivateProfileIntW(L"ledstrip", L"fw_sum", 0xadf7, filename); + + GetPrivateProfileStringW(L"ledstrip", L"board_number", L"15093-06", tmpstr, _countof(tmpstr), filename); + size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number)); + for (int i = n; i < sizeof(cfg->board_number); i++) + { + cfg->board_number[i] = ' '; + } + + GetPrivateProfileStringW(L"ledstrip", L"chip_number", L"6710 ", tmpstr, _countof(tmpstr), filename); + n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number)); + for (int i = n; i < sizeof(cfg->chip_number); i++) + { + cfg->chip_number[i] = ' '; + } +} + + +void chusan_hook_config_load( + struct chusan_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + memset(cfg, 0, sizeof(*cfg)); + + platform_config_load(&cfg->platform, filename); + aime_config_load(&cfg->aime, filename); + dvd_config_load(&cfg->dvd, filename); + io4_config_load(&cfg->io4, filename); + gfx_config_load(&cfg->gfx, filename); + chuni_dll_config_load(&cfg->dll, filename); + slider_config_load(&cfg->slider, filename); + led1509306_config_load(&cfg->led1509306, filename); +} diff --git a/chusanhook/config.h b/chusanhook/config.h new file mode 100644 index 0000000..e98059e --- /dev/null +++ b/chusanhook/config.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include "board/config.h" + +#include "hooklib/dvd.h" + +#include "gfxhook/config.h" + +#include "platform/config.h" + +#include "chusanhook/chuni-dll.h" +#include "chusanhook/slider.h" +#include "chunihook/led1509306.h" + +struct chusan_hook_config { + struct platform_config platform; + struct aime_config aime; + struct dvd_config dvd; + struct io4_config io4; + struct gfx_config gfx; + struct chuni_dll_config dll; + struct slider_config slider; + struct led1509306_config led1509306; +}; + +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 chusan_hook_config_load( + struct chusan_hook_config *cfg, + const wchar_t *filename); diff --git a/chusanhook/dllmain.c b/chusanhook/dllmain.c new file mode 100644 index 0000000..28aa8e3 --- /dev/null +++ b/chusanhook/dllmain.c @@ -0,0 +1,147 @@ +#include + +#include +#include + +#include "amex/amex.h" + +#include "board/sg-reader.h" +#include "board/vfd.h" + +#include "chusanhook/config.h" +#include "chusanhook/io4.h" +#include "chusanhook/slider.h" +#include "chunihook/led1509306.h" + +#include "chuniio/chuniio.h" + +#include "hook/process.h" + +#include "gfxhook/d3d9.h" +#include "gfxhook/gfx.h" + +#include "hooklib/serial.h" +#include "hooklib/spike.h" + +#include "platform/platform.h" + +#include "util/dprintf.h" + +static HMODULE chusan_hook_mod; +static process_entry_t chusan_startup; +static struct chusan_hook_config chusan_hook_cfg; + +static DWORD CALLBACK chusan_pre_startup(void) +{ + HMODULE d3dc; + HMODULE dbghelp; + HRESULT hr; + + dprintf("--- Begin chusan_pre_startup ---\n"); + + /* Pin the D3D shader compiler. This makes startup much faster. */ + + d3dc = LoadLibraryW(L"D3DCompiler_43.dll"); + + if (d3dc != NULL) { + dprintf("Pinned shader compiler, hMod=%p\n", d3dc); + } else { + dprintf("Failed to load shader compiler!\n"); + } + + /* Pin dbghelp so the path hooks apply to it. */ + + dbghelp = LoadLibraryW(L"dbghelp.dll"); + + if (dbghelp != NULL) { + dprintf("Pinned debug helper library, hMod=%p\n", dbghelp); + } else { + dprintf("Failed to load debug helper library!\n"); + } + + /* Config load */ + + chusan_hook_config_load(&chusan_hook_cfg, L".\\segatools.ini"); + + /* Hook Win32 APIs */ + + dvd_hook_init(&chusan_hook_cfg.dvd, chusan_hook_mod); + gfx_hook_init(&chusan_hook_cfg.gfx); + gfx_d3d9_hook_init(&chusan_hook_cfg.gfx, chusan_hook_mod); + serial_hook_init(); + + /* Initialize emulation hooks */ + + hr = platform_hook_init( + &chusan_hook_cfg.platform, + "SDHD", + "ACA2", + chusan_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = chuni_dll_init(&chusan_hook_cfg.dll, chusan_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = chusan_io4_hook_init(&chusan_hook_cfg.io4); + + if (FAILED(hr)) { + goto fail; + } + + hr = slider_hook_init(&chusan_hook_cfg.slider); + + if (FAILED(hr)) { + goto fail; + } + + hr = led1509306_hook_init(&chusan_hook_cfg.led1509306); + + if (FAILED(hr)) { + goto fail; + } + + + hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, chusan_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + /* Initialize debug helpers */ + + spike_hook_init(L".\\segatools.ini"); + + dprintf("--- End chusan_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return chusan_startup(); + +fail: + ExitProcess(EXIT_FAILURE); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + chusan_hook_mod = mod; + + hr = process_hijack_startup(chusan_pre_startup, &chusan_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/chusanhook/io4.c b/chusanhook/io4.c new file mode 100644 index 0000000..2495369 --- /dev/null +++ b/chusanhook/io4.c @@ -0,0 +1,107 @@ +#include + +#include +#include +#include + +#include "board/io4.h" + +#include "chusanhook/chuni-dll.h" +#include "util/dprintf.h" + +struct chunithm_jvs_ir_mask { + uint16_t p1; + uint16_t p2; +}; + +// Incorrect IR beam mappings retained for backward compatibility +static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks_v1[] = { + { 0, 1 << 13 }, + { 1 << 13, 0 }, + { 0, 1 << 12 }, + { 1 << 12, 0 }, + { 0, 1 << 11 }, + { 1 << 11, 0 }, +}; + +static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = { + { 1 << 13, 0 }, + { 0, 1 << 13 }, + { 1 << 12, 0 }, + { 0, 1 << 12 }, + { 1 << 11, 0 }, + { 0, 1 << 11 }, +}; + +static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state); +static uint16_t coins; + +static const struct io4_ops chusan_io4_ops = { + .poll = chusan_io4_poll, +}; + +HRESULT chusan_io4_hook_init(const struct io4_config* cfg) +{ + HRESULT hr; + + assert(chuni_dll.jvs_init != NULL); + + dprintf("USB I/O: Starting IO backend\n"); + hr = chuni_dll.jvs_init(); + + if (FAILED(hr)) { + dprintf("USB I/O: Backend error, I/O disconnected: %x\n", (int)hr); + + return hr; + } + + io4_hook_init(cfg, &chusan_io4_ops, NULL); + + return S_OK; +} + +static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state) +{ + const struct chunithm_jvs_ir_mask *masks; + uint8_t opbtn; + uint8_t beams; + size_t i; + + memset(state, 0, sizeof(*state)); + + opbtn = 0; + beams = 0; + + chuni_dll.jvs_poll(&opbtn, &beams); + + if (chuni_dll.api_version >= 0x0101) { + // Use correct mapping + masks = chunithm_jvs_ir_masks; + } else { + // Use backwards-compatible incorrect mapping + masks = chunithm_jvs_ir_masks_v1; + } + + if (opbtn & CHUNI_IO_OPBTN_TEST) { + state->buttons[0] |= IO4_BUTTON_TEST; + } + + if (opbtn & CHUNI_IO_OPBTN_SERVICE) { + state->buttons[0] |= IO4_BUTTON_SERVICE; + } + + if (opbtn & CHUNI_IO_OPBTN_COIN) { + coins++; + } + state->chutes[0] = coins << 8; + + for (i = 0; i < 6; i++) { + /* Beam "press" is active-low hence the ~ */ + if (~beams & (1 << i)) { + state->buttons[0] |= masks[i].p1; + state->buttons[1] |= masks[i].p2; + } + } + + return S_OK; +} diff --git a/chusanhook/io4.h b/chusanhook/io4.h new file mode 100644 index 0000000..8d56491 --- /dev/null +++ b/chusanhook/io4.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +HRESULT chusan_io4_hook_init(const struct io4_config *cfg); diff --git a/chusanhook/led1509306.c b/chusanhook/led1509306.c new file mode 100644 index 0000000..6863fa1 --- /dev/null +++ b/chusanhook/led1509306.c @@ -0,0 +1,389 @@ +#include + +#include +#include +#include +#include +#include + +#include "board/led1509306-cmd.h" +#include "board/led1509306-frame.h" + +#include "chunihook/led1509306.h" + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT led1509306_handle_irp(struct irp *irp); +static HRESULT led1509306_handle_irp_locked(int board, struct irp *irp); + +static HRESULT led1509306_req_dispatch(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_reset(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_get_board_info(int board); +static HRESULT led1509306_req_get_fw_sum(int board); +static HRESULT led1509306_req_get_protocol_ver(int board); +static HRESULT led1509306_req_get_board_status(int board); +static HRESULT led1509306_req_set_led(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_set_disable_response(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_set_timeout(int board, const struct led1509306_req_any *req); + +static char led1509306_board_num[8]; +static char led1509306_chip_num[5]; +static uint8_t led1509306_fw_ver; +static uint16_t led1509306_fw_sum; +static uint8_t led1509306_board_adr = 2; +static uint8_t led1509306_host_adr = 1; + +#define led1509306_nboards 2 + +typedef struct { + CRITICAL_SECTION lock; + struct uart boarduart; + uint8_t written_bytes[520]; + uint8_t readable_bytes[520]; + bool enable_response; +} _led1509306_per_board_vars; + +_led1509306_per_board_vars led1509306_per_board_vars[led1509306_nboards]; + +HRESULT led1509306_hook_init(const struct led1509306_config *cfg) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + memcpy(led1509306_board_num, cfg->board_number, sizeof(led1509306_board_num)); + memcpy(led1509306_chip_num, cfg->chip_number, sizeof(led1509306_chip_num)); + led1509306_fw_ver = cfg->fw_ver; + led1509306_fw_sum = cfg->fw_sum; + + int com_ports[2]; + + if (!cfg->cvt_port) { + // SP mode: COM20, COM21 + com_ports[0] = 20; + com_ports[1] = 21; + } else { + // CVT mode: COM3, COM23 + com_ports[0] = 2; + com_ports[1] = 3; + } + + for (int i = 0; i < led1509306_nboards; i++) + { + _led1509306_per_board_vars *v = &led1509306_per_board_vars[i]; + + InitializeCriticalSection(&v->lock); + + uart_init(&v->boarduart, com_ports[i]); + v->boarduart.written.bytes = v->written_bytes; + v->boarduart.written.nbytes = sizeof(v->written_bytes); + v->boarduart.readable.bytes = v->readable_bytes; + v->boarduart.readable.nbytes = sizeof(v->readable_bytes); + + v->enable_response = true; + } + + return iohook_push_handler(led1509306_handle_irp); +} + +static HRESULT led1509306_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + for (int i = 0; i < led1509306_nboards; i++) + { + _led1509306_per_board_vars *v = &led1509306_per_board_vars[i]; + struct uart *boarduart = &v->boarduart; + + if (uart_match_irp(boarduart, irp)) + { + CRITICAL_SECTION lock = v->lock; + + EnterCriticalSection(&lock); + hr = led1509306_handle_irp_locked(i, irp); + LeaveCriticalSection(&lock); + + return hr; + } + } + + return iohook_invoke_next(irp); +} + +static HRESULT led1509306_handle_irp_locked(int board, struct irp *irp) +{ + struct led1509306_req_any req; + struct iobuf req_iobuf; + HRESULT hr; + + struct uart *boarduart = &led1509306_per_board_vars[board].boarduart; + + hr = uart_handle_irp(boarduart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 0 + dprintf("TX Buffer:\n"); + dump_iobuf(&boarduart->written); +#endif + + req_iobuf.bytes = (byte*)&req; + req_iobuf.nbytes = sizeof(req.hdr) + sizeof(req.cmd) + sizeof(req.payload); + req_iobuf.pos = 0; + + hr = led1509306_frame_decode(&req_iobuf, &boarduart->written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Chunithm LED Strip: Deframe error: %x\n", (int) hr); + } + + return hr; + } + +#if 0 + dprintf("Deframe Buffer:\n"); + dump_iobuf(&req_iobuf); +#endif + + hr = led1509306_req_dispatch(board, &req); + + if (FAILED(hr)) { + dprintf("Chunithm LED Strip: Processing error: %x\n", (int) hr); + } + } +} + +static HRESULT led1509306_req_dispatch(int board, const struct led1509306_req_any *req) +{ + switch (req->cmd) { + case LED_15093_06_CMD_RESET: + return led1509306_req_reset(board, req); + + case LED_15093_06_CMD_BOARD_INFO: + return led1509306_req_get_board_info(board); + + case LED_15093_06_CMD_FW_SUM: + return led1509306_req_get_fw_sum(board); + + case LED_15093_06_CMD_PROTOCOL_VER: + return led1509306_req_get_protocol_ver(board); + + case LED_15093_06_CMD_BOARD_STATUS: + return led1509306_req_get_board_status(board); + + case LED_15093_06_CMD_SET_LED: + return led1509306_req_set_led(board, req); + + case LED_15093_06_CMD_SET_DISABLE_RESPONSE: + return led1509306_req_set_disable_response(board, req); + + case LED_15093_06_CMD_SET_TIMEOUT: + return led1509306_req_set_timeout(board, req); + + default: + dprintf("Chunithm LED Strip: Unhandled command %02x\n", req->cmd); + + return S_OK; + } +} + +static HRESULT led1509306_req_reset(int board, const struct led1509306_req_any *req) +{ + dprintf("Chunithm LED Strip: Reset (board %u, type %02x)\n", board, req->payload[0]); + + if (req->payload[0] != 0xd9) + dprintf("Chunithm LED Strip: Warning -- Unknown reset type %02x\n", req->payload[0]); + + led1509306_per_board_vars[board].enable_response = true; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_RESET; + resp.report = 1; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_board_info(int board) +{ + dprintf("Chunithm LED Strip: Get board info (board %u)\n", board); + + struct led1509306_resp_board_info resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = sizeof(resp.data) + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_BOARD_INFO; + resp.report = 1; + + memcpy(resp.data.board_num, led1509306_board_num, sizeof(resp.data.board_num)); + resp.data._0a = 0x0a; + memcpy(resp.data.chip_num, led1509306_chip_num, sizeof(resp.data.chip_num)); + resp.data._ff = 0xff; + resp.data.fw_ver = led1509306_fw_ver; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_fw_sum(int board) +{ + dprintf("Chunithm LED Strip: Get firmware checksum (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 2 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_FW_SUM; + resp.report = 1; + + resp.data[0] = (led1509306_fw_sum >> 8) & 0xff; + resp.data[1] = led1509306_fw_sum & 0xff; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_protocol_ver(int board) +{ + dprintf("Chunithm LED Strip: Get protocol version (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_PROTOCOL_VER; + resp.report = 1; + + resp.data[0] = 1; + resp.data[1] = 1; + resp.data[2] = 4; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_board_status(int board) +{ + dprintf("Chunithm LED Strip: Get board status (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 4 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_BOARD_STATUS; + resp.report = 1; + + resp.data[0] = 0; + resp.data[1] = 0; + resp.data[2] = 0; + resp.data[3] = 0; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_led(int board, const struct led1509306_req_any *req) +{ + // dprintf("Chunithm LED Strip: Set LED (board %u)\n", board); + + if (!led1509306_per_board_vars[board].enable_response) + return S_OK; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_LED; + resp.report = 1; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_disable_response(int board, const struct led1509306_req_any *req) +{ + dprintf("Chunithm LED Strip: Disable LED responses (board %u)\n", board); + + led1509306_per_board_vars[board].enable_response = !req->payload[0]; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 1 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_DISABLE_RESPONSE; + resp.report = 1; + + resp.data[0] = req->payload[0]; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_timeout(int board, const struct led1509306_req_any *req) +{ + dprintf("Chunithm LED Strip: Set timeout (board %u)\n", board); + + // not actually implemented, but respond correctly anyway + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 2 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_TIMEOUT; + resp.report = 1; + + resp.data[0] = req->payload[0]; + resp.data[1] = req->payload[1]; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} diff --git a/chusanhook/led1509306.h b/chusanhook/led1509306.h new file mode 100644 index 0000000..15c7c3e --- /dev/null +++ b/chusanhook/led1509306.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +struct led1509306_config { + bool enable; + char board_number[8]; + char chip_number[5]; + uint8_t fw_ver; + uint16_t fw_sum; +}; + +HRESULT led1509306_hook_init(const struct led1509306_config *cfg); diff --git a/chusanhook/meson.build b/chusanhook/meson.build new file mode 100644 index 0000000..02c82b0 --- /dev/null +++ b/chusanhook/meson.build @@ -0,0 +1,34 @@ +shared_library( + 'chusanhook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'chusanhook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + aimeio_lib, + board_lib, + chuniio_lib, + gfxhook_lib, + hooklib_lib, + platform_lib, + util_lib, + ], + sources : [ + 'chuni-dll.c', + 'chuni-dll.h', + 'config.c', + 'config.h', + 'dllmain.c', + 'io4.c', + 'io4.h', + 'slider.c', + 'slider.h', + 'led1509306.c', + 'led1509306.h', + ], +) diff --git a/chusanhook/slider.c b/chusanhook/slider.c new file mode 100644 index 0000000..78f5d53 --- /dev/null +++ b/chusanhook/slider.c @@ -0,0 +1,249 @@ +#include + +#include +#include +#include +#include +#include + +#include "board/slider-cmd.h" +#include "board/slider-frame.h" + +#include "chusanhook/chuni-dll.h" +#include "chusanhook/slider.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 void slider_res_auto_scan(const uint8_t *state); + +static CRITICAL_SECTION slider_lock; +static struct uart slider_uart; +static uint8_t slider_written_bytes[520]; +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; + } + + InitializeCriticalSection(&slider_lock); + + uart_init(&slider_uart, 1); + 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); + + return 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; + + if (irp->op == IRP_OP_OPEN) { + dprintf("Chunithm slider: Starting backend\n"); + hr = chuni_dll.slider_init(); + + if (FAILED(hr)) { + dprintf("Chunithm slider: Backend error: %x\n", (int) hr); + + return 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("Chunithm 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("Chunithm 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_SET_LED: + return slider_req_set_led(&req->set_led); + + case SLIDER_CMD_AUTO_SCAN_START: + return slider_req_auto_scan_start(); + + case SLIDER_CMD_AUTO_SCAN_STOP: + return slider_req_auto_scan_stop(); + + default: + dprintf("Unhandled command %02x\n", req->hdr.cmd); + + return S_OK; + } +} + +static HRESULT slider_req_reset(void) +{ + struct slider_hdr resp; + + dprintf("Chunithm 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("Chunithm 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); + + strcpy_s( + resp.version, + sizeof(resp.version), + "15330 \xA0" "06712\xFF" "\x90"); + + return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT slider_req_auto_scan_start(void) +{ + assert(chuni_dll.slider_start != NULL); + + dprintf("Chunithm slider: Start slider notifications\n"); + chuni_dll.slider_start(slider_res_auto_scan); + + /* This message is not acknowledged */ + + return S_OK; +} + +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 + to take slider_lock, which we are currently holding) before noticing that + it needs to shut down. Unlock here so that we don't deadlock in that + situation. */ + + LeaveCriticalSection(&slider_lock); + chuni_dll.slider_stop(); + 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) +{ + assert(chuni_dll.slider_set_leds != NULL); + + chuni_dll.slider_set_leds(req->payload.rgb); + + /* This message is not acknowledged */ + + return S_OK; +} + +static void slider_res_auto_scan(const uint8_t *state) +{ + struct slider_resp_auto_scan resp; + + resp.hdr.sync = SLIDER_FRAME_SYNC; + resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN; + resp.hdr.nbytes = sizeof(resp.pressure); + memcpy(resp.pressure, state, sizeof(resp.pressure)); + + EnterCriticalSection(&slider_lock); + slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); + LeaveCriticalSection(&slider_lock); +} diff --git a/chusanhook/slider.h b/chusanhook/slider.h new file mode 100644 index 0000000..7053e04 --- /dev/null +++ b/chusanhook/slider.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +struct slider_config { + bool enable; +}; + +HRESULT slider_hook_init(const struct slider_config *cfg); diff --git a/dist/chusan/segatools.ini b/dist/chusan/segatools.ini new file mode 100644 index 0000000..196766f --- /dev/null +++ b/dist/chusan/segatools.ini @@ -0,0 +1,112 @@ +[vfs] +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs= +; Insert the path to the game Option directory here (contains Axxx directories) +option= +; Create an empty directory somewhere and insert the path here. +; This directory may be shared between multiple SEGA games. +; NOTE: This has nothing to do with Windows %APPDATA%. +appdata= + +[aime] +; Enable aime reader emulation. +enable=1 +; Enable high baud rate. +;highbaud=1 + +[aimeio] +; x64 aimeio dll path. +; Uncomment this if you have custom aime implementation. +;path64= + +[dns] +; Insert the hostname or IP address of the server you wish to use here. +; Note that 127.0.0.1, localhost etc are specifically rejected. +default=127.0.0.1 + +[netenv] +; Simulate an ideal LAN environment. This may interfere with head-to-head play. +; Chunithm is extremely picky about its LAN environment, so leaving this +; setting enabled is strongly recommended. +enable=1 + +[keychip] +; The /24 LAN subnet that the emulated keychip will tell the game to expect. +; If you disable netenv then you must set this to your LAN's IP subnet, and +; that subnet must start with 192.168. +subnet=192.168.100.0 + +[gfx] +; Force the game to run windowed. +windowed=1 +; Add a frame to the game window if running windowed. +framed=1 +; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen) +monitor=0 + +; ----------------------------------------------------------------------------- +; Input settings +; ----------------------------------------------------------------------------- + +; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal +; (not prefixed with 0x) virtual-key codes, a list of which can be found here: +; +; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +; +; This is, admittedly, not the most user-friendly configuration method in the +; world. An improved solution will be provided later. + +[io3] +; Test button virtual-key code. Default is the 1 key. +test=0x31 +; Service button virtual-key code. Default is the 2 key. +service=0x32 +; Keyboard button to increment coin counter. Default is the 3 key. +coin=0x33 +; Set to 0 for enable separate ir control. Deafult is space key. +ir=0x20 + +[io4] +enable=1 + +[ledstrip] +; Set to 1 if running game in CVT mode. +cvt_port=0 + +[chuniio] +; Uncomment this if you have custom chuniio implementation. +; x86 chuniio to path32, x64 to path64. Both are necessary. +;path32= +;path64= + +[slider] +; Enable slider emulation. If you have real AC slider, set this to 0. +; Slider serial port must be COM1. +;enable=1 + +; Key bindings for each of the 32 touch cells. The default key map, depicted +; in left-to-right order, is as follows: +; +; SSSSDDDDFFFFGGGGHHHHJJJJKKKKLLLL +; +; Touch cells are numbered FROM RIGHT TO LEFT! starting from 1. This is in +; order to match the numbering used in the operator menu and service manual. +; +; Uncomment and complete the following sequence of settings to configure a +; custom high-precision touch strip controller if you have one. +;cell1=0x53 +;cell2=0x53 +; ... etc ... +;cell31=0x53 +;cell32=0x53 + +; Enable slider LED serial output. This follows OpeNITHM Serial LED Protocol. +; eg. COM5 +;ledport= + +[ir] +; Uncomment and complete the following sequence of settings to configure a +; custom ir-cappable controller if you have one. +;ir6=0x53 +; ... etc ... +;ir1=0x53 \ No newline at end of file diff --git a/dist/chusan/start.bat b/dist/chusan/start.bat new file mode 100644 index 0000000..dbd1a8b --- /dev/null +++ b/dist/chusan/start.bat @@ -0,0 +1,11 @@ +@echo off + +pushd %~dp0 + +start /min inject_x64.exe -d -k chusanhook_x64.dll amdaemon.exe -f -c config_common.json config_server.json config_sp.json +inject_x86.exe -d -k chusanhook_x86.dll chusanApp.exe +taskkill /f /im amdaemon.exe > nul 2>&1 + +echo. +echo Game processes have terminated +pause \ No newline at end of file diff --git a/meson.build b/meson.build index e346cea..5972de2 100644 --- a/meson.build +++ b/meson.build @@ -71,6 +71,7 @@ subdir('idzhook') subdir('idachook') subdir('swdchook') subdir('minihook') +subdir('chusanhook') subdir('mu3hook') subdir('mercuryhook') subdir('cxbhook')