1
0
Fork 0

mai2: segatools added

This commit is contained in:
Dniel97 2023-07-14 01:00:28 +02:00
parent 01be6ee33c
commit 0ee081ca1b
Signed by untrusted user: Dniel97
GPG Key ID: 6180B3C768FB2E08
20 changed files with 1000 additions and 0 deletions

View File

@ -17,6 +17,12 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo
* Initial D The Arcade
* SEGA World Drivers Championship
* SEGA World Drivers Championship 2019
* ONGEKI
* bright MEMORY
* maimai DX
* maimai DX FESTiVAL
* Card Maker
* Card Maker 1.35
* Wacca
* Wacca Lilly R (WIP)

70
dist/mai2/segatools.ini vendored Normal file
View File

@ -0,0 +1,70 @@
[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, Bxxx 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
[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=
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; SEGA games are somewhat picky about its 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.100.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.
[io4]
; 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
; Key bindings for buttons around screen. The default key map, depicted
; in clockwise order, is as follows:
;
; Player 1 Ring buttons: WEDCXZAQ, Select button: 3
; Player 2 Ring buttons: (Numpad) 89632147, Select button: (Numpad) *
;
; Select buttons are considered as button 9.
;
; Uncomment and complete the following sequence of settings to configure a
; custom keybinding.
[button]
;1p_btn1=0x53
;1p_btn2=0x53
;1p_btn3=0x53
; ... etc ...
;2p_btn1=0x53
;2p_btn2=0x53
;2p_btn3=0x53
; ... etc ...

11
dist/mai2/start.bat vendored Normal file
View File

@ -0,0 +1,11 @@
@echo off
pushd %~dp0
start /min inject -d -k mai2hook.dll amdaemon.exe -f -c config_common.json config_server.json config_client.json
inject -d -k mai2hook.dll sinmai -screen-fullscreen 0
taskkill /f /im amdaemon.exe > nul 2>&1
echo.
echo Game processes have terminated
pause

41
mai2hook/config.c Normal file
View File

@ -0,0 +1,41 @@
#include <assert.h>
#include <stddef.h>
#include "board/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "mai2hook/config.h"
#include "platform/config.h"
void mai2_dll_config_load(
struct mai2_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"mai2io",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void mai2_hook_config_load(
struct mai2_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);
mai2_dll_config_load(&cfg->dll, filename);
}

27
mai2hook/config.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "hooklib/dvd.h"
#include "mai2hook/mai2-dll.h"
#include "platform/config.h"
struct mai2_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct mai2_dll_config dll;
};
void mai2_dll_config_load(
struct mai2_dll_config *cfg,
const wchar_t *filename);
void mai2_hook_config_load(
struct mai2_hook_config *cfg,
const wchar_t *filename);

116
mai2hook/dllmain.c Normal file
View File

@ -0,0 +1,116 @@
#include <windows.h>
#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 "mai2hook/config.h"
#include "mai2hook/io4.h"
#include "mai2hook/mai2-dll.h"
#include "mai2hook/unity.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE mai2_hook_mod;
static process_entry_t mai2_startup;
static struct mai2_hook_config mai2_hook_cfg;
/* This hook is based on mu3hook, with leaked mai2hook i/o codes. */
static DWORD CALLBACK mai2_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin mai2_pre_startup ---\n");
/* Load config */
mai2_hook_config_load(&mai2_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&mai2_hook_cfg.dvd, mai2_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&mai2_hook_cfg.platform,
"SDEZ",
"ACA1",
mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&mai2_hook_cfg.aime, 1, mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
hr = mai2_dll_init(&mai2_hook_cfg.dll, mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = mai2_io4_hook_init(&mai2_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
/* Initialize Unity native plugin DLL hooks
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `mai2hook` initialization. */
unity_hook_init();
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End mai2_pre_startup ---\n");
/* Jump to EXE start address */
return mai2_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
mai2_hook_mod = mod;
hr = process_hijack_startup(mai2_pre_startup, &mai2_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

153
mai2hook/io4.c Normal file
View File

@ -0,0 +1,153 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "mai2hook/mai2-dll.h"
#include "util/dprintf.h"
static HRESULT mai2_io4_poll(void *ctx, struct io4_state *state);
static uint16_t coins;
static const struct io4_ops mai2_io4_ops = {
.poll = mai2_io4_poll,
};
HRESULT mai2_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(mai2_dll.init != NULL);
hr = io4_hook_init(cfg, &mai2_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return mai2_dll.init();
}
static HRESULT mai2_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
uint16_t player1;
uint16_t player2;
HRESULT hr;
assert(mai2_dll.poll != NULL);
assert(mai2_dll.get_opbtns != NULL);
assert(mai2_dll.get_gamebtns != NULL);
memset(state, 0, sizeof(*state));
hr = mai2_dll.poll();
if (FAILED(hr)) {
return hr;
}
opbtn = 0;
player1 = 0;
player2 = 0;
mai2_dll.get_opbtns(&opbtn);
mai2_dll.get_gamebtns(&player1, &player2);
if (opbtn & MAI2_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & MAI2_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & MAI2_IO_OPBTN_COIN) {
coins++;
}
state->chutes[0] = coins << 8;
// Buttons around screen are active-low, select button is active-high
// Player 1
if (!(player1 & MAI2_IO_GAMEBTN_1)) {
state->buttons[0] |= 1 << 2;
}
if (!(player1 & MAI2_IO_GAMEBTN_2)) {
state->buttons[0] |= 1 << 3;
}
if (!(player1 & MAI2_IO_GAMEBTN_3)) {
state->buttons[0] |= 1 << 0;
}
if (!(player1 & MAI2_IO_GAMEBTN_4)) {
state->buttons[0] |= 1 << 15;
}
if (!(player1 & MAI2_IO_GAMEBTN_5)) {
state->buttons[0] |= 1 << 14;
}
if (!(player1 & MAI2_IO_GAMEBTN_6)) {
state->buttons[0] |= 1 << 13;
}
if (!(player1 & MAI2_IO_GAMEBTN_7)) {
state->buttons[0] |= 1 << 12;
}
if (!(player1 & MAI2_IO_GAMEBTN_8)) {
state->buttons[0] |= 1 << 11;
}
if (player1 & MAI2_IO_GAMEBTN_SELECT) {
state->buttons[0] |= 1 << 1;
}
// Player 2
if (!(player2 & MAI2_IO_GAMEBTN_1)) {
state->buttons[1] |= 1 << 2;
}
if (!(player2 & MAI2_IO_GAMEBTN_2)) {
state->buttons[1] |= 1 << 3;
}
if (!(player2 & MAI2_IO_GAMEBTN_3)) {
state->buttons[1] |= 1 << 0;
}
if (!(player2 & MAI2_IO_GAMEBTN_4)) {
state->buttons[1] |= 1 << 15;
}
if (!(player2 & MAI2_IO_GAMEBTN_5)) {
state->buttons[1] |= 1 << 14;
}
if (!(player2 & MAI2_IO_GAMEBTN_6)) {
state->buttons[1] |= 1 << 13;
}
if (!(player2 & MAI2_IO_GAMEBTN_7)) {
state->buttons[1] |= 1 << 12;
}
if (!(player2 & MAI2_IO_GAMEBTN_8)) {
state->buttons[1] |= 1 << 11;
}
if (player2 & MAI2_IO_GAMEBTN_SELECT) {
state->buttons[1] |= 1 << 4;
}
return S_OK;
}

7
mai2hook/io4.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/io4.h"
HRESULT mai2_io4_hook_init(const struct io4_config *cfg);

109
mai2hook/mai2-dll.c Normal file
View File

@ -0,0 +1,109 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "mai2hook/mai2-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym mai2_dll_syms[] = {
{
.sym = "mai2_io_init",
.off = offsetof(struct mai2_dll, init),
}, {
.sym = "mai2_io_poll",
.off = offsetof(struct mai2_dll, poll),
}, {
.sym = "mai2_io_get_opbtns",
.off = offsetof(struct mai2_dll, get_opbtns),
}, {
.sym = "mai2_io_get_gamebtns",
.off = offsetof(struct mai2_dll, get_gamebtns),
}
};
struct mai2_dll mai2_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 mai2_dll_init(const struct mai2_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("Maimai DX IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Maimai DX IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "mai2_io_get_api_version");
if (get_api_version != NULL) {
mai2_dll.api_version = get_api_version();
} else {
mai2_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose mai2_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (mai2_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Maimai DX IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
mai2_dll.api_version);
goto end;
}
sym = mai2_dll_syms;
hr = dll_bind(&mai2_dll, src, &sym, _countof(mai2_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Maimai DX 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;
}

21
mai2hook/mai2-dll.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include "mai2io/mai2io.h"
struct mai2_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 mai2_dll_config {
wchar_t path[MAX_PATH];
};
extern struct mai2_dll mai2_dll;
HRESULT mai2_dll_init(const struct mai2_dll_config *cfg, HINSTANCE self);

18
mai2hook/mai2hook.def Normal file
View File

@ -0,0 +1,18 @@
LIBRARY mai2hook
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
mai2_io_get_api_version
mai2_io_get_gamebtns
mai2_io_get_opbtns
mai2_io_init
mai2_io_poll

31
mai2hook/meson.build Normal file
View File

@ -0,0 +1,31 @@
shared_library(
'mai2hook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'mai2hook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
],
link_with : [
aimeio_lib,
board_lib,
hooklib_lib,
mai2io_lib,
platform_lib,
util_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'mai2-dll.c',
'mai2-dll.h',
'unity.h',
'unity.c',
],
)

95
mai2hook/unity.c Normal file
View File

@ -0,0 +1,95 @@
#include <stdbool.h>
#include <windows.h>
#include "hook/table.h"
#include "hooklib/dll.h"
#include "hooklib/path.h"
#include "util/dprintf.h"
static void dll_hook_insert_hooks(HMODULE target);
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
static const struct hook_symbol unity_kernel32_syms[] = {
{
.name = "LoadLibraryW",
.patch = my_LoadLibraryW,
.link = (void **) &next_LoadLibraryW,
},
};
static const wchar_t *target_modules[] = {
L"mono-2.0-bdwgc.dll",
L"cri_ware_unity.dll",
};
static const size_t target_modules_len = _countof(target_modules);
void unity_hook_init(void)
{
dll_hook_insert_hooks(NULL);
}
static void dll_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
unity_kernel32_syms,
_countof(unity_kernel32_syms));
}
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
{
const wchar_t *name_end;
const wchar_t *target_module;
bool already_loaded;
HMODULE result;
size_t name_len;
size_t target_module_len;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// Check if the module is already loaded
already_loaded = GetModuleHandleW(name) != NULL;
// Must call the next handler so the DLL reference count is incremented
result = next_LoadLibraryW(name);
if (!already_loaded && result != NULL) {
name_len = wcslen(name);
for (size_t i = 0; i < target_modules_len; i++) {
target_module = target_modules[i];
target_module_len = wcslen(target_module);
// Check if the newly loaded library is at least the length of
// the name of the target module
if (name_len < target_module_len) {
continue;
}
name_end = &name[name_len - target_module_len];
// Check if the name of the newly loaded library is one of the
// modules the path hooks should be injected into
if (_wcsicmp(name_end, target_module) != 0) {
continue;
}
dprintf("Unity: Loaded %S\n", target_module);
dll_hook_insert_hooks(result);
path_hook_insert_hooks(result);
}
}
return result;
}

3
mai2hook/unity.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void unity_hook_init(void);

48
mai2io/config.c Normal file
View File

@ -0,0 +1,48 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "mai2io/config.h"
/*
Maimai DX Default key binding
1P: self-explanatory
2P: (Numpad) 8, 9, 6, 3, 2, 1, 4, 7, *
*/
static const int mai2_io_1p_default[] = {'W', 'E', 'D', 'C', 'X', 'Z', 'A', 'Q', '3'};
static const int mai2_io_2p_default[] = {0x68, 0x69, 0x66, 0x63, 0x62, 0x61, 0x64, 0x67, 0x54};
void mai2_io_config_load(
struct mai2_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);
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
for (i = 0 ; i < 9 ; i++) {
swprintf_s(key, _countof(key), L"1p_btn%i", i + 1);
cfg->vk_1p_btn[i] = GetPrivateProfileIntW(
L"button",
key,
mai2_io_1p_default[i],
filename);
}
for (i = 0 ; i < 9 ; i++) {
swprintf_s(key, _countof(key), L"2p_btn%i", i + 1);
cfg->vk_2p_btn[i] = GetPrivateProfileIntW(
L"button",
key,
mai2_io_2p_default[i],
filename);
}
}

18
mai2io/config.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct mai2_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_1p_btn[9];
uint8_t vk_2p_btn[9];
};
void mai2_io_config_load(
struct mai2_io_config *cfg,
const wchar_t *filename);

143
mai2io/mai2io.c Normal file
View File

@ -0,0 +1,143 @@
#include <windows.h>
#include <limits.h>
#include <stdint.h>
#include "mai2io/mai2io.h"
#include "mai2io/config.h"
static uint8_t mai2_opbtn;
static uint16_t mai2_player1_btn;
static uint16_t mai2_player2_btn;
static struct mai2_io_config mai2_io_cfg;
static bool mai2_io_coin;
uint16_t mai2_io_get_api_version(void)
{
return 0x0100;
}
HRESULT mai2_io_init(void)
{
mai2_io_config_load(&mai2_io_cfg, L".\\segatools.ini");
return S_OK;
}
HRESULT mai2_io_poll(void)
{
mai2_opbtn = 0;
mai2_player1_btn = 0;
mai2_player2_btn = 0;
if (GetAsyncKeyState(mai2_io_cfg.vk_test) & 0x8000) {
mai2_opbtn |= MAI2_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_service) & 0x8000) {
mai2_opbtn |= MAI2_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_coin) & 0x8000) {
if (!mai2_io_coin) {
mai2_io_coin = true;
mai2_opbtn |= MAI2_IO_OPBTN_COIN;
}
} else {
mai2_io_coin = false;
}
//Player 1
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[0])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_1;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[1])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_2;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[2])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_3;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[3])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_4;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[4])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_5;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[5])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_6;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[6])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_7;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[7])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_8;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[8])) {
mai2_player1_btn |= MAI2_IO_GAMEBTN_SELECT;
}
//Player 2
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[0])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_1;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[1])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_2;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[2])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_3;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[3])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_4;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[4])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_5;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[5])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_6;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[6])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_7;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[7])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_8;
}
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[8])) {
mai2_player2_btn |= MAI2_IO_GAMEBTN_SELECT;
}
return S_OK;
}
void mai2_io_get_opbtns(uint8_t *opbtn)
{
if (opbtn != NULL) {
*opbtn = mai2_opbtn;
}
}
void mai2_io_get_gamebtns(uint16_t *player1, uint16_t *player2)
{
if (player1 != NULL) {
*player1 = mai2_player1_btn;
}
if (player2 != NULL ){
*player2 = mai2_player2_btn;
}
}

68
mai2io/mai2io.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
MAI2_IO_OPBTN_TEST = 0x01,
MAI2_IO_OPBTN_SERVICE = 0x02,
MAI2_IO_OPBTN_COIN = 0x04,
};
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 Maimai 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 mai2_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after mai2_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT mai2_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 mai2_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 mai2_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 mai2_io_get_gamebtns(uint16_t *player1, uint16_t *player2);

13
mai2io/meson.build Normal file
View File

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

View File

@ -61,6 +61,7 @@ subdir('idzio')
subdir('idacio')
subdir('swdcio')
subdir('mu3io')
subdir('mai2io')
subdir('cmio')
subdir('mercuryio')
subdir('cxbio')
@ -74,6 +75,7 @@ subdir('swdchook')
subdir('minihook')
subdir('chusanhook')
subdir('mu3hook')
subdir('mai2hook')
subdir('cmhook')
subdir('mercuryhook')
subdir('cxbhook')