From badae01357c76ad630721482eeec2b31de44b5fa Mon Sep 17 00:00:00 2001 From: din <5580979+dinsfire64@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:34:25 -0600 Subject: [PATCH] bt5api fix (#552) ## Description of change use case: user with two ICCA readers in a daisy chain configuration (as is wired in og IIDX/DDR) wanted to play DDR using their existing equipment. Existing `reader/` implementation is only designed for a single reader per com port vs two readers on one daisy chained com port. This can be implemented and improved at a future date. Due to time constraints `eamio-icca.dll` from btools did support both wavepass and slotted in a daisy chain configuration, and spice does support bt5api calls. However, the implementation was not sufficient to allow use of both modules correctly, often stalling, not did it allow the ability to eject the card. I have cleaned up the implementation such that it matches the btools `eamiotest` implementation and allows the user to use decimal to eject the card as desired. As well as properly staging the requests for state of the two individual readers, like `eamio-icca` expects. ## Testing Tested with both wavepass and slotted (ICCA and ICCB) modules and in combination with released `eamio-icca.dll` binaries from btools 5.49 as released by their github using `MDX` as a test subject. Works as expected and allows end users to use real readers (if they have them) as virtual readers in spice's emulation layer. --- src/spice2x/misc/bt5api.cpp | 72 ++++++++++++++++++++++++++----------- src/spice2x/misc/eamuse.cpp | 9 ++++- 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/spice2x/misc/bt5api.cpp b/src/spice2x/misc/bt5api.cpp index 6cc2b94..6ceb5ce 100644 --- a/src/spice2x/misc/bt5api.cpp +++ b/src/spice2x/misc/bt5api.cpp @@ -119,6 +119,7 @@ static std::string EAMIO_DLL_NAME = "eamio.dll"; static robin_hood::unordered_map BT5API_THREADS; static robin_hood::unordered_map BT5API_THREAD_RESULTS; static uint8_t BT5API_CARD_STATES[] = {0, 0}; +static uint16_t BT5API_KEYPAD_STATES[] = {0, 0}; static void bt5api_log(const char *bt5_module, const char *fmt, ...) { @@ -240,46 +241,77 @@ void bt5api_hook(HINSTANCE module) { void bt5api_poll_reader_card(uint8_t unit_no) { - // check if initialized - if (!EAMIO_DLL) { + // check if initialized or out of bounds + if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) { return; } // poll if (!eam_io_poll(unit_no)) { - log_warning("bt5api", "polling bt5api reader {} returned failure", + log_warning("bt5api", "polling bt5api reader card {} returned failure", static_cast(unit_no)); + return; } // get sensor state uint8_t sensor_state = eam_io_get_sensor_state(unit_no); + + // if we have a change in state. + if(sensor_state != BT5API_CARD_STATES[unit_no]) { - // check for card in - if (sensor_state > BT5API_CARD_STATES[unit_no]) { - uint8_t card_id[8]; - eam_io_read_card(unit_no, card_id, 8); - eamuse_card_insert(unit_no, card_id); + // card inserted into the back. + if(sensor_state & (1 << EAM_IO_SENSOR_BACK)) { + uint8_t card_id[8]; + + log_info("bt5api", "card unit {} inserted", static_cast(unit_no)); + + // do the bt5 dance + eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_CLOSE); + eam_io_poll(unit_no); + eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_READ); + eam_io_poll(unit_no); + + // ask the api for the card, then insert it into our engine. + eam_io_read_card(unit_no, card_id, 8); + eamuse_card_insert(unit_no, card_id); + } + + // if card removed entirely, do another bt5 dance! + if(sensor_state == 0) { + eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_CLOSE); + eam_io_poll(unit_no); + eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_OPEN); + + log_info("bt5api", "card unit {} removed", static_cast(unit_no)); + } } - - // save state + + // save prev state BT5API_CARD_STATES[unit_no] = sensor_state; } - + void bt5api_poll_reader_keypad(uint8_t unit_no) { - // check if initialized - if (!EAMIO_DLL) { + // check if initialized or out of bounds + if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) { return; } - - // poll - if (!eam_io_poll(unit_no)) { - log_warning("bt5api", "polling bt5api reader {} returned failure", - static_cast(unit_no)); + + uint16_t keypad_current = eam_io_get_keypad_state(unit_no); + uint16_t keypad_changes = ~BT5API_KEYPAD_STATES[unit_no] & keypad_current; + + // if user has pressed decimal for the first time, eject the slotted reader. + if(keypad_changes & (1 << EAM_IO_KEYPAD_DECIMAL)) { + log_info("bt5api", "card unit {} ejecting", static_cast(unit_no)); + + eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_EJECT); } + + // save the last pressed. + BT5API_KEYPAD_STATES[unit_no] = keypad_current; - // get keypad - eamuse_set_keypad_overrides_bt5(unit_no, eam_io_get_keypad_state(unit_no)); + // send off the state to the main engine. + eamuse_set_keypad_overrides_bt5(unit_no, keypad_current); } void bt5api_dispose() { diff --git a/src/spice2x/misc/eamuse.cpp b/src/spice2x/misc/eamuse.cpp index facb422..933387e 100644 --- a/src/spice2x/misc/eamuse.cpp +++ b/src/spice2x/misc/eamuse.cpp @@ -202,7 +202,14 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) { // bt5api if (BT5API_ENABLED) { - bt5api_poll_reader_card((uint8_t) index); + // some bt5api want to be polled in order. + if (eamuse_get_game_keypads() > 1) { + bt5api_poll_reader_card(1); + bt5api_poll_reader_card(0); + } + else { + bt5api_poll_reader_card((uint8_t) index); + } } // auto insert card