forked from TeamTofuShop/segatools
Mai2: Add touch and led15070 hook (#55)
In this PR, I have added the `mai2` touch and `led15070` hooks to provide an example for handling custom peripherals. This change allows users to implement the touch and `led15070` logic by writing appropriate `mai2io` scripts. #### **Touch Hook**: - The touch hook simulates touch points based on keyboard combinations. For example, to trigger the A1 touch point, the user must press the A and 1 keys on the keyboard. Input for the 1p requires Caps Lock to be off, while 2p requires Caps Lock to be on. - The hook allows for independent control of whether device simulation is enabled for "1p" and "2p" and whether keyboard input mapping is enabled. - **Note**: The current touch hook is not yet functional as it requires modifications to the `capnhook` for proper completion of the `sinmai` hook. #### **LED15070 Hook**: - This hook implements basic device simulation. Peripherals requiring lighting data should complete the logic as needed. - **Note**: The LED data refresh can flood the console logs, so I’ve added a `DEBUG` flag to control whether the debug logging is enabled or not. #### **Other Changes**: - In certain versions of `sinmai`, key inputs for 1p and 2p can be directly read from the keyboard without requiring simulation via the `amdaemon io4` hook. I’ve added a switch to control this behavior to prevent redundant input. - **Benefit**: This ensures that key input is only read when `sinmai` is in the foreground. If you'd like to learn more about the touch and `led15070` features, my research findings are available here: [Mai2Touch](https://github.com/Sucareto/Mai2Touch) Co-authored-by: Sucareto <28331534+Sucareto@users.noreply.github.com> Reviewed-on: TeamTofuShop/segatools#55 Co-authored-by: Mahuyo <mahuyo@noreply.gitea.tendokyu.moe> Co-committed-by: Mahuyo <mahuyo@noreply.gitea.tendokyu.moe>
This commit is contained in:
@ -238,7 +238,7 @@ static HRESULT led15070_handle_irp_locked(int board, struct irp *irp)
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("TX Buffer:\n");
|
||||
dump_iobuf(&boarduart->written);
|
||||
#endif
|
||||
@ -257,7 +257,7 @@ static HRESULT led15070_handle_irp_locked(int board, struct irp *irp)
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("Deframe Buffer:\n");
|
||||
dump_iobuf(&req_iobuf);
|
||||
#endif
|
||||
@ -385,7 +385,9 @@ static HRESULT led15070_req_reset(int board, const struct led15070_req_any *req)
|
||||
|
||||
static HRESULT led15070_req_set_input(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set input (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
@ -408,9 +410,10 @@ static HRESULT led15070_req_set_input(int board, const struct led15070_req_any *
|
||||
static HRESULT led15070_req_set_normal_12bit(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
uint8_t idx = req->payload[0];
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Normal 12bit (board %u, index %u)\n",
|
||||
board, idx);
|
||||
#endif
|
||||
|
||||
// TODO: Data for this command. Seen with Carol
|
||||
|
||||
@ -435,9 +438,10 @@ static HRESULT led15070_req_set_normal_12bit(int board, const struct led15070_re
|
||||
static HRESULT led15070_req_set_normal_8bit(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
uint8_t idx = req->payload[0];
|
||||
|
||||
// dprintf("LED 15070: Set LED - Normal 8bit (board %u, index %u)\n",
|
||||
// board, idx);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Normal 8bit (board %u, index %u)\n",
|
||||
board, idx);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].gs[idx][0] = req->payload[1]; // R
|
||||
led15070_per_board_vars[board].gs[idx][1] = req->payload[2]; // G
|
||||
@ -468,8 +472,10 @@ static HRESULT led15070_req_set_multi_flash_8bit(int board, const struct led1507
|
||||
uint8_t idx_skip = req->payload[2];
|
||||
|
||||
// TODO: useful?
|
||||
// dprintf("LED 15070: Set LED - Multi flash 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
// board, idx_start, idx_end, idx_skip);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Multi flash 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
board, idx_start, idx_end, idx_skip);
|
||||
#endif
|
||||
|
||||
if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) {
|
||||
idx_start += idx_skip;
|
||||
@ -508,9 +514,10 @@ static HRESULT led15070_req_set_multi_fade_8bit(int board, const struct led15070
|
||||
uint8_t idx_start = req->payload[0];
|
||||
uint8_t idx_end = req->payload[1];
|
||||
uint8_t idx_skip = req->payload[2];
|
||||
|
||||
// dprintf("LED 15070: Set LED - Multi fade 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
// board, idx_start, idx_end, idx_skip);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Multi fade 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
board, idx_start, idx_end, idx_skip);
|
||||
#endif
|
||||
|
||||
if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) {
|
||||
idx_start += idx_skip;
|
||||
@ -545,7 +552,9 @@ static HRESULT led15070_req_set_multi_fade_8bit(int board, const struct led15070
|
||||
|
||||
static HRESULT led15070_req_set_palette_7_normal_led(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set palette - 7 Normal LED (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
@ -567,7 +576,9 @@ static HRESULT led15070_req_set_palette_7_normal_led(int board, const struct led
|
||||
|
||||
static HRESULT led15070_req_set_palette_6_flash_led(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set palette - 6 Flash LED (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
@ -589,7 +600,9 @@ static HRESULT led15070_req_set_palette_6_flash_led(int board, const struct led1
|
||||
|
||||
static HRESULT led15070_req_set_15dc_out(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set 15DC out (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
@ -611,7 +624,9 @@ static HRESULT led15070_req_set_15dc_out(int board, const struct led15070_req_an
|
||||
|
||||
static HRESULT led15070_req_set_15gs_out(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set 15GS out (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
@ -633,7 +648,9 @@ static HRESULT led15070_req_set_15gs_out(int board, const struct led15070_req_an
|
||||
|
||||
static HRESULT led15070_req_set_psc_max(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set PSC max (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
@ -655,7 +672,9 @@ static HRESULT led15070_req_set_psc_max(int board, const struct led15070_req_any
|
||||
|
||||
static HRESULT led15070_req_set_fet_output(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set FET output (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].fet[0] = req->payload[0]; // R or FET0 intensity
|
||||
led15070_per_board_vars[board].fet[1] = req->payload[1]; // G or FET1 intensity
|
||||
@ -685,8 +704,9 @@ static HRESULT led15070_req_set_fet_output(int board, const struct led15070_req_
|
||||
static HRESULT led15070_req_set_gs_palette(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
uint8_t idx = req->payload[0];
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set GS palette (board %u, index %u)\n", board, idx);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].gs_palette[idx][0] = req->payload[1]; // R
|
||||
led15070_per_board_vars[board].gs_palette[idx][1] = req->payload[2]; // G
|
||||
@ -712,7 +732,9 @@ static HRESULT led15070_req_set_gs_palette(int board, const struct led15070_req_
|
||||
|
||||
static HRESULT led15070_req_dc_update(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
// dprintf("LED 15070: DC update (board %u)\n", board);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: DC update (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (led_dc_update)
|
||||
led_dc_update((const uint8_t*)led15070_per_board_vars[board].dc);
|
||||
@ -737,7 +759,9 @@ static HRESULT led15070_req_dc_update(int board, const struct led15070_req_any *
|
||||
|
||||
static HRESULT led15070_req_gs_update(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
// dprintf("LED 15070: GS update (board %u)\n", board);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: GS update (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (led_gs_update)
|
||||
led_gs_update((const uint8_t*)led15070_per_board_vars[board].gs);
|
||||
@ -762,7 +786,9 @@ static HRESULT led15070_req_gs_update(int board, const struct led15070_req_any *
|
||||
|
||||
static HRESULT led15070_req_rotate(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Rotate (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
@ -787,9 +813,10 @@ static HRESULT led15070_req_set_dc_data(int board, const struct led15070_req_any
|
||||
uint8_t idx_start = req->payload[0];
|
||||
uint8_t idx_end = req->payload[1];
|
||||
uint8_t idx_skip = req->payload[2];
|
||||
|
||||
// dprintf("LED 15070: Set DC data (board %u, start %u, end %u, skip %u)\n",
|
||||
// board, idx_start, idx_end, idx_skip);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set DC data (board %u, start %u, end %u, skip %u)\n",
|
||||
board, idx_start, idx_end, idx_skip);
|
||||
#endif
|
||||
|
||||
if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) {
|
||||
idx_start += idx_skip;
|
||||
@ -829,9 +856,10 @@ static HRESULT led15070_req_eeprom_write(int board, const struct led15070_req_an
|
||||
|
||||
uint8_t addr = req->payload[0];
|
||||
uint8_t data = req->payload[1];
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: EEPROM write (board %u, address %02x, data %02x)\n",
|
||||
board, addr, data);
|
||||
#endif
|
||||
|
||||
if (addr > 0x07) {
|
||||
dprintf("LED 15070: Error -- Invalid EEPROM write address %02x\n",
|
||||
@ -919,8 +947,9 @@ static HRESULT led15070_req_eeprom_read(int board, const struct led15070_req_any
|
||||
|
||||
uint8_t addr = req->payload[0];
|
||||
uint8_t data = 0;
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: EEPROM read (board %u, address %02x)\n", board, addr);
|
||||
#endif
|
||||
|
||||
if (addr > 0x07) {
|
||||
dprintf("LED 15070: Error -- Invalid EEPROM read address %02x\n",
|
||||
@ -1002,7 +1031,9 @@ static HRESULT led15070_req_eeprom_read(int board, const struct led15070_req_any
|
||||
|
||||
static HRESULT led15070_req_ack_on(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Acknowledge commands ON (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].enable_response = true;
|
||||
|
||||
@ -1023,7 +1054,9 @@ static HRESULT led15070_req_ack_on(int board, const struct led15070_req_any *req
|
||||
|
||||
static HRESULT led15070_req_ack_off(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Acknowledge commands OFF (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].enable_response = false;
|
||||
|
||||
@ -1044,7 +1077,9 @@ static HRESULT led15070_req_ack_off(int board, const struct led15070_req_any *re
|
||||
|
||||
static HRESULT led15070_req_board_info(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get board info (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_board_info resp;
|
||||
|
||||
@ -1067,7 +1102,9 @@ static HRESULT led15070_req_board_info(int board)
|
||||
|
||||
static HRESULT led15070_req_board_status(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get board status (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_any resp;
|
||||
|
||||
@ -1091,7 +1128,9 @@ static HRESULT led15070_req_board_status(int board)
|
||||
|
||||
static HRESULT led15070_req_fw_sum(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get firmware checksum (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_any resp;
|
||||
|
||||
@ -1113,7 +1152,9 @@ static HRESULT led15070_req_fw_sum(int board)
|
||||
|
||||
static HRESULT led15070_req_protocol_ver(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get protocol version (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_any resp;
|
||||
|
||||
@ -1187,7 +1228,7 @@ static HRESULT led15070_eeprom_open(int board, wchar_t *path, HANDLE *handle)
|
||||
HRESULT hr;
|
||||
BOOL ok;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Opening EEPROM file '%S' handle (board %u)\n", path, board);
|
||||
#endif
|
||||
|
||||
@ -1233,7 +1274,7 @@ static HRESULT led15070_eeprom_close(int board, wchar_t *path, HANDLE *handle)
|
||||
HRESULT hr;
|
||||
BOOL ok;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Closing EEPROM file '%S' handle (board %u)\n", path, board);
|
||||
#endif
|
||||
|
||||
|
28
dist/mai2/segatools.ini
vendored
28
dist/mai2/segatools.ini
vendored
@ -126,11 +126,27 @@ coin=0x72
|
||||
; Uncomment and complete the following sequence of settings to configure a
|
||||
; custom keybinding.
|
||||
[button]
|
||||
;1p_btn1=0x53
|
||||
;1p_btn2=0x53
|
||||
;1p_btn3=0x53
|
||||
enable=1
|
||||
;p1Btn1=0x53
|
||||
;p1Btn2=0x53
|
||||
;p1Btn3=0x53
|
||||
; ... etc ...
|
||||
;2p_btn1=0x53
|
||||
;2p_btn2=0x53
|
||||
;2p_btn3=0x53
|
||||
;p2Btn1=0x53
|
||||
;p2Btn2=0x53
|
||||
;p2Btn3=0x53
|
||||
; ... etc ...
|
||||
|
||||
[touch]
|
||||
p1Enable=1
|
||||
;p1DebugInput=0
|
||||
p2Enable=1
|
||||
;p2DebugInput=0
|
||||
;p1TouchA1=0x53
|
||||
;p1TouchA2=0x53
|
||||
; ... etc ...
|
||||
;p1TouchE8=0x53
|
||||
|
||||
[led15070]
|
||||
; Enable emulation of the 837-15070-02 controlled lights, which handle the
|
||||
; cabinet and seat LEDs.
|
||||
enable=1
|
@ -308,7 +308,7 @@ static void reg_hook_init(void)
|
||||
|
||||
reg_hook_initted = true;
|
||||
InitializeCriticalSection(®_hook_lock);
|
||||
dprintf("Reg hook init\n");
|
||||
dprintf("Reg: hook init.\n");
|
||||
|
||||
reg_hook_insert_hooks(NULL);
|
||||
|
||||
|
@ -12,24 +12,70 @@
|
||||
#include "platform/config.h"
|
||||
|
||||
void mai2_dll_config_load(
|
||||
struct mai2_dll_config *cfg,
|
||||
const wchar_t *filename)
|
||||
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);
|
||||
L"mai2io",
|
||||
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_1p = GetPrivateProfileIntW(L"touch", L"p1Enable", 1, filename);
|
||||
cfg->enable_2p = GetPrivateProfileIntW(L"touch", L"p2Enable", 1, filename);
|
||||
}
|
||||
|
||||
void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
wchar_t tmpstr[16];
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15070", L"fwVer", 0x90, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15070", L"fwSum", 0x00, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15070",
|
||||
L"boardNumber",
|
||||
L"15070-04",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
|
||||
for (int i = n; i < sizeof(cfg->board_number); i++)
|
||||
{
|
||||
cfg->board_number[i] = ' ';
|
||||
}
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15070",
|
||||
L"eepromPath",
|
||||
L"DEVICE",
|
||||
cfg->eeprom_path,
|
||||
_countof(cfg->eeprom_path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void mai2_hook_config_load(
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename)
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
@ -40,5 +86,7 @@ void mai2_hook_config_load(
|
||||
io4_config_load(&cfg->io4, filename);
|
||||
vfd_config_load(&cfg->vfd, filename);
|
||||
mai2_dll_config_load(&cfg->dll, filename);
|
||||
touch_config_load(&cfg->touch, filename);
|
||||
led15070_config_load(&cfg->led15070, filename);
|
||||
unity_config_load(&cfg->unity, filename);
|
||||
}
|
||||
|
@ -10,6 +10,10 @@
|
||||
|
||||
#include "platform/config.h"
|
||||
|
||||
#include "mai2hook/touch.h"
|
||||
|
||||
#include "board/led15070.h"
|
||||
|
||||
#include "unityhook/config.h"
|
||||
|
||||
struct mai2_hook_config {
|
||||
@ -19,13 +23,23 @@ struct mai2_hook_config {
|
||||
struct io4_config io4;
|
||||
struct vfd_config vfd;
|
||||
struct mai2_dll_config dll;
|
||||
struct touch_config touch;
|
||||
struct led15070_config led15070;
|
||||
struct unity_config unity;
|
||||
};
|
||||
|
||||
void mai2_dll_config_load(
|
||||
struct mai2_dll_config *cfg,
|
||||
const wchar_t *filename);
|
||||
struct mai2_dll_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void touch_config_load(
|
||||
struct touch_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void led15070_config_load(
|
||||
struct led15070_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void mai2_hook_config_load(
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename);
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
@ -67,16 +67,44 @@ static DWORD CALLBACK mai2_pre_startup(void)
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
hr = platform_hook_init(
|
||||
&mai2_hook_cfg.platform,
|
||||
"SDEZ",
|
||||
"ACA1",
|
||||
mai2_hook_mod);
|
||||
&mai2_hook_cfg.platform,
|
||||
"SDEZ",
|
||||
"ACA1",
|
||||
mai2_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&mai2_hook_cfg.aime, 1, 1, mai2_hook_mod);
|
||||
/* Initialize DLLs */
|
||||
|
||||
hr = mai2_dll_init(&mai2_hook_cfg.dll, mai2_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Touch Panel uses COM3 and COM4
|
||||
|
||||
hr = touch_hook_init(&mai2_hook_cfg.touch);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// LED board uses COM21 and COM23
|
||||
hr = led15070_hook_init(&mai2_hook_cfg.led15070,
|
||||
mai2_dll.led_init,
|
||||
mai2_dll.led_set_fet_output,
|
||||
mai2_dll.led_dc_update,
|
||||
mai2_dll.led_gs_update,
|
||||
21, 2);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&mai2_hook_cfg.aime, 1, 3, mai2_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
@ -88,12 +116,6 @@ static DWORD CALLBACK mai2_pre_startup(void)
|
||||
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)) {
|
||||
|
@ -21,7 +21,28 @@ const struct dll_bind_sym mai2_dll_syms[] = {
|
||||
}, {
|
||||
.sym = "mai2_io_get_gamebtns",
|
||||
.off = offsetof(struct mai2_dll, get_gamebtns),
|
||||
}
|
||||
}, {
|
||||
.sym = "mai2_io_touch_init",
|
||||
.off = offsetof(struct mai2_dll, touch_init),
|
||||
}, {
|
||||
.sym = "mai2_io_touch_set_sens",
|
||||
.off = offsetof(struct mai2_dll, touch_set_sens),
|
||||
}, {
|
||||
.sym = "mai2_io_touch_update",
|
||||
.off = offsetof(struct mai2_dll, touch_update),
|
||||
}, {
|
||||
.sym = "mai2_io_led_init",
|
||||
.off = offsetof(struct mai2_dll, led_init),
|
||||
}, {
|
||||
.sym = "mai2_io_led_set_fet_output",
|
||||
.off = offsetof(struct mai2_dll, led_set_fet_output),
|
||||
}, {
|
||||
.sym = "mai2_io_led_dc_update",
|
||||
.off = offsetof(struct mai2_dll, led_dc_update),
|
||||
}, {
|
||||
.sym = "mai2_io_led_gs_update",
|
||||
.off = offsetof(struct mai2_dll, led_gs_update),
|
||||
},
|
||||
};
|
||||
|
||||
struct mai2_dll mai2_dll;
|
||||
@ -67,7 +88,7 @@ HRESULT mai2_dll_init(const struct mai2_dll_config *cfg, HINSTANCE self)
|
||||
if (get_api_version != NULL) {
|
||||
mai2_dll.api_version = get_api_version();
|
||||
} else {
|
||||
mai2_dll.api_version = 0x0100;
|
||||
mai2_dll.api_version = 0x0101;
|
||||
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");
|
||||
|
@ -10,6 +10,13 @@ struct mai2_dll {
|
||||
HRESULT (*poll)(void);
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint16_t *player1, uint16_t *player2);
|
||||
HRESULT (*touch_init)(mai2_io_touch_callback_t callback);
|
||||
void (*touch_set_sens)(uint8_t *bytes);
|
||||
void (*touch_update)(bool player1, bool player2);
|
||||
HRESULT (*led_init)(void);
|
||||
void (*led_set_fet_output)(const uint8_t *rgb);
|
||||
void (*led_dc_update)(const uint8_t *rgb);
|
||||
void (*led_gs_update)(const uint8_t *rgb);
|
||||
};
|
||||
|
||||
struct mai2_dll_config {
|
||||
|
@ -15,4 +15,11 @@ EXPORTS
|
||||
mai2_io_get_gamebtns
|
||||
mai2_io_get_opbtns
|
||||
mai2_io_init
|
||||
mai2_io_poll
|
||||
mai2_io_poll
|
||||
mai2_io_touch_init
|
||||
mai2_io_touch_set_sens
|
||||
mai2_io_touch_update
|
||||
mai2_io_led_init
|
||||
mai2_io_led_set_fet_output
|
||||
mai2_io_led_dc_update
|
||||
mai2_io_led_gs_update
|
||||
|
@ -23,6 +23,8 @@ shared_library(
|
||||
'dllmain.c',
|
||||
'io4.c',
|
||||
'io4.h',
|
||||
'touch.c',
|
||||
'touch.h',
|
||||
'mai2-dll.c',
|
||||
'mai2-dll.h',
|
||||
],
|
||||
|
248
mai2hook/touch.c
Normal file
248
mai2hook/touch.c
Normal file
@ -0,0 +1,248 @@
|
||||
#include "mai2hook/touch.h"
|
||||
|
||||
static HRESULT read_reg_touch_1p(void *bytes, uint32_t *nbytes)
|
||||
{
|
||||
return reg_hook_read_wstr(bytes, nbytes, L"COM3");
|
||||
}
|
||||
|
||||
static HRESULT read_reg_touch_2p(void *bytes, uint32_t *nbytes)
|
||||
{
|
||||
return reg_hook_read_wstr(bytes, nbytes, L"COM4");
|
||||
}
|
||||
|
||||
static const struct reg_hook_val touch_reg_key[] = {
|
||||
{
|
||||
.name = L"\\Device\\RealTouchBoard0",
|
||||
.read = read_reg_touch_1p,
|
||||
.type = REG_SZ,
|
||||
},
|
||||
{
|
||||
.name = L"\\Device\\RealTouchBoard1",
|
||||
.read = read_reg_touch_2p,
|
||||
.type = REG_SZ,
|
||||
},
|
||||
};
|
||||
|
||||
const char *sensor_map[34] = {
|
||||
"A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", // 0x41 - 0x48
|
||||
"B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", // 0x49 - 0x50
|
||||
"C1", "C2", // 0x51 - 0x52
|
||||
"D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", // 0x53 - 0x5A
|
||||
"E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8" // 0x5B - 0x62
|
||||
};
|
||||
|
||||
const char *sensor_to_str(uint8_t sensor)
|
||||
{
|
||||
if (sensor < 0x41 || sensor > 0x62)
|
||||
{
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
return sensor_map[sensor - 0x41];
|
||||
}
|
||||
|
||||
static CRITICAL_SECTION touch_1p_lock;
|
||||
static struct uart touch_1p_uart;
|
||||
static uint8_t touch_1p_written_bytes[64];
|
||||
static uint8_t touch_1p_readable_bytes[64];
|
||||
static bool touch_1p_status = false;
|
||||
|
||||
static CRITICAL_SECTION touch_2p_lock;
|
||||
static struct uart touch_2p_uart;
|
||||
static uint8_t touch_2p_written_bytes[64];
|
||||
static uint8_t touch_2p_readable_bytes[64];
|
||||
static bool touch_2p_status = false;
|
||||
|
||||
HRESULT touch_hook_init(const struct touch_config *cfg)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable_1p && !cfg->enable_2p)
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT hr = reg_hook_push_key(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", touch_reg_key, _countof(touch_reg_key));
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (cfg->enable_1p)
|
||||
{
|
||||
dprintf("Mai2 touch 1P: Init.\n");
|
||||
|
||||
InitializeCriticalSection(&touch_1p_lock);
|
||||
uart_init(&touch_1p_uart, 3);
|
||||
touch_1p_uart.written.bytes = touch_1p_written_bytes;
|
||||
touch_1p_uart.written.nbytes = sizeof(touch_1p_written_bytes);
|
||||
touch_1p_uart.readable.bytes = touch_1p_readable_bytes;
|
||||
touch_1p_uart.readable.nbytes = sizeof(touch_1p_readable_bytes);
|
||||
}
|
||||
|
||||
if (cfg->enable_2p)
|
||||
{
|
||||
dprintf("Mai2 touch port 2P: Init.\n");
|
||||
|
||||
InitializeCriticalSection(&touch_2p_lock);
|
||||
uart_init(&touch_2p_uart, 4);
|
||||
touch_2p_uart.written.bytes = touch_2p_written_bytes;
|
||||
touch_2p_uart.written.nbytes = sizeof(touch_2p_written_bytes);
|
||||
touch_2p_uart.readable.bytes = touch_2p_readable_bytes;
|
||||
touch_2p_uart.readable.nbytes = sizeof(touch_2p_readable_bytes);
|
||||
}
|
||||
|
||||
return iohook_push_handler(touch_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT touch_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (uart_match_irp(&touch_1p_uart, irp))
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
hr = touch_handle_irp_locked(irp, &touch_1p_uart);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else if (uart_match_irp(&touch_2p_uart, irp))
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
hr = touch_handle_irp_locked(irp, &touch_2p_uart);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (irp->op == IRP_OP_OPEN)
|
||||
{
|
||||
dprintf("Mai2 touch port %d: Starting backend\n", uart->port_no);
|
||||
hr = mai2_dll.touch_init(touch_auto_scan);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
dprintf("Mai2 touch port %d: Backend error: %x\n", uart->port_no, (int)hr);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch port %d WRITE:\n", uart->port_no);
|
||||
dump_iobuf(&uart->written);
|
||||
#endif
|
||||
uint8_t port_no = uart->port_no;
|
||||
uint8_t *src = uart->written.bytes;
|
||||
uint8_t *dest = uart->readable.bytes;
|
||||
|
||||
switch (src[3])
|
||||
{
|
||||
case commandRSET:
|
||||
dprintf("Mai2 touch port %d: Reset\n", port_no);
|
||||
break;
|
||||
|
||||
case commandHALT: // Enter Conditioning mode and stop sending touch data.
|
||||
dprintf("Mai2 touch port %d: Halt\n", port_no);
|
||||
assert(mai2_dll.touch_update != NULL);
|
||||
if (port_no == 3)
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
touch_1p_status = false;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
touch_2p_status = false;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
break;
|
||||
|
||||
case commandSTAT: // Exit Conditioning mode and resume sending touch data.
|
||||
dprintf("Mai2 touch port %d: Stat\n", port_no);
|
||||
assert(mai2_dll.touch_update != NULL);
|
||||
if (port_no == 3)
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
touch_1p_status = true;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
touch_2p_status = true;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
break;
|
||||
|
||||
case commandRatio:
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch side %c: set sensor %s ratio to %d\n", src[1], sensor_to_str(src[2]), src[4]);
|
||||
#endif
|
||||
dest[0] = res_start;
|
||||
dest[1] = src[1]; // L,R
|
||||
dest[2] = src[2]; // sensor
|
||||
dest[3] = commandRatio;
|
||||
dest[4] = src[4]; // Ratio
|
||||
dest[5] = res_end;
|
||||
uart->readable.pos = 6;
|
||||
// The Ratio is fixed at 0x72 and does not need to be sent to mai2io for processing.
|
||||
break;
|
||||
|
||||
case commandSens:
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch side %c: set sensor %s sensitivity to %d\n", src[1], sensor_to_str(src[2]), src[4]);
|
||||
#endif
|
||||
dest[0] = res_start;
|
||||
dest[1] = src[1]; // L,R
|
||||
dest[2] = src[2]; // sensor
|
||||
dest[3] = commandSens;
|
||||
dest[4] = src[4]; // Sensitivity
|
||||
dest[5] = res_end;
|
||||
uart->readable.pos = 6;
|
||||
mai2_dll.touch_set_sens(dest);
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintf("Mai2 touch port %d: Unknow %02x\n", port_no, src[3]);
|
||||
break;
|
||||
}
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch port %d READ:\n", uart->port_no);
|
||||
dump_iobuf(&uart->readable);
|
||||
#endif
|
||||
uart->written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void touch_auto_scan(const uint8_t player, const uint8_t state[7])
|
||||
{
|
||||
struct uart *touch_uart = player == 1 ? &touch_1p_uart : &touch_2p_uart;
|
||||
touch_uart->readable.bytes[0] = res_start;
|
||||
memcpy(&touch_uart->readable.bytes[1], state, 7);
|
||||
touch_uart->readable.bytes[8] = res_end;
|
||||
touch_uart->readable.pos = 9;
|
||||
}
|
43
mai2hook/touch.h
Normal file
43
mai2hook/touch.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "hooklib/fdshark.h"
|
||||
#include "hooklib/reg.h"
|
||||
#include "hooklib/uart.h"
|
||||
#include "mai2hook/mai2-dll.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
struct touch_config
|
||||
{
|
||||
bool enable_1p;
|
||||
bool enable_2p;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
commandRSET = 0x45, // E
|
||||
commandHALT = 0x4C, // L
|
||||
commandSTAT = 0x41, // A
|
||||
commandRatio = 0x72, // r
|
||||
commandSens = 0x6B, // k
|
||||
req_start = 0x7b, // {
|
||||
req_end = 0x7d, // }
|
||||
res_start = 0x28, // (
|
||||
res_end = 0x29, // )
|
||||
};
|
||||
|
||||
extern const char *sensor_map[34];
|
||||
const char *sensor_to_str(uint8_t sensor);
|
||||
|
||||
HRESULT touch_hook_init(const struct touch_config *cfg);
|
||||
static HRESULT touch_handle_irp(struct irp *irp);
|
||||
static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart);
|
||||
|
||||
/* Called in mai2io to send touch data.
|
||||
Similar to chuni slider_res_auto_scan, but the host does not require periodic updates.
|
||||
Touch data is sent only when there is a change. */
|
||||
static void touch_auto_scan(const uint8_t player, const uint8_t state[7]);
|
@ -6,6 +6,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mai2io/config.h"
|
||||
#include "mai2hook/touch.h"
|
||||
|
||||
/*
|
||||
Maimai DX Default key binding
|
||||
@ -15,9 +16,24 @@ Maimai DX Default key binding
|
||||
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};
|
||||
|
||||
static const int mai2_io_1p_touch_default[] = {
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
'G', 'G',
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
};
|
||||
static const int mai2_io_2p_touch_default[] = {
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
'K', 'K',
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
};
|
||||
|
||||
void mai2_io_config_load(
|
||||
struct mai2_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
struct mai2_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
wchar_t key[16];
|
||||
int i;
|
||||
@ -28,22 +44,41 @@ void mai2_io_config_load(
|
||||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, filename);
|
||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
|
||||
cfg->vk_btn_enable = GetPrivateProfileIntW(L"button", L"enable", 1, filename);
|
||||
|
||||
for (i = 0 ; i < 9 ; i++) {
|
||||
swprintf_s(key, _countof(key), L"1p_btn%i", i + 1);
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
swprintf_s(key, _countof(key), L"p1Btn%i", i + 1);
|
||||
cfg->vk_1p_btn[i] = GetPrivateProfileIntW(
|
||||
L"button",
|
||||
key,
|
||||
mai2_io_1p_default[i],
|
||||
filename);
|
||||
L"button",
|
||||
key,
|
||||
mai2_io_1p_default[i],
|
||||
filename);
|
||||
|
||||
swprintf_s(key, _countof(key), L"p2Btn%i", i + 1);
|
||||
cfg->vk_2p_btn[i] = GetPrivateProfileIntW(
|
||||
L"button",
|
||||
key,
|
||||
mai2_io_2p_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);
|
||||
cfg->debug_input_1p = GetPrivateProfileIntW(L"touch", L"p1DebugInput", 0, filename);
|
||||
cfg->debug_input_2p = GetPrivateProfileIntW(L"touch", L"p2DebugInput", 0, filename);
|
||||
for (i = 0; i < 34; i++)
|
||||
{
|
||||
swprintf_s(key, _countof(key), L"p1Touch%S", sensor_map[i]);
|
||||
cfg->vk_1p_touch[i] = GetPrivateProfileIntW(
|
||||
L"touch",
|
||||
key,
|
||||
mai2_io_1p_touch_default[i],
|
||||
filename);
|
||||
|
||||
swprintf_s(key, _countof(key), L"p2Touch%S", sensor_map[i]);
|
||||
cfg->vk_2p_touch[i] = GetPrivateProfileIntW(
|
||||
L"touch",
|
||||
key,
|
||||
mai2_io_2p_touch_default[i],
|
||||
filename);
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,13 @@ struct mai2_io_config {
|
||||
uint8_t vk_test;
|
||||
uint8_t vk_service;
|
||||
uint8_t vk_coin;
|
||||
bool vk_btn_enable;
|
||||
uint8_t vk_1p_btn[9];
|
||||
uint8_t vk_2p_btn[9];
|
||||
bool debug_input_1p;
|
||||
bool debug_input_2p;
|
||||
uint8_t vk_1p_touch[34];
|
||||
uint8_t vk_2p_touch[34];
|
||||
};
|
||||
|
||||
void mai2_io_config_load(
|
||||
|
163
mai2io/mai2io.c
163
mai2io/mai2io.c
@ -1,10 +1,9 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <process.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mai2io/mai2io.h"
|
||||
#include "mai2io/config.h"
|
||||
#include "mai2hook/touch.h"
|
||||
#include "util/env.h"
|
||||
|
||||
static uint8_t mai2_opbtn;
|
||||
@ -12,10 +11,15 @@ 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;
|
||||
mai2_io_touch_callback_t _callback;
|
||||
static HANDLE mai2_io_touch_1p_thread;
|
||||
static bool mai2_io_touch_1p_stop_flag;
|
||||
static HANDLE mai2_io_touch_2p_thread;
|
||||
static bool mai2_io_touch_2p_stop_flag;
|
||||
|
||||
uint16_t mai2_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
return 0x0101;
|
||||
}
|
||||
|
||||
HRESULT mai2_io_init(void)
|
||||
@ -47,6 +51,10 @@ HRESULT mai2_io_poll(void)
|
||||
} else {
|
||||
mai2_io_coin = false;
|
||||
}
|
||||
// If sinmai has enabled DebugInput, there is no need to input buttons through hook amdaemon.
|
||||
if (!mai2_io_cfg.vk_btn_enable) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//Player 1
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[0])) {
|
||||
@ -142,3 +150,150 @@ void mai2_io_get_gamebtns(uint16_t *player1, uint16_t *player2)
|
||||
*player2 = mai2_player2_btn;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT mai2_io_touch_init(mai2_io_touch_callback_t callback)
|
||||
{
|
||||
_callback = callback;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void mai2_io_touch_set_sens(uint8_t *bytes)
|
||||
{
|
||||
#if 0
|
||||
dprintf("Mai2 touch side %c: set sensor %s sensitivity to %d\n", bytes[1], sensor_to_str(bytes[2]), bytes[4]);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void mai2_io_touch_update(bool player1, bool player2)
|
||||
{
|
||||
if (mai2_io_cfg.debug_input_1p)
|
||||
{
|
||||
if (player1 && mai2_io_touch_1p_thread == NULL)
|
||||
{
|
||||
mai2_io_touch_1p_thread = (HANDLE)_beginthreadex(
|
||||
NULL,
|
||||
0,
|
||||
mai2_io_touch_1p_thread_proc,
|
||||
_callback,
|
||||
0,
|
||||
NULL);
|
||||
}
|
||||
else if (!player1 && mai2_io_touch_1p_thread != NULL)
|
||||
{
|
||||
mai2_io_touch_1p_stop_flag = true;
|
||||
|
||||
WaitForSingleObject(mai2_io_touch_1p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_1p_thread);
|
||||
mai2_io_touch_1p_thread = NULL;
|
||||
|
||||
mai2_io_touch_1p_stop_flag = false;
|
||||
}
|
||||
}
|
||||
if (mai2_io_cfg.debug_input_2p)
|
||||
{
|
||||
if (player2 && mai2_io_touch_2p_thread == NULL)
|
||||
{
|
||||
mai2_io_touch_2p_thread = (HANDLE)_beginthreadex(
|
||||
NULL,
|
||||
0,
|
||||
mai2_io_touch_2p_thread_proc,
|
||||
_callback,
|
||||
0,
|
||||
NULL);
|
||||
}
|
||||
else if (!player2 && mai2_io_touch_2p_thread != NULL)
|
||||
{
|
||||
mai2_io_touch_2p_stop_flag = true;
|
||||
|
||||
WaitForSingleObject(mai2_io_touch_2p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_2p_thread);
|
||||
mai2_io_touch_2p_thread = NULL;
|
||||
|
||||
mai2_io_touch_2p_stop_flag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int __stdcall mai2_io_touch_1p_thread_proc(void *ctx)
|
||||
{
|
||||
mai2_io_touch_callback_t callback = ctx;
|
||||
|
||||
while (!mai2_io_touch_1p_stop_flag)
|
||||
{
|
||||
uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 34; i++)
|
||||
{
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_touch[i]))
|
||||
{
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
}
|
||||
callback(1, state);
|
||||
Sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __stdcall mai2_io_touch_2p_thread_proc(void *ctx)
|
||||
{
|
||||
mai2_io_touch_callback_t callback = ctx;
|
||||
|
||||
while (!mai2_io_touch_2p_stop_flag)
|
||||
{
|
||||
uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 34; i++)
|
||||
{
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_touch[i]))
|
||||
{
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
}
|
||||
callback(2, state);
|
||||
Sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
HRESULT mai2_io_led_init(void)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void mai2_io_led_set_fet_output(const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
dprintf("mai2 LED: BodyLed brightness: %d%%\n", (rgb[0] * 100) / 255);
|
||||
dprintf("mai2 LED: ExtLed brightness: %d%%\n", (rgb[1] * 100) / 255);
|
||||
dprintf("mai2 LED: SideLed brightness: %d%%\n", (rgb[2] * 100) / 255);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void mai2_io_led_dc_update(const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
for (int i = 0; i < 10; i++) {
|
||||
dprintf("mai2 LED: LED %d: %02X %02X %02X Speed: %02X\n",
|
||||
i, rgb[i * 4], rgb[i * 4 + 1], rgb[i * 4 + 2], rgb[i * 4 + 3]);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void mai2_io_led_gs_update(const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
for (int i = 0; i < 8; i++) {
|
||||
dprintf("mai2 LED: LED %d: %02X %02X %02X Speed: %02X\n",
|
||||
i, rgb[i * 4], rgb[i * 4 + 1], rgb[i * 4 + 2], rgb[i * 4 + 3]);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
118
mai2io/mai2io.h
118
mai2io/mai2io.h
@ -3,6 +3,7 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum {
|
||||
MAI2_IO_OPBTN_TEST = 0x01,
|
||||
@ -66,3 +67,120 @@ void mai2_io_get_opbtns(uint8_t *opbtn);
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void mai2_io_get_gamebtns(uint16_t *player1, uint16_t *player2);
|
||||
|
||||
/* Callback function used by mai2_io_touch_1p/2p_thread_proc.
|
||||
|
||||
The 'player'(1 or 2) parameter indicates which player the touch data is for.
|
||||
|
||||
The 'state' represents a complete response packet.
|
||||
The format of the state array is as follows:
|
||||
uint8_t state[7] = {
|
||||
bytes[0] - bit(0 , 0 , 0 , A5, A4, A3, A2, A1)
|
||||
bytes[1] - bit(0 , 0 , 0 , B2, B1, A8, A7, A6)
|
||||
bytes[2] - bit(0 , 0 , 0 , B7, B6, B5, B4, B3)
|
||||
bytes[3] - bit(0 , 0 , 0 , D2, D1, C2, C1, B8)
|
||||
bytes[4] - bit(0 , 0 , 0 , D7, D6, D5, D4, D3)
|
||||
bytes[5] - bit(0 , 0 , 0 , E4, E3, E2, E1, D8)
|
||||
bytes[6] - bit(0 , 0 , 0 , 0 , E8, E7, E6, E5)
|
||||
}
|
||||
The 7 bytes are the touch data, with each byte storing the touch state in the lower 5 bits.
|
||||
A value of 1 indicates that the corresponding touch area is pressed.
|
||||
The touch areas are ordered from A1 to E8, and the binary values are stored from low to high. */
|
||||
|
||||
typedef void (*mai2_io_touch_callback_t)(const uint8_t player, const uint8_t state[7]);
|
||||
|
||||
/**
|
||||
* @brief Initializes the touch input callback function
|
||||
*
|
||||
* This function accepts a callback function as a parameter and stores it in the global variable `_callback`
|
||||
* for later handling of touch input events.
|
||||
*
|
||||
* @param callback The touch input callback function that takes two parameters: player number and the touch state array.
|
||||
* @return HRESULT Returns the result of the operation, S_OK on success.
|
||||
*/
|
||||
|
||||
HRESULT mai2_io_touch_init(mai2_io_touch_callback_t callback);
|
||||
|
||||
/* Send sensitivity setting data to the touch device.
|
||||
Format:
|
||||
bytes[0] - Header
|
||||
bytes[1] - Target device, ASCII characters 'L' or 'R'
|
||||
bytes[2] - Target touch point
|
||||
bytes[3] - commandRatio identifier
|
||||
bytes[4] - Ratio value to be set, within a fixed range
|
||||
bytes[5] - Footer
|
||||
|
||||
Example function, not actually used. The sensitivity range can be determined
|
||||
based on the Ratio set within the game. */
|
||||
|
||||
void mai2_io_touch_set_sens(uint8_t *bytes);
|
||||
|
||||
/**
|
||||
* @brief Updates the touch input acceptance state
|
||||
*
|
||||
* This function determines whether the game is ready to accept touch input based on the states of player 1 and player 2.
|
||||
* If the game is ready, it creates or stops the corresponding threads to handle touch data for each player.
|
||||
* Whether or not threads are created for each player is controlled by `mai2_io_cfg.debug_input_1p` and `mai2_io_cfg.debug_input_2p` configuration.
|
||||
*
|
||||
* @param player1 If `true`, indicates the game is ready to accept touch data from player 1, `false` means the game is not ready.
|
||||
* @param player2 If `true`, indicates the game is ready to accept touch data from player 2, `false` means the game is not ready.
|
||||
*/
|
||||
|
||||
void mai2_io_touch_update(bool player1, bool player2);
|
||||
|
||||
/**
|
||||
* @brief Player touch input handling thread
|
||||
*
|
||||
* This function runs in a separate thread, continuously monitoring player touch status and passing the state data
|
||||
* to the main thread via a callback function. Each time a touch input is detected, it updates the `state` array and calls the callback.
|
||||
* The thread stops when `mai2_io_touch_1p/2p_stop_flag` is `true`.
|
||||
*
|
||||
* @param ctx The callback function context, of type `mai2_io_touch_callback_t`, used to handle the touch input events.
|
||||
* @return The thread's return value, typically `0`.
|
||||
*/
|
||||
|
||||
static unsigned int __stdcall mai2_io_touch_1p_thread_proc(void *ctx);
|
||||
|
||||
static unsigned int __stdcall mai2_io_touch_2p_thread_proc(void *ctx);
|
||||
|
||||
/* Initialize LED emulation. This function will be called before any
|
||||
other mai2_io_led_*() function calls.
|
||||
|
||||
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: 0x0101 */
|
||||
|
||||
HRESULT mai2_io_led_init(void);
|
||||
|
||||
/* Update the FET outputs. rgb is a pointer to an array up to 3 bytes.
|
||||
|
||||
Set the brightness of the white light on the machine's outer shell.
|
||||
The program will continuously send changed values to request the blinking effect.
|
||||
|
||||
[0]: BodyLed
|
||||
[1]: ExtLed
|
||||
[2]: SideLed
|
||||
|
||||
The LED is truned on when the byte is 255 and turned off when the byte is 0.
|
||||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void mai2_io_led_set_fet_output(const uint8_t *rgb);
|
||||
|
||||
/* The effect of this command is unknown, it is triggered after LED_15070_CMD_EEPROM_READ. */
|
||||
|
||||
void mai2_io_led_dc_update(const uint8_t *rgb);
|
||||
|
||||
/* Update the RGB LEDs. rgb is a pointer to an array up to 32 * 4 = 128 bytes.
|
||||
|
||||
The LEDs are laid out as follows:
|
||||
[0-7]: 8 button LED
|
||||
|
||||
Each rgb value is comprised for 4 bytes in the order of R, G, B, Speed.
|
||||
Speed is a value from 0 to 255, where 0 is the fastest speed and 255 is the slowest.
|
||||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void mai2_io_led_gs_update(const uint8_t *rgb);
|
||||
|
@ -3,6 +3,9 @@ mai2io_lib = static_library(
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
],
|
||||
sources : [
|
||||
'mai2io.c',
|
||||
'mai2io.h',
|
||||
|
@ -46,6 +46,9 @@ endif
|
||||
if get_option('log_all') or get_option('log_io3')
|
||||
add_project_arguments('-DLOG_IO3', language: 'c')
|
||||
endif
|
||||
if get_option('log_all') or get_option('log_led15070')
|
||||
add_project_arguments('-DLOG_LED15070', language: 'c')
|
||||
endif
|
||||
if get_option('log_all') or get_option('log_led15093')
|
||||
add_project_arguments('-DLOG_LED15093', language: 'c')
|
||||
endif
|
||||
@ -61,6 +64,9 @@ endif
|
||||
if get_option('log_all') or get_option('log_carol_touch')
|
||||
add_project_arguments('-DLOG_CAROL_TOUCH', language: 'c')
|
||||
endif
|
||||
if get_option('log_all') or get_option('log_mai2_touch')
|
||||
add_project_arguments('-DLOG_MAI2_TOUCH', language: 'c')
|
||||
endif
|
||||
if get_option('log_all') or get_option('log_chuni_slider')
|
||||
add_project_arguments('-DLOG_CHUNI_SLIDER', language: 'c')
|
||||
endif
|
||||
|
@ -13,6 +13,11 @@ option('log_io3',
|
||||
value : false,
|
||||
description : 'Enable debug logging for JVS'
|
||||
)
|
||||
option('log_led15070',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description : 'Enable debug logging for the 15070 LED board emulation'
|
||||
)
|
||||
option('log_led15093',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
@ -38,6 +43,11 @@ option('log_carol_touch',
|
||||
value : false,
|
||||
description : 'Enable debug logging for the Carlo Touchscreen'
|
||||
)
|
||||
option('log_mai2_touch',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description : 'Enable debug logging for the mai2 TouchPanel'
|
||||
)
|
||||
option('log_chuni_slider',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
|
@ -189,7 +189,7 @@ HRESULT epay_hook_init(const struct epay_config *cfg) {
|
||||
thinca_stub->impl1->unk220 = thinca_unk;
|
||||
thinca_stub->impl1->unk228 = thinca_unk;
|
||||
|
||||
dprintf("Epay: Init\n");
|
||||
dprintf("Epay: Init.\n");
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
Reference in New Issue
Block a user