From c7fe5189cfe6f93804accc7fa069d8a07d9036dc Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 11 Dec 2022 17:01:51 -0500 Subject: [PATCH] add half-working CXB support --- Dockerfile | 2 + Package.mk | 16 +++ aimeio/aimeio.c | 1 + cxbhook/config.c | 61 +++++++++ cxbhook/config.h | 40 ++++++ cxbhook/cxb-dll.c | 120 +++++++++++++++++ cxbhook/cxb-dll.h | 24 ++++ cxbhook/cxbhook.def | 21 +++ cxbhook/dllmain.c | 149 +++++++++++++++++++++ cxbhook/led.c | 93 +++++++++++++ cxbhook/led.h | 11 ++ cxbhook/meson.build | 36 +++++ cxbhook/network.c | 13 ++ cxbhook/network.h | 13 ++ cxbhook/revio.c | 226 ++++++++++++++++++++++++++++++++ cxbhook/revio.h | 17 +++ cxbio/config.c | 22 ++++ cxbio/config.h | 17 +++ cxbio/cxbio.c | 78 +++++++++++ cxbio/cxbio.h | 19 +++ cxbio/meson.build | 13 ++ dist/cxb/resource/segatools.ini | 4 + dist/cxb/segatools.ini | 75 +++++++++++ dist/cxb/start.bat | 9 ++ meson.build | 2 + 25 files changed, 1082 insertions(+) create mode 100644 cxbhook/config.c create mode 100644 cxbhook/config.h create mode 100644 cxbhook/cxb-dll.c create mode 100644 cxbhook/cxb-dll.h create mode 100644 cxbhook/cxbhook.def create mode 100644 cxbhook/dllmain.c create mode 100644 cxbhook/led.c create mode 100644 cxbhook/led.h create mode 100644 cxbhook/meson.build create mode 100644 cxbhook/network.c create mode 100644 cxbhook/network.h create mode 100644 cxbhook/revio.c create mode 100644 cxbhook/revio.h create mode 100644 cxbio/config.c create mode 100644 cxbio/config.h create mode 100644 cxbio/cxbio.c create mode 100644 cxbio/cxbio.h create mode 100644 cxbio/meson.build create mode 100644 dist/cxb/resource/segatools.ini create mode 100644 dist/cxb/segatools.ini create mode 100644 dist/cxb/start.bat diff --git a/Dockerfile b/Dockerfile index 5a3ff66..c52ccf4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,8 @@ COPY amex amex COPY board board COPY chunihook chunihook COPY chuniio chuniio +COPY cxbhook cxbhook +COPY cxbio cxbio COPY dist dist COPY divahook divahook COPY divaio divaio diff --git a/Package.mk b/Package.mk index 4f21e64..850f9ee 100644 --- a/Package.mk +++ b/Package.mk @@ -12,6 +12,21 @@ $(BUILD_DIR_ZIP)/chuni.zip: $(BUILD_DIR_ZIP)/chuni/DEVICE $(V)strip $(BUILD_DIR_ZIP)/chuni/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/chuni ; zip -r ../chuni.zip * + +$(BUILD_DIR_ZIP)/cxb.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/cxb + $(V)mkdir -p $(BUILD_DIR_ZIP)/cxb/DEVICE + $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_32)/cxbhook/cxbhook.dll \ + $(DIST_DIR)/cxb/segatools.ini \ + $(DIST_DIR)/cxb/start.bat \ + $(BUILD_DIR_ZIP)/cxb + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/cxb/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/cxb/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/cxb ; zip -r ../cxb.zip * $(BUILD_DIR_ZIP)/diva.zip: $(V)echo ... $@ @@ -99,6 +114,7 @@ $(BUILD_DIR_ZIP)/doc.zip: \ $(BUILD_DIR_ZIP)/segatools.zip: \ $(BUILD_DIR_ZIP)/chuni.zip \ + $(BUILD_DIR_ZIP)/cxb.zip \ $(BUILD_DIR_ZIP)/carol.zip \ $(BUILD_DIR_ZIP)/diva.zip \ $(BUILD_DIR_ZIP)/doc.zip \ diff --git a/aimeio/aimeio.c b/aimeio/aimeio.c index b09d612..f505aac 100644 --- a/aimeio/aimeio.c +++ b/aimeio/aimeio.c @@ -62,6 +62,7 @@ static void aime_io_config_read( cfg->felica_path, _countof(cfg->felica_path), filename); + dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError()); cfg->felica_gen = GetPrivateProfileIntW( L"aime", diff --git a/cxbhook/config.c b/cxbhook/config.c new file mode 100644 index 0000000..9a513d1 --- /dev/null +++ b/cxbhook/config.c @@ -0,0 +1,61 @@ +#include + +#include +#include +#include + +#include "amex/amex.h" +#include "amex/config.h" + +#include "board/config.h" +#include "board/sg-reader.h" + +#include "cxbhook/config.h" + +#include "gfxhook/config.h" + +#include "hooklib/config.h" + +#include "platform/config.h" +#include "platform/platform.h" + +void cxb_dll_config_load( + struct cxb_dll_config *cfg, + const wchar_t *filename) +{ + +} + +void revio_config_load(struct revio_config *cfg, const wchar_t *filename) +{ + +} + +void network_config_load(struct network_config *cfg, const wchar_t *filename) +{ + +} + +void led_config_load(struct led_config *cfg, const wchar_t *filename) +{ + +} + +void cxb_hook_config_load( + struct cxb_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + memset(cfg, 0, sizeof(*cfg)); + + platform_config_load(&cfg->platform, filename); + amex_config_load(&cfg->amex, filename); + aime_config_load(&cfg->aime, filename); + gfx_config_load(&cfg->gfx, filename); + cxb_dll_config_load(&cfg->dll, filename); + revio_config_load(&cfg->revio, filename); + network_config_load(&cfg->network, filename); + led_config_load(&cfg->led, filename); +} \ No newline at end of file diff --git a/cxbhook/config.h b/cxbhook/config.h new file mode 100644 index 0000000..f2bb8ca --- /dev/null +++ b/cxbhook/config.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include "amex/amex.h" + +#include "board/sg-reader.h" + +#include "cxbhook/cxb-dll.h" +#include "cxbhook/revio.h" +#include "cxbhook/led.h" +#include "cxbhook/network.h" + +#include "gfxhook/gfx.h" + +#include "platform/platform.h" + +struct cxb_hook_config { + struct platform_config platform; + struct amex_config amex; + struct aime_config aime; + struct gfx_config gfx; + struct cxb_dll_config dll; + struct revio_config revio; + struct network_config network; + struct led_config led; +}; + +void cxb_dll_config_load( + struct cxb_dll_config *cfg, + const wchar_t *filename); + +void revio_config_load(struct revio_config *cfg, const wchar_t *filename); +void network_config_load(struct network_config *cfg, const wchar_t *filename); +void led_config_load(struct led_config *cfg, const wchar_t *filename); + +void cxb_hook_config_load( + struct cxb_hook_config *cfg, + const wchar_t *filename); diff --git a/cxbhook/cxb-dll.c b/cxbhook/cxb-dll.c new file mode 100644 index 0000000..93fb83b --- /dev/null +++ b/cxbhook/cxb-dll.c @@ -0,0 +1,120 @@ +#include + +#include +#include + +#include "cxbhook/cxb-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym cxb_dll_syms[] = { + { + .sym = "cxb_io_revio_init", + .off = offsetof(struct cxb_dll, revio_init), + }, + { + .sym = "cxb_io_revio_poll", + .off = offsetof(struct cxb_dll, revio_poll), + }, + { + .sym = "cxb_io_revio_get_coins", + .off = offsetof(struct cxb_dll, revio_get_coins), + }, + { + .sym = "cxb_io_revio_set_coins", + .off = offsetof(struct cxb_dll, revio_set_coins), + }, + { + .sym = "cxb_io_led_init", + .off = offsetof(struct cxb_dll, led_init), + }, + { + .sym = "cxb_io_led_update", + .off = offsetof(struct cxb_dll, led_update), + }, +}; + +struct cxb_dll cxb_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 cxb_dll_init(const struct cxb_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("Crossbeats IO: Failed to load IO DLL: %lx: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Crossbeats IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "cxb_io_get_api_version"); + + if (get_api_version != NULL) { + cxb_dll.api_version = get_api_version(); + } else { + cxb_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose cxb_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (cxb_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Crossbeats IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + cxb_dll.api_version); + + goto end; + } + + sym = cxb_dll_syms; + hr = dll_bind(&cxb_dll, src, &sym, _countof(cxb_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Crossbeats 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/cxbhook/cxb-dll.h b/cxbhook/cxb-dll.h new file mode 100644 index 0000000..de57958 --- /dev/null +++ b/cxbhook/cxb-dll.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include "cxbio/cxbio.h" + +struct cxb_dll { + uint16_t api_version; + HRESULT (*revio_init)(void); + void (*revio_poll)(uint16_t *opbtn); + void (*revio_get_coins)(long *coins); + void (*revio_set_coins)(int coins); + HRESULT (*led_init)(void); + void (*led_update)(int id, int color); +}; + +struct cxb_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct cxb_dll cxb_dll; + +HRESULT cxb_dll_init(const struct cxb_dll_config *cfg, HINSTANCE self); diff --git a/cxbhook/cxbhook.def b/cxbhook/cxbhook.def new file mode 100644 index 0000000..9efaaf6 --- /dev/null +++ b/cxbhook/cxbhook.def @@ -0,0 +1,21 @@ +LIBRARY cxbhook + +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 + cxb_io_get_api_version + cxb_io_revio_init + cxb_io_revio_poll + cxb_io_revio_get_coins + cxb_io_revio_set_coins + cxb_io_led_init + cxb_io_led_update diff --git a/cxbhook/dllmain.c b/cxbhook/dllmain.c new file mode 100644 index 0000000..b9c3056 --- /dev/null +++ b/cxbhook/dllmain.c @@ -0,0 +1,149 @@ +#include + +#include + +#include "amex/amex.h" + +#include "board/sg-reader.h" + +#include "cxbhook/config.h" +#include "cxbhook/revio.h" +#include "cxbhook/led.h" +#include "cxbhook/network.h" + +#include "cxbio/cxbio.h" + +#include "gfxhook/gfx.h" +#include "gfxhook/d3d9.h" + +#include "hook/process.h" + +#include "hooklib/serial.h" +#include "hooklib/spike.h" + +#include "platform/platform.h" + +#include "util/dprintf.h" + +static HMODULE cxb_hook_mod; +static process_entry_t cxb_startup; +static struct cxb_hook_config cxb_hook_cfg; + +static DWORD CALLBACK cxb_pre_startup(void) +{ + HMODULE d3dc; + HMODULE dbghelp; + HRESULT hr; + + dprintf("--- Begin cxb_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 */ + + cxb_hook_config_load(&cxb_hook_cfg, L".\\segatools.ini"); + + /* Hook Win32 APIs */ + + gfx_hook_init(&cxb_hook_cfg.gfx); + gfx_d3d9_hook_init(&cxb_hook_cfg.gfx, cxb_hook_mod); + serial_hook_init(); + + /* Initialize emulation hooks */ + + hr = platform_hook_init( + &cxb_hook_cfg.platform, + "SDCA", + "AAV1", + cxb_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = cxb_dll_init(&cxb_hook_cfg.dll, cxb_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = amex_hook_init(&cxb_hook_cfg.amex, NULL); + + if (FAILED(hr)) { + goto fail; + } + + hr = sg_reader_hook_init(&cxb_hook_cfg.aime, 12, cxb_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = revio_hook_init(&cxb_hook_cfg.revio); + + if (FAILED(hr)) { + goto fail; + } + + hr = network_hook_init(&cxb_hook_cfg.network); + + if (FAILED(hr)) { + goto fail; + } + + hr = led_hook_init(&cxb_hook_cfg.led); + + if (FAILED(hr)) { + goto fail; + } + + /* Initialize debug helpers */ + + spike_hook_init(L".\\segatools.ini"); + + dprintf("--- End cxb_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return cxb_startup(); + +fail: + ExitProcess(EXIT_FAILURE); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + cxb_hook_mod = mod; + + hr = process_hijack_startup(cxb_pre_startup, &cxb_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/cxbhook/led.c b/cxbhook/led.c new file mode 100644 index 0000000..39d6d4f --- /dev/null +++ b/cxbhook/led.c @@ -0,0 +1,93 @@ +#include +#include +#include + +#include "cxbhook/led.h" +#include "cxbhook/cxb-dll.h" + +#include "hooklib/procaddr.h" + +#include "hook/table.h" + +#include "util/dprintf.h" + + +static int my_cCommLamp_Open(char *port); +static void my_cCommLamp_Close(); +static int my_cCommLamp_Setup(int led_id); +static int my_cCommLamp_SetColor(int led_id, int color); +static int my_cCommLamp_Update(); +static int my_cCommLamp_UpdateDelta(float delta); + + +static struct hook_symbol lamp_syms[] = { + { + .name = "cCommLamp_Open", + .patch = my_cCommLamp_Open + }, + { + .name = "cCommLamp_Close", + .patch = my_cCommLamp_Close + }, + { + .name = "cCommLamp_Setup", + .patch = my_cCommLamp_Setup, + }, + { + .name = "cCommLamp_SetColor", + .patch = my_cCommLamp_SetColor + }, + { + .name = "cCommLamp_Update", + .patch = my_cCommLamp_Update + }, + { + .name = "cCommLamp_UpdateDelta", + .patch = my_cCommLamp_UpdateDelta + }, +}; + +HRESULT led_hook_init(struct led_config *cfg) +{ + dprintf("LED: Init\n"); + return proc_addr_table_push("CommLamp.dll", lamp_syms, _countof(lamp_syms)); +} + +static int my_cCommLamp_Open(char *port) +{ + HRESULT hr = cxb_dll.led_init(); + dprintf("LED: Open %s (DLL init result %lx)\n", port, hr); + + if (FAILED(hr)) { + return 0; + } + + return 1; +} + +static void my_cCommLamp_Close() +{ + dprintf("LED: Close\n"); +} + +static int my_cCommLamp_Setup(int led_id) +{ + dprintf("LED: Setup %d\n", led_id); + return 0; +} + +static int my_cCommLamp_SetColor(int led_id, int color) +{ + cxb_dll.led_update(led_id, color); + return 1; +} + +static int my_cCommLamp_Update() +{ + return 0; +} + +static int my_cCommLamp_UpdateDelta(float delta) +{ + return 0; +} \ No newline at end of file diff --git a/cxbhook/led.h b/cxbhook/led.h new file mode 100644 index 0000000..0cbfac7 --- /dev/null +++ b/cxbhook/led.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +struct led_config { + bool enable; +}; + +HRESULT led_hook_init(struct led_config *cfg); \ No newline at end of file diff --git a/cxbhook/meson.build b/cxbhook/meson.build new file mode 100644 index 0000000..1c23a00 --- /dev/null +++ b/cxbhook/meson.build @@ -0,0 +1,36 @@ +shared_library( + 'cxbhook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'cxbhook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + aimeio_lib, + amex_lib, + board_lib, + cxbio_lib, + gfxhook_lib, + hooklib_lib, + jvs_lib, + platform_lib, + util_lib, + ], + sources : [ + 'cxb-dll.c', + 'cxb-dll.h', + 'config.c', + 'config.h', + 'dllmain.c', + 'revio.c', + 'revio.h', + 'led.c', + 'led.h', + 'network.c', + 'network.h', + ], +) diff --git a/cxbhook/network.c b/cxbhook/network.c new file mode 100644 index 0000000..e9a832a --- /dev/null +++ b/cxbhook/network.c @@ -0,0 +1,13 @@ +#include +#include +#include + +#include "cxbhook/network.h" + +#include "util/dprintf.h" + +HRESULT network_hook_init(struct network_config *cfg) +{ + dprintf("Network: Init\n"); + return S_OK; +} \ No newline at end of file diff --git a/cxbhook/network.h b/cxbhook/network.h new file mode 100644 index 0000000..a2ae7ce --- /dev/null +++ b/cxbhook/network.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +struct network_config { + bool enable; + bool disable_ssl; + char title_server[PATH_MAX]; +}; + +HRESULT network_hook_init(struct network_config *cfg); \ No newline at end of file diff --git a/cxbhook/revio.c b/cxbhook/revio.c new file mode 100644 index 0000000..e72158d --- /dev/null +++ b/cxbhook/revio.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include + +#include "cxbhook/revio.h" +#include "cxbhook/cxb-dll.h" + +#include "hooklib/procaddr.h" + +#include "hook/table.h" + +#include "util/dprintf.h" + +static int my_cCommIo_Open(char *port); +static int my_cCommIo_Close(); +static long my_cCommIo_GetCoin(); +static int my_cCommIo_SetCoin(int coin_ct); +static int my_cCommIo_GetStatus(); +static int my_cCommIo_GetSwitch(); +static int my_cCommIo_GetTrigger(); +static int my_cCommIo_GetRelease(); +static long my_cCommIo_GetVolume(); +static int my_cCommIo_SetAmpVolume(int amp_id, long new_volume); +static int my_cCommIo_GetAmpVolume(int amp_id); +static int my_cCommIo_SetAmpMute(int amp_id, int a2); + +int amp_volume[] = {20, 20, 20}; +int last_triggers = 0; +int last_is_mouse_down = false; + +static struct hook_symbol revio_syms[] = { + { + .name = "cCommIo_Open", + .patch = my_cCommIo_Open + }, + { + .name = "cCommIo_Close", + .patch = my_cCommIo_Close + }, + { + .name = "cCommIo_GetStatus", + .patch = my_cCommIo_GetStatus + }, + { + .name = "cCommIo_GetCoin", + .patch = my_cCommIo_GetCoin + }, + { + .name = "cCommIo_SetCoin", + .patch = my_cCommIo_SetCoin + }, + { + .name = "cCommIo_GetSwitch", + .patch = my_cCommIo_GetSwitch + }, + { + .name = "cCommIo_GetTrigger", + .patch = my_cCommIo_GetTrigger + }, + { + .name = "cCommIo_GetRelease", + .patch = my_cCommIo_GetRelease + }, + { + .name = "cCommIo_GetVolume", + .patch = my_cCommIo_GetVolume + }, + { + .name = "cCommIo_SetAmpVolume", + .patch = my_cCommIo_SetAmpVolume + }, + { + .name = "cCommIo_GetAmpVolume", + .patch = my_cCommIo_GetAmpVolume + }, + { + .name = "cCommIo_SetAmpMute", + .patch = my_cCommIo_SetAmpMute + }, +}; + +HRESULT revio_hook_init(struct revio_config *cfg) +{ + dprintf("Revio: Init\n"); + return proc_addr_table_push("CommIo.dll", revio_syms, _countof(revio_syms)); +} + +static int my_cCommIo_Open(char *port) +{ + dprintf("Revio: Open port %s\n", port); + cxb_dll.revio_init(); + return 1; +} + +static int my_cCommIo_Close() +{ + dprintf("Revio: Close\n"); + return 0; +} + +static int my_cCommIo_GetStatus() +{ + return 1; +} + +static long my_cCommIo_GetCoin() +{ + long coins; + cxb_dll.revio_get_coins(&coins); + + return coins; +} + +static int my_cCommIo_SetCoin(int coin_ct) +{ + // does some weird shit, not sure + //dprintf("Revio: Set coin %d\n", coin_ct); + cxb_dll.revio_set_coins(coin_ct); + return 1; +} + +static int my_cCommIo_GetSwitch() +{ + return 0; +} + +static int my_cCommIo_GetTrigger() +{ + uint16_t btns = 0; + int out = 0; + + cxb_dll.revio_poll(&btns); + + if (btns & 0x01) { + out |= 1 << 4; // test + } + + if (btns & 0x02) { + out |= 1 << 5; // service? + } + + if (btns & 0x04) { + out |= 1 << 1; // up + } + + if (btns & 0x08) { + out |= 1 << 3; // down + } + + if (btns & 0x0F) { + out |= 1 << 2; // cancel + } + + out &= ~last_triggers; + + dprintf("Revio: GetTrigger %X\n", out); + last_triggers = out; + return out; +} + +static int my_cCommIo_GetRelease() +{ + uint16_t btns = 0; + int out = last_triggers; + + cxb_dll.revio_poll(&btns); + + if (btns & 0x01) { + out |= 1 << 4; // test + } + + if (btns & 0x02) { + out |= 1 << 5; // service? + } + + if (btns & 0x04) { + out |= 1 << 1; // up + } + + if (btns & 0x08) { + out |= 1 << 3; // down + } + + if (btns & 0x0F) { + out |= 1 << 2; // cancel + } + + out &= ~btns; + + dprintf("Revio: GetRelease %X\n", out); + last_triggers = btns; + return out; +} + +static long my_cCommIo_GetVolume() +{ + return 0; +} + +static int my_cCommIo_SetAmpVolume(int amp_id, long new_volume) +{ + dprintf("Revio: SetAmpVolume id %d -> vol %ld\n", amp_id, new_volume); + if (amp_id > _countof(amp_volume)) { + return 0; + } + + amp_volume[amp_id] = new_volume; + return 0; +} + +static int my_cCommIo_GetAmpVolume(int amp_id) +{ + dprintf("Revio: GetAmpVolume id %d\n", amp_id); + if (amp_id > _countof(amp_volume)) { + return 0; + } + + return amp_volume[amp_id]; +} + +static int my_cCommIo_SetAmpMute(int amp_id, int a2) +{ + dprintf("Revio: GetAmpVolume id %d unknown %d\n", amp_id, a2); + return 0; +} diff --git a/cxbhook/revio.h b/cxbhook/revio.h new file mode 100644 index 0000000..8150c97 --- /dev/null +++ b/cxbhook/revio.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +struct revio_config { + bool enable; + uint8_t test; + uint8_t service; + uint8_t coin; + uint8_t up; + uint8_t down; + uint8_t cancel; +}; + +HRESULT revio_hook_init(struct revio_config *cfg); \ No newline at end of file diff --git a/cxbio/config.c b/cxbio/config.c new file mode 100644 index 0000000..681c78a --- /dev/null +++ b/cxbio/config.c @@ -0,0 +1,22 @@ +#include + +#include +#include +#include + +#include "cxbio/config.h" + +void cxb_io_config_load( + struct cxb_io_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->test = GetPrivateProfileIntW(L"revio", L"test", '1', filename); + cfg->service = GetPrivateProfileIntW(L"revio", L"service", '2', filename); + cfg->coin = GetPrivateProfileIntW(L"revio", L"coin", '3', filename); + cfg->cancel = GetPrivateProfileIntW(L"revio", L"cancel", '4', filename); + cfg->up = GetPrivateProfileIntW(L"revio", L"up", VK_UP, filename); + cfg->down = GetPrivateProfileIntW(L"revio", L"down", VK_DOWN, filename); +} diff --git a/cxbio/config.h b/cxbio/config.h new file mode 100644 index 0000000..f6e82cb --- /dev/null +++ b/cxbio/config.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include + +struct cxb_io_config { + uint8_t test; + uint8_t service; + uint8_t coin; + uint8_t cancel; + uint8_t up; + uint8_t down; +}; + +void cxb_io_config_load( + struct cxb_io_config *cfg, + const wchar_t *filename); \ No newline at end of file diff --git a/cxbio/cxbio.c b/cxbio/cxbio.c new file mode 100644 index 0000000..d021d3c --- /dev/null +++ b/cxbio/cxbio.c @@ -0,0 +1,78 @@ +#include + +#include +#include +#include + +#include "cxbio/cxbio.h" +#include "cxbio/config.h" + +#include "util/dprintf.h" + +static bool cxb_io_coin; +static int cxb_io_coins; +static struct cxb_io_config cxb_io_cfg; + +uint16_t cxb_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT cxb_io_revio_init(void) +{ + dprintf("CXB IO: REVIO init\n"); + cxb_io_config_load(&cxb_io_cfg, L".\\segatools.ini"); + + return S_OK; +} + +void cxb_io_revio_poll(uint16_t *opbtn) +{ + if (GetAsyncKeyState(cxb_io_cfg.test)) { + *opbtn |= 0x01; /* Test */ + } + + if (GetAsyncKeyState(cxb_io_cfg.service)) { + *opbtn |= 0x02; /* Service */ + } + + if (GetAsyncKeyState(cxb_io_cfg.cancel)) { + *opbtn |= 0x04; /* Cancel */ + } + + if (GetAsyncKeyState(cxb_io_cfg.up)) { + *opbtn |= 0x08; /* Up */ + } + + if (GetAsyncKeyState(cxb_io_cfg.down)) { + *opbtn |= 0x10; /* Down */ + } +} + +void cxb_io_revio_get_coins(long *coins) +{ + if (GetAsyncKeyState(cxb_io_cfg.coin)) { + if (!cxb_io_coin) { + cxb_io_coin = true; + cxb_io_coins++; + } + } else { + cxb_io_coin = false; + } + + *coins = cxb_io_coins; +} + +void cxb_io_revio_set_coins(int coins) +{ + cxb_io_coins = coins; +} + +HRESULT cxb_io_led_init(void) +{ + dprintf("CXB IO: LED init\n"); + return S_OK; +} + +void cxb_io_led_update(int id, int color) +{} \ No newline at end of file diff --git a/cxbio/cxbio.h b/cxbio/cxbio.h new file mode 100644 index 0000000..35fccea --- /dev/null +++ b/cxbio/cxbio.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +uint16_t cxb_io_get_api_version(void); + +HRESULT cxb_io_revio_init(void); + +void cxb_io_revio_poll(uint16_t *opbtn); + +void cxb_io_revio_get_coins(long *coins); + +void cxb_io_revio_set_coins(int coins); + +HRESULT cxb_io_led_init(void); + +void cxb_io_led_update(int id, int color); \ No newline at end of file diff --git a/cxbio/meson.build b/cxbio/meson.build new file mode 100644 index 0000000..2ce52d2 --- /dev/null +++ b/cxbio/meson.build @@ -0,0 +1,13 @@ +cxbio_lib = static_library( + 'cxbio', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + sources : [ + 'cxbio.c', + 'cxbio.h', + 'config.c', + 'config.h', + ], +) diff --git a/dist/cxb/resource/segatools.ini b/dist/cxb/resource/segatools.ini new file mode 100644 index 0000000..a214976 --- /dev/null +++ b/dist/cxb/resource/segatools.ini @@ -0,0 +1,4 @@ +[aime] +; CXB is stupid, so we have to make the paths go back one +aimePath=../DEVICE/aime.txt +felicaPath=../DEVICE/felica.txt \ No newline at end of file diff --git a/dist/cxb/segatools.ini b/dist/cxb/segatools.ini new file mode 100644 index 0000000..e0491ff --- /dev/null +++ b/dist/cxb/segatools.ini @@ -0,0 +1,75 @@ +[vfs] +; Make sure theses are full paths and not relative or you will have a bad time +; 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. +; Crossbeats 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 +billingCa=../DEVICE/ca.crt +billingPub=../DEVICE/billing.pub +billingType=2 + +[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 + +[aime] +; Aime reader emulation +; CXB is stupid, so we have to make the paths go back one +enable=1 +aimePath=../DEVICE/aime.txt +felicaPath=../DEVICE/felica.txt + +[eeprom] +; See above +path=../DEVICE/eeprom.bin + +[sram] +; See above +path=../DEVICE/sram.bin + +[led] +; Emulation for the LED board. Currently it's just dummy responses, +; but if somebody wants to make their keyboard or whatever light +; up with the game they can +enable=1 + +[revio] +; Enable emulation of the rev IO board +enabe=1 +; 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 +; Menu up key. Default is up arrow. +up=0x26 +; Menu down key. Default is down arrow. +down=0x28 +; Menu cancel key. Default is the 4 key. +cancel=0x34 diff --git a/dist/cxb/start.bat b/dist/cxb/start.bat new file mode 100644 index 0000000..af92b2d --- /dev/null +++ b/dist/cxb/start.bat @@ -0,0 +1,9 @@ +@echo off + +pushd %~dp0 + +inject -d -k cxbhook.dll Rev_v11.exe + +echo. +echo Game processes have terminated +pause \ No newline at end of file diff --git a/meson.build b/meson.build index acf85ef..2c6b673 100644 --- a/meson.build +++ b/meson.build @@ -59,6 +59,7 @@ subdir('carolio') subdir('idzio') subdir('mu3io') subdir('mercuryio') +subdir('cxbio') subdir('chunihook') subdir('divahook') @@ -67,3 +68,4 @@ subdir('idzhook') subdir('minihook') subdir('mu3hook') subdir('mercuryhook') +subdir('cxbhook')