diff --git a/Dockerfile b/Dockerfile index ff2f85d..6aa360d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,6 +27,8 @@ COPY hooklib hooklib COPY iccard iccard COPY idzhook idzhook COPY idzio idzio +COPY mercuryhook mercuryhook +COPY mercuryio mercuryio COPY jvs jvs COPY minihook minihook COPY mu3hook mu3hook diff --git a/Package.mk b/Package.mk index c18c033..fcac6f9 100644 --- a/Package.mk +++ b/Package.mk @@ -28,6 +28,21 @@ $(BUILD_DIR_ZIP)/idz.zip: $(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip * +$(BUILD_DIR_ZIP)/mercury.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/mercury + $(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \ + $(DIST_DIR)/mercury/segatools.ini \ + $(DIST_DIR)/mercury/start.bat \ + $(BUILD_DIR_ZIP)/mercury + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/mercury/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip * + $(BUILD_DIR_ZIP)/doc.zip: \ $(DOC_DIR)/config \ $(DOC_DIR)/chunihook.md \ @@ -40,6 +55,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \ $(BUILD_DIR_ZIP)/chuni.zip \ $(BUILD_DIR_ZIP)/doc.zip \ $(BUILD_DIR_ZIP)/idz.zip \ + $(BUILD_DIR_ZIP)/mercury.zip \ CHANGELOG.md \ README.md \ diff --git a/board/io4.c b/board/io4.c index efad62f..e7fe9e3 100644 --- a/board/io4.c +++ b/board/io4.c @@ -28,6 +28,7 @@ enum { IO4_CMD_CLEAR_BOARD_STATUS = 0x03, IO4_CMD_SET_GENERAL_OUTPUT = 0x04, IO4_CMD_SET_PWM_OUTPUT = 0x05, + IO4_CMD_UNIMPLEMENTED = 0x41, IO4_CMD_UPDATE_FIRMWARE = 0x85, }; @@ -234,7 +235,12 @@ static HRESULT io4_handle_write(struct irp *irp) case IO4_CMD_UPDATE_FIRMWARE: dprintf("USB I/O: Update firmware..?\n"); - return E_FAIL; + return E_FAIL; + + case IO4_CMD_UNIMPLEMENTED: + //dprintf("USB I/O: Unimplemented cmd 41\n"); + + return S_OK; default: dprintf("USB I/O: Unknown command %02x\n", out.cmd); diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini new file mode 100644 index 0000000..c439f13 --- /dev/null +++ b/dist/mercury/segatools.ini @@ -0,0 +1,42 @@ +[vfs] +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs=amfs +; 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=appdata +option=option + +[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 + +[ds] +; Region code on the emulated AMEX board DS EEPROM. +; 1: Japan +; 4: Export (some UI elements in English) +; +; NOTE: Changing this setting causes a factory reset. +region=1 + +[netenv] +; Simulate an ideal LAN environment. This may interfere with head-to-head play. +; SEGA games are somewhat picky about their LAN environment, so leaving this +; setting enabled is 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.250.0 + + +[io4] +; Input API selection for JVS input emulator. +test=0x31 +service=0x32 + +[gfx] +enable=0 diff --git a/dist/mercury/start.bat b/dist/mercury/start.bat new file mode 100644 index 0000000..437722e --- /dev/null +++ b/dist/mercury/start.bat @@ -0,0 +1,10 @@ +@echo off +pushd %~dp0 +taskkill /f /im amdaemon.exe > nul 2>&1 +:LOOP +inject -d -k mercuryhook.dll amdaemon.exe -c config.json config_region_jpn.json config_video_clone.json config_video_clone_flip.json config_video_dual.json config_video_dual_flip.json +inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury.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/mercuryhook/config.c b/mercuryhook/config.c new file mode 100644 index 0000000..9a61aad --- /dev/null +++ b/mercuryhook/config.c @@ -0,0 +1,43 @@ +#include +#include + +#include "board/config.h" + +#include "hooklib/config.h" +#include "hooklib/dvd.h" +#include "hooklib/gfx.h" + +#include "mercuryhook/config.h" + +#include "platform/config.h" + +void mercury_dll_config_load( + struct mercury_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"mercuryio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + +void mercury_hook_config_load( + struct mercury_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + 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); + mercury_dll_config_load(&cfg->dll, filename); +} diff --git a/mercuryhook/config.h b/mercuryhook/config.h new file mode 100644 index 0000000..627e69c --- /dev/null +++ b/mercuryhook/config.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "board/config.h" + +#include "hooklib/dvd.h" +#include "hooklib/gfx.h" + +#include "mercuryhook/mercury-dll.h" + +#include "platform/config.h" + +struct mercury_hook_config { + struct platform_config platform; + struct aime_config aime; + struct dvd_config dvd; + struct io4_config io4; + struct gfx_config gfx; + struct mercury_dll_config dll; +}; + +void mercury_dll_config_load( + struct mercury_dll_config *cfg, + const wchar_t *filename); + +void mercury_hook_config_load( + struct mercury_hook_config *cfg, + const wchar_t *filename); diff --git a/mercuryhook/dllmain.c b/mercuryhook/dllmain.c new file mode 100644 index 0000000..a362f7b --- /dev/null +++ b/mercuryhook/dllmain.c @@ -0,0 +1,109 @@ +#include + +#include "board/io4.h" +#include "board/sg-reader.h" +#include "board/vfd.h" + +#include "hook/process.h" + +#include "hooklib/serial.h" +#include "hooklib/spike.h" + +#include "mercuryhook/config.h" +#include "mercuryhook/io4.h" +#include "mercuryhook/mercury-dll.h" + +#include "platform/platform.h" + +#include "util/dprintf.h" + +static HMODULE mercury_hook_mod; +static process_entry_t mercury_startup; +static struct mercury_hook_config mercury_hook_cfg; + +/* This hook is based on mu3hook, with leaked mercuryhook i/o codes. */ + +static DWORD CALLBACK mercury_pre_startup(void) +{ + HRESULT hr; + + dprintf("--- Begin mercury_pre_startup ---\n"); + + /* Load config */ + + mercury_hook_config_load(&mercury_hook_cfg, L".\\segatools.ini"); + + /* Hook Win32 APIs */ + + dvd_hook_init(&mercury_hook_cfg.dvd, mercury_hook_mod); + gfx_hook_init(&mercury_hook_cfg.gfx, mercury_hook_mod); + serial_hook_init(); + + /* Initialize emulation hooks */ + + hr = platform_hook_init( + &mercury_hook_cfg.platform, + "SDFE", + "ACA1", + mercury_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = sg_reader_hook_init(&mercury_hook_cfg.aime, 1, mercury_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = vfd_hook_init(2); + + if (FAILED(hr)) { + goto fail; + } + + hr = mercury_dll_init(&mercury_hook_cfg.dll, mercury_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = mercury_io4_hook_init(&mercury_hook_cfg.io4); + + if (FAILED(hr)) { + goto fail; + } + + /* Initialize debug helpers */ + + spike_hook_init(L".\\segatools.ini"); + + dprintf("--- End mercury_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return mercury_startup(); + +fail: + ExitProcess(EXIT_FAILURE); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + mercury_hook_mod = mod; + + hr = process_hijack_startup(mercury_pre_startup, &mercury_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c new file mode 100644 index 0000000..2ff0e83 --- /dev/null +++ b/mercuryhook/io4.c @@ -0,0 +1,64 @@ +#include + +#include +#include +#include + +#include "board/io4.h" + +#include "mercuryhook/mercury-dll.h" + +#include "util/dprintf.h" + +static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state); + +static const struct io4_ops mercury_io4_ops = { + .poll = mercury_io4_poll, +}; + +HRESULT mercury_io4_hook_init(const struct io4_config *cfg) +{ + HRESULT hr; + + assert(mercury_dll.init != NULL); + + hr = io4_hook_init(cfg, &mercury_io4_ops, NULL); + + if (FAILED(hr)) { + return hr; + } + + return mercury_dll.init(); +} + +static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) +{ + uint8_t opbtn; + HRESULT hr; + + assert(mercury_dll.poll != NULL); + assert(mercury_dll.get_opbtns != NULL); + assert(mercury_dll.get_gamebtns != NULL); + + memset(state, 0, sizeof(*state)); + + hr = mercury_dll.poll(); + + if (FAILED(hr)) { + return hr; + } + + opbtn = 0; + + mercury_dll.get_opbtns(&opbtn); + + if (opbtn & MAI2_IO_OPBTN_TEST) { + state->buttons[0] |= IO4_BUTTON_TEST; + } + + if (opbtn & MAI2_IO_OPBTN_SERVICE) { + state->buttons[0] |= IO4_BUTTON_SERVICE; + } + + return S_OK; +} diff --git a/mercuryhook/io4.h b/mercuryhook/io4.h new file mode 100644 index 0000000..87dc6d7 --- /dev/null +++ b/mercuryhook/io4.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "board/io4.h" + +HRESULT mercury_io4_hook_init(const struct io4_config *cfg); diff --git a/mercuryhook/mercury-dll.c b/mercuryhook/mercury-dll.c new file mode 100644 index 0000000..6873820 --- /dev/null +++ b/mercuryhook/mercury-dll.c @@ -0,0 +1,109 @@ +#include + +#include +#include + +#include "mercuryhook/mercury-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym mercury_dll_syms[] = { + { + .sym = "mercury_io_init", + .off = offsetof(struct mercury_dll, init), + }, { + .sym = "mercury_io_poll", + .off = offsetof(struct mercury_dll, poll), + }, { + .sym = "mercury_io_get_opbtns", + .off = offsetof(struct mercury_dll, get_opbtns), + }, { + .sym = "mercury_io_get_gamebtns", + .off = offsetof(struct mercury_dll, get_gamebtns), + } +}; + +struct mercury_dll mercury_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 mercury_dll_init(const struct mercury_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("Wacca IO: Failed to load IO DLL: %lx: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Wacca IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "mercury_io_get_api_version"); + + if (get_api_version != NULL) { + mercury_dll.api_version = get_api_version(); + } else { + mercury_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose mercury_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (mercury_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Wacca IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + mercury_dll.api_version); + + goto end; + } + + sym = mercury_dll_syms; + hr = dll_bind(&mercury_dll, src, &sym, _countof(mercury_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Wacca 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/mercuryhook/mercury-dll.h b/mercuryhook/mercury-dll.h new file mode 100644 index 0000000..4e679a8 --- /dev/null +++ b/mercuryhook/mercury-dll.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "mercuryio/mercuryio.h" + +struct mercury_dll { + uint16_t api_version; + HRESULT (*init)(void); + HRESULT (*poll)(void); + void (*get_opbtns)(uint8_t *opbtn); + void (*get_gamebtns)(uint16_t *player1, uint16_t *player2); +}; + +struct mercury_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct mercury_dll mercury_dll; + +HRESULT mercury_dll_init(const struct mercury_dll_config *cfg, HINSTANCE self); diff --git a/mercuryhook/mercuryhook.def b/mercuryhook/mercuryhook.def new file mode 100644 index 0000000..916d278 --- /dev/null +++ b/mercuryhook/mercuryhook.def @@ -0,0 +1,19 @@ +LIBRARY mercuryhook + +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 + mercury_io_get_api_version + mercury_io_get_gamebtns + mercury_io_get_opbtns + mercury_io_init + mercury_io_poll \ No newline at end of file diff --git a/mercuryhook/meson.build b/mercuryhook/meson.build new file mode 100644 index 0000000..5fc141c --- /dev/null +++ b/mercuryhook/meson.build @@ -0,0 +1,29 @@ +shared_library( + 'mercuryhook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'mercuryhook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + aimeio_lib, + board_lib, + hooklib_lib, + mercuryio_lib, + platform_lib, + util_lib, + ], + sources : [ + 'config.c', + 'config.h', + 'dllmain.c', + 'io4.c', + 'io4.h', + 'mercury-dll.c', + 'mercury-dll.h' + ], +) diff --git a/mercuryio/config.c b/mercuryio/config.c new file mode 100644 index 0000000..3b62789 --- /dev/null +++ b/mercuryio/config.c @@ -0,0 +1,25 @@ +#include + +#include +#include +#include + +#include "mercuryio/config.h" + +/* +Wacca Default key binding +*/ + +void mercury_io_config_load( + struct mercury_io_config *cfg, + const wchar_t *filename) +{ + wchar_t key[16]; + int i; + + assert(cfg != NULL); + assert(filename != NULL); + + cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename); + cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename); +} diff --git a/mercuryio/config.h b/mercuryio/config.h new file mode 100644 index 0000000..69cdf32 --- /dev/null +++ b/mercuryio/config.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include + +struct mercury_io_config { + uint8_t vk_test; + uint8_t vk_service; + uint8_t vk_1p_btn[9]; + uint8_t vk_2p_btn[9]; +}; + +void mercury_io_config_load( + struct mercury_io_config *cfg, + const wchar_t *filename); diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c new file mode 100644 index 0000000..93266ab --- /dev/null +++ b/mercuryio/mercuryio.c @@ -0,0 +1,133 @@ +#include + +#include +#include + +#include "mercuryio/mercuryio.h" +#include "mercuryio/config.h" + +static uint8_t mercury_opbtn; +static uint16_t mercury_player1_btn; +static uint16_t mercury_player2_btn; +static struct mercury_io_config mercury_io_cfg; + +uint16_t mercury_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT mercury_io_init(void) +{ + mercury_io_config_load(&mercury_io_cfg, L".\\segatools.ini"); + + return S_OK; +} + +HRESULT mercury_io_poll(void) +{ + mercury_opbtn = 0; + mercury_player1_btn = 0; + mercury_player2_btn = 0; + + if (GetAsyncKeyState(mercury_io_cfg.vk_test)) { + mercury_opbtn |= MAI2_IO_OPBTN_TEST; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_service)) { + mercury_opbtn |= MAI2_IO_OPBTN_SERVICE; + } + + //Player 1 + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[0])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_1; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[1])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_2; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[2])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_3; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[3])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_4; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[4])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_5; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[5])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_6; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[6])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_7; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[7])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_8; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[8])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_SELECT; + } + + //Player 2 + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[0])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_1; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[1])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_2; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[2])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_3; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[3])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_4; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[4])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_5; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[5])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_6; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[6])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_7; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[7])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_8; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[8])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_SELECT; + } + + return S_OK; +} + +void mercury_io_get_opbtns(uint8_t *opbtn) +{ + if (opbtn != NULL) { + *opbtn = mercury_opbtn; + } +} + +void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2) +{ + if (player1 != NULL) { + *player1 = mercury_player1_btn; + } + + if (player2 != NULL ){ + *player2 = mercury_player2_btn; + } +} \ No newline at end of file diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h new file mode 100644 index 0000000..85b3d5a --- /dev/null +++ b/mercuryio/mercuryio.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include + +enum { + MAI2_IO_OPBTN_TEST = 0x01, + MAI2_IO_OPBTN_SERVICE = 0x02, +}; + +enum { + MAI2_IO_GAMEBTN_1 = 0x01, + MAI2_IO_GAMEBTN_2 = 0x02, + MAI2_IO_GAMEBTN_3 = 0x04, + MAI2_IO_GAMEBTN_4 = 0x08, + MAI2_IO_GAMEBTN_5 = 0x10, + MAI2_IO_GAMEBTN_6 = 0x20, + MAI2_IO_GAMEBTN_7 = 0x40, + MAI2_IO_GAMEBTN_8 = 0x80, + MAI2_IO_GAMEBTN_SELECT = 0x100, +}; + +/* Get the version of the Wacca 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 mercury_io_get_api_version(void); + +/* Initialize the IO DLL. This is the second function that will be called on + your DLL, after mercury_io_get_api_version. + + All subsequent calls to this API may originate from arbitrary threads. + + Minimum API version: 0x0100 */ + +HRESULT mercury_io_init(void); + +/* Send any queued outputs (of which there are currently none, though this may + change in subsequent API versions) and retrieve any new inputs. + + Minimum API version: 0x0100 */ + +HRESULT mercury_io_poll(void); + +/* Get the state of the cabinet's operator buttons as of the last poll. See + MAI2_IO_OPBTN enum above: this contains bit mask definitions for button + states returned in *opbtn. All buttons are active-high. + + Minimum API version: 0x0100 */ + +void mercury_io_get_opbtns(uint8_t *opbtn); + +/* Get the state of the cabinet's gameplay buttons as of the last poll. See + MAI2_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into + a left hand side set of inputs and a right hand side set of inputs: the bit + mappings are the same in both cases. + + All buttons are active-high, even though some buttons' electrical signals + on a real cabinet are active-low. + + Minimum API version: 0x0100 */ + +void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2); diff --git a/mercuryio/meson.build b/mercuryio/meson.build new file mode 100644 index 0000000..813958a --- /dev/null +++ b/mercuryio/meson.build @@ -0,0 +1,13 @@ +mercuryio_lib = static_library( + 'mercuryio', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + sources : [ + 'mercuryio.c', + 'mercuryio.h', + 'config.c', + 'config.h', + ], +) diff --git a/meson.build b/meson.build index 50660ad..f143e72 100644 --- a/meson.build +++ b/meson.build @@ -48,9 +48,11 @@ subdir('chuniio') subdir('divaio') subdir('idzio') subdir('mu3io') +subdir('mercuryio') subdir('chunihook') subdir('divahook') subdir('idzhook') subdir('minihook') subdir('mu3hook') +subdir('mercuryhook')