Add carolhook for Wonderland Wars

This commit is contained in:
Hay1tsme 2022-12-10 22:01:52 -05:00 committed by Hay1tsme
parent 04ca905467
commit 376dad0bc8
29 changed files with 1296 additions and 1 deletions

27
.vscode/settings.json vendored
View File

@ -1,3 +1,28 @@
{
"editor.formatOnSave": false
"editor.formatOnSave": false,
"files.associations": {
"assert.h": "c",
"config.h": "c",
"backend.h": "c",
"idacio.h": "c",
"idac-dll.h": "c",
"d3d11.h": "c",
"mu3-dll.h": "c",
"printer.h": "c",
"netenv.h": "c",
"stdint.h": "c",
"string.h": "c",
"platform.h": "c",
"jvs.h": "c",
"sg-reader.h": "c",
"carol-dll.h": "c",
"slider.h": "c",
"slider-cmd.h": "c",
"touch.h": "c",
"iobd.h": "c",
"stdbool.h": "c",
"sg-frame.h": "c",
"sg-cmd.h": "c",
"controlbd.h": "c"
}
}

View File

@ -22,6 +22,8 @@ COPY chuniio chuniio
COPY dist dist
COPY divahook divahook
COPY divaio divaio
COPY carolhook carolhook
COPY carolio carolio
COPY doc doc
COPY hooklib hooklib
COPY iccard iccard

View File

@ -13,6 +13,36 @@ $(BUILD_DIR_ZIP)/chuni.zip:
$(V)strip $(BUILD_DIR_ZIP)/chuni/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/chuni ; zip -r ../chuni.zip *
$(BUILD_DIR_ZIP)/diva.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/divahook/divahook.dll \
$(DIST_DIR)/diva/segatools.ini \
$(DIST_DIR)/diva/start.bat \
$(BUILD_DIR_ZIP)/diva
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/diva/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/diva/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/diva ; zip -r ../diva.zip *
$(BUILD_DIR_ZIP)/carol.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/carolhook/carolhook.dll \
$(DIST_DIR)/carol/segatools.ini \
$(DIST_DIR)/carol/start.bat \
$(BUILD_DIR_ZIP)/carol
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/carol/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/carol/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/carol ; zip -r ../carol.zip *
$(BUILD_DIR_ZIP)/idz.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/idz
@ -69,6 +99,8 @@ $(BUILD_DIR_ZIP)/doc.zip: \
$(BUILD_DIR_ZIP)/segatools.zip: \
$(BUILD_DIR_ZIP)/chuni.zip \
$(BUILD_DIR_ZIP)/carol.zip \
$(BUILD_DIR_ZIP)/diva.zip \
$(BUILD_DIR_ZIP)/doc.zip \
$(BUILD_DIR_ZIP)/idz.zip \
$(BUILD_DIR_ZIP)/mercury.zip \

112
carolhook/carol-dll.c Normal file
View File

@ -0,0 +1,112 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "carolhook/carol-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym carol_dll_syms[] = {
{
.sym = "carol_io_jvs_init",
.off = offsetof(struct carol_dll, jvs_init),
}, {
.sym = "carol_io_jvs_poll",
.off = offsetof(struct carol_dll, jvs_poll),
}, {
.sym = "carol_io_jvs_read_coin_counter",
.off = offsetof(struct carol_dll, jvs_read_coin_counter),
}, {
.sym = "carol_io_touch_init",
.off = offsetof(struct carol_dll, touch_init),
}, {
.sym = "carol_io_controlbd_init",
.off = offsetof(struct carol_dll, controlbd_init),
}
};
struct carol_dll carol_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 carol_dll_init(const struct carol_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("carol IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("carol IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "carol_io_get_api_version");
if (get_api_version != NULL) {
carol_dll.api_version = get_api_version();
} else {
carol_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose carol_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (carol_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("carol IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
carol_dll.api_version);
goto end;
}
sym = carol_dll_syms;
hr = dll_bind(&carol_dll, src, &sym, _countof(carol_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("carol 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;
}

22
carolhook/carol-dll.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <windows.h>
#include "carolio/carolio.h"
struct carol_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 (*touch_init)();
HRESULT (*controlbd_init)();
};
struct carol_dll_config {
wchar_t path[MAX_PATH];
};
extern struct carol_dll carol_dll;
HRESULT carol_dll_init(const struct carol_dll_config *cfg, HINSTANCE self);

19
carolhook/carolhook.def Normal file
View File

@ -0,0 +1,19 @@
LIBRARY carolhook
EXPORTS
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
carol_io_get_api_version
carol_io_jvs_init
carol_io_jvs_poll
carol_io_jvs_read_coin_counter
carol_io_touch_init
carol_io_controlbd_init

76
carolhook/config.c Normal file
View File

@ -0,0 +1,76 @@
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include "amex/amex.h"
#include "amex/config.h"
#include "gfxhook/config.h"
#include "board/config.h"
#include "board/sg-reader.h"
#include "carolhook/config.h"
#include "platform/config.h"
#include "platform/platform.h"
void carol_dll_config_load(
struct carol_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"carolio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void touch_config_load(
struct touch_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(
L"touch",
L"enable",
1,
filename);
}
void controlbd_config_load(
struct controlbd_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(
L"controlbd",
L"enable",
1,
filename);
}
void carol_hook_config_load(
struct carol_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
amex_config_load(&cfg->amex, filename);
aime_config_load(&cfg->aime, filename);
carol_dll_config_load(&cfg->dll, filename);
gfx_config_load(&cfg->gfx, filename);
touch_config_load(&cfg->touch, filename);
controlbd_config_load(&cfg->controlbd, filename);
}

33
carolhook/config.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <stddef.h>
#include "amex/amex.h"
#include "board/sg-reader.h"
#include "carolhook/carol-dll.h"
#include "platform/platform.h"
#include "gfxhook/gfx.h"
#include "carolhook/touch.h"
#include "carolhook/controlbd.h"
struct carol_hook_config {
struct platform_config platform;
struct amex_config amex;
struct aime_config aime;
struct carol_dll_config dll;
struct gfx_config gfx;
struct touch_config touch;
struct controlbd_config controlbd;
};
void carol_dll_config_load(
struct carol_dll_config *cfg,
const wchar_t *filename);
void carol_hook_config_load(
struct carol_hook_config *cfg,
const wchar_t *filename);

178
carolhook/controlbd.c Normal file
View File

@ -0,0 +1,178 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "carolhook/carol-dll.h"
#include "carolhook/controlbd.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT controlbd_handle_irp(struct irp *irp);
static HRESULT controlbd_handle_irp_locked(struct irp *irp);
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf);
static HRESULT controlbd_frame_dispatch(struct controlbd_req *dest);
static HRESULT controlbd_req_nop(uint8_t cmd);
static CRITICAL_SECTION controlbd_lock;
static struct uart controlbd_uart;
static uint8_t controlbd_written_bytes[520];
static uint8_t controlbd_readable_bytes[520];
HRESULT controlbd_hook_init(const struct controlbd_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&controlbd_lock);
uart_init(&controlbd_uart, 11);
controlbd_uart.written.bytes = controlbd_written_bytes;
controlbd_uart.written.nbytes = sizeof(controlbd_written_bytes);
controlbd_uart.readable.bytes = controlbd_readable_bytes;
controlbd_uart.readable.nbytes = sizeof(controlbd_readable_bytes);
dprintf("Control Board: Init\n");
return iohook_push_handler(controlbd_handle_irp);
}
static HRESULT controlbd_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&controlbd_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&controlbd_lock);
hr = controlbd_handle_irp_locked(irp);
LeaveCriticalSection(&controlbd_lock);
return hr;
}
static HRESULT controlbd_handle_irp_locked(struct irp *irp)
{
struct controlbd_req req;
HRESULT hr;
assert(carol_dll.controlbd_init != NULL);
if (irp->op == IRP_OP_OPEN) {
dprintf("Control Board: Starting backend DLL\n");
hr = carol_dll.controlbd_init();
if (FAILED(hr)) {
dprintf("Control Board: Backend DLL error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&controlbd_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("Control Board: TX Buffer:\n");
dump_iobuf(&controlbd_uart.written);
#endif
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
if (FAILED(hr)) {
dprintf("Control Board: Deframe Error: %x\n", (int) hr);
return hr;
}
hr = controlbd_frame_dispatch(&req);
if (FAILED(hr)) {
dprintf("Control Board: Dispatch Error: %x\n", (int) hr);
return hr;
}
return hr;
}
}
static HRESULT controlbd_frame_dispatch(struct controlbd_req *req)
{
switch (req->cmd) {
case CONTROLBD_CMD_UNK_11:
return controlbd_req_nop(req->cmd);
default:
dprintf("Unhandled command %#02x\n", req->cmd);
return S_OK;
}
}
static HRESULT controlbd_req_nop(uint8_t cmd)
{
dprintf("Control Board: No-op cmd %#02x\n", cmd);
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0xE0;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x11;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x03;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x10;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x27;
return S_OK;
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf)
{
int initial_pos = iobuf->pos;
uint8_t check = 0;
dest->sync = iobuf->bytes[0];
iobuf->pos--;
dest->cmd = iobuf->bytes[1];
iobuf->pos--;
check += dest->cmd;
dest->checksum = iobuf->bytes[initial_pos - 1];
iobuf->pos--;
dest->data_length = initial_pos - 3; // sync, cmd, checksum
if (dest->data_length > 0) {
for (int i = 0; i < dest->data_length; i++) {
dest->data[i] = iobuf->bytes[i+2];
check += dest->data[i];
}
}
iobuf->pos -= dest->data_length;
if (dest->sync != 0xe0) {
dprintf("Control Board: Sync error, expected 0xe0, got %x\n", dest->sync);
return E_FAIL;
}
if (dest->checksum != check) {
dprintf("Control Board: Checksum error, expected %x, got %x\n", check, dest->checksum);
return E_FAIL;
}
return S_OK;
}

21
carolhook/controlbd.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct controlbd_config {
bool enable;
};
enum controlbd_cmd {
CONTROLBD_CMD_UNK_11 = 0x11
};
struct controlbd_req {
uint8_t sync; // First byte is the sync
uint8_t cmd; // Command byte
uint8_t data[256]; // Request body goes here
uint8_t checksum; // Final byte is all bytes added, except the sync
uint8_t data_length; // Size of the data including command byte
};
HRESULT controlbd_hook_init(const struct controlbd_config *cfg);

131
carolhook/dllmain.c Normal file
View File

@ -0,0 +1,131 @@
#include <windows.h>
#include <stdlib.h>
#include "amex/amex.h"
#include "gfxhook/gfx.h"
#include "gfxhook/d3d9.h"
#include "board/sg-reader.h"
#include "carolhook/config.h"
#include "carolhook/carol-dll.h"
#include "carolhook/jvs.h"
#include "carolhook/touch.h"
#include "carolhook/controlbd.h"
#include "carolhook/serial.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE carol_hook_mod;
static process_entry_t carol_startup;
static struct carol_hook_config carol_hook_cfg;
/*
COM Layout
01:(?) Touchscreen
10: Aime reader
11: Control board
12(?): LED Board
*/
static DWORD CALLBACK carol_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin carol_pre_startup ---\n");
/* Config load */
carol_hook_config_load(&carol_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&carol_hook_cfg.platform,
"SDAP",
"AAV0",
carol_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = carol_dll_init(&carol_hook_cfg.dll, carol_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = amex_hook_init(&carol_hook_cfg.amex, carol_jvs_init);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&carol_hook_cfg.aime, 10, carol_hook_mod);
if (FAILED(hr)) {
goto fail;
}
gfx_hook_init(&carol_hook_cfg.gfx);
gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod);
//serial_init();
hr = touch_hook_init(&carol_hook_cfg.touch);
if (FAILED(hr)) {
goto fail;
}
hr = controlbd_hook_init(&carol_hook_cfg.controlbd);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End carol_pre_startup ---\n");
/* Jump to EXE start address */
return carol_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
carol_hook_mod = mod;
hr = process_hijack_startup(carol_pre_startup, &carol_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

109
carolhook/jvs.c Normal file
View File

@ -0,0 +1,109 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "amex/jvs.h"
#include "board/io3.h"
#include "carolhook/carol-dll.h"
#include "jvs/jvs-bus.h"
#include "util/dprintf.h"
static void carol_jvs_read_switches(void *ctx, struct io3_switch_state *out);
static void carol_jvs_read_coin_counter(
void *ctx,
uint8_t slot_no,
uint16_t *out);
static const struct io3_ops carol_jvs_io3_ops = {
.read_switches = carol_jvs_read_switches,
.read_coin_counter = carol_jvs_read_coin_counter,
};
static struct io3 carol_jvs_io3;
HRESULT carol_jvs_init(struct jvs_node **out)
{
HRESULT hr;
assert(out != NULL);
assert(carol_dll.jvs_init != NULL);
dprintf("JVS I/O: Starting carol backend DLL\n");
hr = carol_dll.jvs_init();
if (FAILED(hr)) {
dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr);
return hr;
}
io3_init(&carol_jvs_io3, NULL, &carol_jvs_io3_ops, NULL);
*out = io3_to_jvs_node(&carol_jvs_io3);
return S_OK;
}
static void carol_jvs_read_switches(void *ctx, struct io3_switch_state *out)
{
uint8_t opbtn;
uint8_t gamebtn;
assert(out != NULL);
assert(carol_dll.jvs_poll != NULL);
opbtn = 0;
gamebtn = 0;
carol_dll.jvs_poll(&opbtn, &gamebtn);
if (gamebtn & 0x01) {
out->p1 |= 1 << 6;
}
if (gamebtn & 0x02) {
out->p1 |= 1 << 7;
}
if (gamebtn & 0x04) {
out->p1 |= 1 << 8;
}
if (gamebtn & 0x08) {
out->p1 |= 1 << 9;
}
if (gamebtn & 0x10) {
out->p1 |= 1 << 15;
}
if (opbtn & 0x01) {
out->system = 0x80;
} else {
out->system = 0;
}
if (opbtn & 0x02) {
out->p1 |= 1 << 14;
}
}
static void carol_jvs_read_coin_counter(
void *ctx,
uint8_t slot_no,
uint16_t *out)
{
assert(carol_dll.jvs_read_coin_counter != NULL);
if (slot_no > 0) {
return;
}
carol_dll.jvs_read_coin_counter(out);
}

7
carolhook/jvs.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "jvs/jvs-bus.h"
HRESULT carol_jvs_init(struct jvs_node **root);

38
carolhook/meson.build Normal file
View File

@ -0,0 +1,38 @@
shared_library(
'carolhook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'carolhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
],
link_with : [
aimeio_lib,
amex_lib,
board_lib,
carolio_lib,
hooklib_lib,
jvs_lib,
platform_lib,
util_lib,
gfxhook_lib,
],
sources : [
'config.c',
'config.h',
'carol-dll.c',
'carol-dll.h',
'dllmain.c',
'jvs.c',
'jvs.h',
'touch.c',
'touch.h',
'controlbd.c',
'controlbd.h',
'serial.c',
'serial.h',
],
)

39
carolhook/serial.c Normal file
View File

@ -0,0 +1,39 @@
#include <windows.h>
#include <winbase.h>
#include "hook/table.h"
#include "util/dprintf.h"
static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB);
static BOOL (WINAPI *next_SetCommState)(HANDLE hFile, LPDCB lpDCB);
static void com_hook_insert_hooks(HMODULE target);
static const struct hook_symbol win32_hooks[] = {
{
.name = "SetCommState",
.patch = my_SetCommState,
.link = (void **) &next_SetCommState
}
};
void serial_init()
{
com_hook_insert_hooks(NULL);
dprintf("Serial: Spy init\n");
}
static void com_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
}
static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB)
{
dprintf("Serial: my_SetCommState with baudrate %ld\n", lpDCB->BaudRate);
return next_SetCommState(hFile, lpDCB);
}

5
carolhook/serial.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <windows.h>
#include <winbase.h>
void serial_init();

118
carolhook/touch.c Normal file
View File

@ -0,0 +1,118 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "carolhook/carol-dll.h"
#include "carolhook/touch.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT touch_handle_irp(struct irp *irp);
static HRESULT touch_handle_irp_locked(struct irp *irp);
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf);
static CRITICAL_SECTION touch_lock;
static struct uart touch_uart;
static uint8_t touch_written_bytes[520];
static uint8_t touch_readable_bytes[520];
HRESULT touch_hook_init(const struct touch_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&touch_lock);
uart_init(&touch_uart, 1);
touch_uart.written.bytes = touch_written_bytes;
touch_uart.written.nbytes = sizeof(touch_written_bytes);
touch_uart.readable.bytes = touch_readable_bytes;
touch_uart.readable.nbytes = sizeof(touch_readable_bytes);
dprintf("Touchscreen: Init\n");
return iohook_push_handler(touch_handle_irp);
return S_OK;
}
static HRESULT touch_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&touch_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&touch_lock);
hr = touch_handle_irp_locked(irp);
LeaveCriticalSection(&touch_lock);
return hr;
}
static HRESULT touch_handle_irp_locked(struct irp *irp)
{
struct touch_req req;
HRESULT hr;
assert(carol_dll.touch_init != NULL);
if (irp->op == IRP_OP_OPEN) {
dprintf("Touchscreen: Starting backend DLL\n");
hr = carol_dll.touch_init();
if (FAILED(hr)) {
dprintf("Touchscreen: Backend DLL error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&touch_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 1
dprintf("Touchscreen: TX Buffer:\n");
dump_iobuf(&touch_uart.written);
#endif
hr = touch_frame_decode(&req, &touch_uart.written);
if (FAILED(hr)) {
dprintf("Touchscreen: Deframe Error: %x\n", (int) hr);
return hr;
}
return hr;
}
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf)
{
dest->cmd = iobuf->bytes[0];
iobuf->pos--;
dest->data_length = iobuf->pos;
if (dest->data_length > 0) {
for (int i = 1; i < dest->data_length; i++) {
dest->data[i-1] = iobuf->bytes[i];
}
}
iobuf->pos -= dest->data_length;
return S_OK;
}

17
carolhook/touch.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct touch_config {
bool enable;
};
struct touch_req {
uint8_t cmd; // First byte is the command byte
uint8_t data[256]; // rest of the data goes here
uint8_t data_length; // Size of the data including command byte
};
HRESULT touch_hook_init(const struct touch_config *cfg);

79
carolio/carolio.c Normal file
View File

@ -0,0 +1,79 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "carolio/carolio.h"
#include "carolio/config.h"
static bool carol_io_coin;
static uint16_t carol_io_coins;
static struct carol_io_config carol_io_cfg;
uint16_t carol_io_get_api_version(void)
{
return 0x0100;
}
HRESULT carol_io_jvs_init(void)
{
carol_io_config_load(&carol_io_cfg, L".\\segatools.ini");
return S_OK;
}
void carol_io_jvs_poll(uint8_t *opbtn_out, uint8_t *gamebtn_out)
{
uint8_t opbtn;
uint8_t gamebtn;
size_t i;
opbtn = 0;
if (GetAsyncKeyState(carol_io_cfg.vk_test) & 0x8000) {
opbtn |= 1;
}
if (GetAsyncKeyState(carol_io_cfg.vk_service) & 0x8000) {
opbtn |= 2;
}
for (i = 0 ; i < _countof(carol_io_cfg.vk_buttons) ; i++) {
if (GetAsyncKeyState(carol_io_cfg.vk_buttons[i]) & 0x8000) {
gamebtn |= 1 << i;
}
}
*opbtn_out = opbtn;
*gamebtn_out = gamebtn;
}
void carol_io_jvs_read_coin_counter(uint16_t *out)
{
if (out == NULL) {
return;
}
if (GetAsyncKeyState(carol_io_cfg.vk_coin) & 0x8000) {
if (!carol_io_coin) {
carol_io_coin = true;
carol_io_coins++;
}
} else {
carol_io_coin = false;
}
*out = carol_io_coins;
}
HRESULT carol_io_touch_init()
{
return S_OK;
}
HRESULT carol_io_controlbd_init()
{
return S_OK;
}

52
carolio/carolio.h Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
/* Get the version of the Project carol 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 carol_io_get_api_version(void);
/* Initialize JVS-based input. This function will be called before any other
carol_io_jvs_*() function calls. Errors returned from this function will
manifest as a disconnected JVS bus.
All subsequent calls may originate from arbitrary threads and some may
overlap with each other. Ensuring synchronization inside your IO DLL is
your responsibility.
Minimum API version: 0x0100 */
HRESULT carol_io_jvs_init(void);
/* Poll JVS input.
opbtn returns the cabinet test/service state, where bit 0 is Test and Bit 1
is Service.
gamebtn bits, from least significant to most significant, are:
Circle Cross Square Triangle Start UNUSED UNUSED UNUSED
Minimum API version: 0x0100 */
void carol_io_jvs_poll(uint8_t *opbtn, uint8_t *gamebtn);
/* Read the current state of the coin counter. This value should be incremented
for every coin detected by the coin acceptor mechanism. This count does not
need to persist beyond the lifetime of the process.
Minimum API Version: 0x0100 */
void carol_io_jvs_read_coin_counter(uint16_t *out);
HRESULT carol_io_touch_init();
HRESULT carol_io_controlbd_init();

20
carolio/config.c Normal file
View File

@ -0,0 +1,20 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "carolio/config.h"
void carol_io_config_load(
struct carol_io_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
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);
}

16
carolio/config.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
struct carol_io_config {
uint8_t vk_buttons[5];
uint8_t vk_slider[8];
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
};
void carol_io_config_load(
struct carol_io_config *cfg,
const wchar_t *filename);

13
carolio/meson.build Normal file
View File

@ -0,0 +1,13 @@
carolio_lib = static_library(
'carolio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'carolio.c',
'carolio.h',
'config.c',
'config.h',
],
)

48
dist/carol/segatools.ini vendored Normal file
View File

@ -0,0 +1,48 @@
[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=
[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.
enable=1
[gpio]
dipsw1=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.126.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
[aimeio]
; To use a custom card reader IO DLL enter its path here.
; Leave empty if you want to use Segatools built-in keyboard input.
path=
[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

13
dist/carol/start.bat vendored Normal file
View File

@ -0,0 +1,13 @@
@echo off
pushd %~dp0
taskkill /f /im aimeReaderHost.exe > nul 2>&1
start /min inject -d -k carolhook.dll aimeReaderHost.exe -p 10
inject -d -k carolhook.dll carol_nu.exe
taskkill /f /im aimeReaderHost.exe > nul 2>&1
echo.
echo Game processes have terminated
pause

54
dist/diva/segatools.ini vendored Normal file
View File

@ -0,0 +1,54 @@
[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=
[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
[gpio]
dipsw1=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.150.0
[slider]
cell1=0x51
cell2=0x57
cell3=0x45
cell4=0x52
cell5=0x55
cell6=0x49
cell7=0x4F
cell8=0x50
[buttons]
key1=0x27
key2=0x28
key3=0x25
key4=0x26
key5=0x20
; Sliders : <- QWER UIOP ->
; Triangle : Up arrow
; Square : Left Arrow
; Cross : Down Arrow
; Circle : Right arrow
; Enter : Space

9
dist/diva/start.bat vendored Normal file
View File

@ -0,0 +1,9 @@
@echo off
pushd %~dp0
inject -d -k divahook.dll diva.exe
echo.
echo Game processes have terminated
pause

View File

@ -55,12 +55,14 @@ subdir('gfxhook')
subdir('aimeio')
subdir('chuniio')
subdir('divaio')
subdir('carolio')
subdir('idzio')
subdir('mu3io')
subdir('mercuryio')
subdir('chunihook')
subdir('divahook')
subdir('carolhook')
subdir('idzhook')
subdir('minihook')
subdir('mu3hook')

View File

@ -28,6 +28,11 @@ static const struct reg_hook_val amvideo_reg_vals[] = {
.name = L"name",
.read = amvideo_reg_read_name,
.type = REG_SZ,
},
{
.name = L"name_x86",
.read = amvideo_reg_read_name,
.type = REG_SZ,
}
};