#include "bi2x_hook.h" #if SPICE64 #include "util/execexe.h" #include "util/logging.h" #include "rawinput/rawinput.h" #include "misc/eamuse.h" #include "games/io.h" #include "io.h" #include "util/tapeled.h" #include "pc.h" #include "util/utils.h" #include "util/time.h" #include "util/socd_cleaner.h" namespace games::pc { constexpr float RELATIVE_DECAY = 0.02f; struct FADER_RELATIVE { // ranges from 0.f to 1.f float previous_raw = 0.5f; // ranges from -1.f to +1.f float effective_value = 0.f; }; FADER_RELATIVE fader_l_relative; FADER_RELATIVE fader_r_relative; float calculate_analog_raw_delta(float old_value, float new_value) { // assumed that values are between 0.f and 1.f float delta = 0.f; if ((old_value < 0.25f) && (0.75f < new_value)) { // wrapped around: going left delta = -((1.f - new_value) + old_value); } else if ((new_value < 0.25f) && (0.75f < old_value)) { // wrapped around: going right delta = (1.f - old_value) + new_value; } else { delta = new_value - old_value; } return delta; } float apply_decay(float val) { // return to center over time if (RELATIVE_DECAY < val) { return val - RELATIVE_DECAY; } else if (val < -RELATIVE_DECAY) { return val + RELATIVE_DECAY; } else { return 0.f; } } float get_fader_knob_mode_value(float raw_analog, FADER_RELATIVE &fader) { // add new value... const float delta = calculate_analog_raw_delta(fader.previous_raw, raw_analog); fader.effective_value += (delta * 20.f); fader.previous_raw = raw_analog; // capture the result after adding new input value fader.effective_value = CLAMP(fader.effective_value, -1.f, 1.f); const float result = fader.effective_value; // post-process for next iteration: apply decay (return to center over time) fader.effective_value = apply_decay(fader.effective_value); return result; } /* * class definitions */ struct AIO_SCI_COMM { uint8_t data[0xf8]; }; struct AIO_NMGR_IOB2 { uint8_t data[0xe30]; }; struct AIO_IOB2_BI2X_AC1 { uint8_t data[0x4538]; }; struct AIO_IOB2_BI2X_AC1__SETTING { }; struct AIO_IOB2_BI2X_WRFIRM { uint8_t data[0x20f48]; }; struct AIO_IOB2_BI2X_AC1__INPUT { uint8_t DevIoCounter; uint8_t bExIoAErr; uint8_t bExIoBErr; uint8_t bPcPowerOn; uint8_t bPcPowerCheck; uint8_t bCoin1Jam; uint8_t bCoin2Jam; uint8_t bCoin3Jam; uint8_t bCoin4Jam; uint8_t Coin1Count; uint8_t Coin2Count; uint8_t Coin3Count; uint8_t Coin4Count; uint16_t AnalogCh1; uint16_t AnalogCh2; uint16_t AnalogCh3; uint16_t AnalogCh4; uint8_t CN8_8; uint8_t CN8_9; uint8_t CN8_10; uint8_t CN9_8; uint8_t CN9_9; uint8_t CN9_10; uint8_t CN11_11; uint8_t CN11_12; uint8_t CN11_13; uint8_t CN11_14; uint8_t CN11_15; uint8_t CN11_16; uint8_t CN11_17; uint8_t CN11_18; uint8_t CN11_19; uint8_t CN11_20; uint8_t CN12_11; uint8_t CN12_12; uint8_t CN12_13; uint8_t CN12_14; uint8_t CN12_15; uint8_t CN12_16; uint8_t CN12_17; uint8_t CN12_18; uint8_t CN12_19; uint8_t CN12_20; uint8_t CN12_21; uint8_t CN12_22; uint8_t CN12_23; uint8_t CN12_24; uint8_t CN15_3; uint8_t CN15_4; uint8_t CN15_5; uint8_t CN15_6; uint8_t CN15_7; uint8_t CN15_8; uint8_t CN15_9; uint8_t CN15_10; uint8_t CN15_11; uint8_t CN15_12; uint8_t CN15_13; uint8_t CN15_14; uint8_t CN15_15; uint8_t CN15_16; uint8_t CN15_17; uint8_t CN15_18; uint8_t CN15_19; uint8_t CN15_20; uint8_t CN19_8; uint8_t CN19_9; uint8_t CN19_10; uint8_t CN19_11; uint8_t CN19_12; uint8_t CN19_13; uint8_t CN19_14; uint8_t CN19_15; }; struct AIO_IOB2_BI2X_AC1__INPUTDATA { uint8_t data[247]; }; struct AIO_IOB2_BI2X_AC1__OUTPUTDATA { uint8_t data[48]; }; struct AIO_IOB2_BI2X_AC1__ICNPIN { uint16_t Ain[4]; uint64_t CnPin; }; struct AIO_IOB2_BI2X_AC1__DEVSTATUS { uint8_t InputCounter; uint8_t OutputCounter; uint8_t IoResetCounter; uint8_t TapeLedCounter; uint8_t TapeLedRate[8]; AIO_IOB2_BI2X_AC1__INPUT Input; AIO_IOB2_BI2X_AC1__INPUTDATA InputData; AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData; AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20]; }; // verified with XIF-2024122300 static_assert(sizeof(AIO_SCI_COMM) == 0xf8); static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30); static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4538); static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48); /* * typedefs */ // libaio-iob2_video.dll typedef AIO_IOB2_BI2X_AC1* (__fastcall *aioIob2Bi2xAC1_Create_t)(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId, AIO_IOB2_BI2X_AC1__SETTING *i_Setting); typedef void (__fastcall *aioIob2Bi2xAC1_GetDeviceStatus_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus); typedef void (__fastcall *aioIob2Bi2xAC1_SetWatchDogTimer_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count); typedef void (__fastcall *aioIob2Bi2xAC1_AddCounter_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count); typedef void (__fastcall *aioIob2Bi2xAC1_SetOutputData_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data); typedef void (__fastcall *aioIob2Bi2xAC1_SetTapeLedDataPart_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh, uint32_t i_Offset, uint8_t *i_pData, uint32_t i_cntTapeLed, bool i_bReverse); typedef void (__fastcall *aioIob2Bi2x_SetTapeLedDataGroup_t)(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_bfGroup); typedef void (__fastcall *aioIob2Bi2x_SetTapeLedDataLimit_t)(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_Channel, uint8_t i_Scale, uint8_t i_Limit); typedef AIO_IOB2_BI2X_WRFIRM *(__fastcall *aioIob2Bi2x_CreateWriteFirmContext_t)(uint32_t i_SerialNumber, uint32_t i_bfIob); typedef AIO_SCI_COMM* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint32_t i_SerialNumber); typedef void (__fastcall *aioIob2Bi2x_DestroyWriteFirmContext_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm); typedef int32_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm); typedef bool (__fastcall *aioIob2Bi2x_WriteFirmIsCompleted_t)(int32_t i_State); typedef bool (__fastcall *aioIob2Bi2x_WriteFirmIsError_t)(int32_t i_State); // libaio-iob.dll typedef AIO_NMGR_IOB2 *(__fastcall *aioNMgrIob2_Create_t)(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode); typedef void (__fastcall *aioNMgrIob_BeginManage_t)(AIO_NMGR_IOB2 *i_pNodeMgr); // libaio.dll typedef void (__fastcall *aioSci_Destroy_t)(AIO_SCI_COMM *i_pNodeMgr); typedef void (__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB2 *i_pNodeMgr); typedef int32_t (__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB2 *i_pNodeMgr); typedef bool (__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State); typedef bool (__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State); typedef void (__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl); typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl); typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State); typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State); /* * function pointers */ // libaio-iob2_video.dll static aioIob2Bi2xAC1_Create_t aioIob2Bi2xAC1_Create_orig = nullptr; static aioIob2Bi2xAC1_GetDeviceStatus_t aioIob2Bi2xAC1_GetDeviceStatus_orig = nullptr; static aioIob2Bi2xAC1_SetWatchDogTimer_t aioIob2Bi2xAC1_SetWatchDogTimer_orig = nullptr; static aioIob2Bi2xAC1_AddCounter_t aioIob2Bi2xAC1_AddCounter_orig = nullptr; static aioIob2Bi2xAC1_SetOutputData_t aioIob2Bi2xAC1_SetOutputData_orig = nullptr; static aioIob2Bi2xAC1_SetTapeLedDataPart_t aioIob2Bi2xAC1_SetTapeLedDataPart_orig = nullptr; static aioIob2Bi2x_SetTapeLedDataGroup_t aioIob2Bi2x_SetTapeLedDataGroup_orig = nullptr; static aioIob2Bi2x_SetTapeLedDataLimit_t aioIob2Bi2x_SetTapeLedDataLimit_orig = nullptr; static aioIob2Bi2x_OpenSciUsbCdc_t aioIob2Bi2x_OpenSciUsbCdc_orig = nullptr; static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr; static aioIob2Bi2x_DestroyWriteFirmContext_t aioIob2Bi2x_DestroyWriteFirmContext_orig = nullptr; static aioIob2Bi2x_WriteFirmGetState_t aioIob2Bi2x_WriteFirmGetState_orig = nullptr; static aioIob2Bi2x_WriteFirmIsCompleted_t aioIob2Bi2x_WriteFirmIsCompleted_orig = nullptr; static aioIob2Bi2x_WriteFirmIsError_t aioIob2Bi2x_WriteFirmIsError_orig = nullptr; // libaio-iob.dll static aioNMgrIob2_Create_t aioNMgrIob2_Create_orig = nullptr; static aioNMgrIob_BeginManage_t aioNMgrIob_BeginManage_orig = nullptr; // libaio.dll static aioSci_Destroy_t aioSci_Destroy_orig = nullptr; static aioNodeMgr_Destroy_t aioNodeMgr_Destroy_orig = nullptr; static aioNodeMgr_GetState_t aioNodeMgr_GetState_orig = nullptr; static aioNodeMgr_IsReady_t aioNodeMgr_IsReady_orig = nullptr; static aioNodeMgr_IsError_t aioNodeMgr_IsError_orig = nullptr; static aioNodeCtl_Destroy_t aioNodeCtl_Destroy_orig = nullptr; static aioNodeCtl_GetState_t aioNodeCtl_GetState_orig = nullptr; static aioNodeCtl_IsReady_t aioNodeCtl_IsReady_orig = nullptr; static aioNodeCtl_IsError_t aioNodeCtl_IsError_orig = nullptr; /* * variables */ static AIO_IOB2_BI2X_AC1 *aioIob2Bi2xAc1; static AIO_SCI_COMM *aioSciComm; static AIO_NMGR_IOB2 *aioNmgrIob2; static AIO_IOB2_BI2X_WRFIRM *aioIob2Bi2xWrfirm; /* * implementations */ // libaio-iob2_video.dll static AIO_IOB2_BI2X_AC1* __fastcall aioIob2Bi2xAC1_Create(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId, AIO_IOB2_BI2X_AC1__SETTING *i_Setting) { if (i_pNodeMgr == aioNmgrIob2) { log_info("bi2x_hook", "aioIob2Bi2xAC1_Create hook hit"); aioIob2Bi2xAc1 = new AIO_IOB2_BI2X_AC1; return aioIob2Bi2xAc1; } else { return aioIob2Bi2xAC1_Create_orig(i_pNodeMgr, i_DevId, i_Setting); } } void __fastcall aioIob2Bi2xAC1_GetDeviceStatus(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus) { RI_MGR->devices_flush_output(); if (i_pNodeCtl != aioIob2Bi2xAc1) { return aioIob2Bi2xAC1_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus); } memset(o_DevStatus, 0x00, sizeof(AIO_IOB2_BI2X_AC1__DEVSTATUS)); auto &buttons = get_buttons(); o_DevStatus->Input.CN8_8 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]) ? 0 : 0xFF; o_DevStatus->Input.CN8_9 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]) ? 0 : 0xFF; o_DevStatus->Input.CN8_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]) ? 0 : 0xFF; o_DevStatus->Input.CN15_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]) ? 0 : 0xFF; o_DevStatus->Input.CN15_12 = 0xFF; // Recorder off; present in I/O test menu but unused in game o_DevStatus->Input.CN12_11 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button1]) ? 0 : 0xFF; o_DevStatus->Input.CN12_12 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button2]) ? 0 : 0xFF; o_DevStatus->Input.CN12_13 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button3]) ? 0 : 0xFF; o_DevStatus->Input.CN12_14 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button4]) ? 0 : 0xFF; o_DevStatus->Input.CN12_15 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button5]) ? 0 : 0xFF; o_DevStatus->Input.CN12_16 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button6]) ? 0 : 0xFF; o_DevStatus->Input.CN12_17 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button7]) ? 0 : 0xFF; o_DevStatus->Input.CN12_18 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button8]) ? 0 : 0xFF; o_DevStatus->Input.CN12_19 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button9]) ? 0 : 0xFF; o_DevStatus->Input.CN12_20 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button10]) ? 0 : 0xFF; o_DevStatus->Input.CN12_21 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button11]) ? 0 : 0xFF; o_DevStatus->Input.CN12_22 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button12]) ? 0 : 0xFF; auto &analogs = get_analogs(); // FADER-L float val = 0.f; if (analogs[Analogs::FaderL].isSet()) { if (PC_KNOB_MODE) { val = get_fader_knob_mode_value( GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]), fader_l_relative); } else { val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2; } } const auto now = get_performance_milliseconds(); const auto fader_l_socd = socd::socd_clean( 0, // Fader-L GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]), GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right]), now); if (fader_l_socd == socd::SocdCCW) { val = -1.0f; } else if (fader_l_socd == socd::SocdCW) { val = +1.0f; } o_DevStatus->Input.CN15_7 = (val < 0.2f) ? 0xFF : 0; o_DevStatus->Input.CN15_6 = (val > -0.85f && val < 0.35f) ? 0xFF : 0; o_DevStatus->Input.CN15_5 = (val > -0.6f && val < 0.6f) ? 0xFF : 0; o_DevStatus->Input.CN15_4 = (val > -0.35f && val < 0.85f) ? 0xFF : 0; o_DevStatus->Input.CN15_3 = (val > -0.2f) ? 0xFF : 0; // FADER-R val = 0.f; if (analogs[Analogs::FaderR].isSet()) { if (PC_KNOB_MODE) { val = get_fader_knob_mode_value( GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]), fader_r_relative); } else { val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2; } } const auto fader_r_socd = socd::socd_clean( 1, // Fader-R GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]), GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right]), now); if (fader_r_socd == socd::SocdCCW) { val = -1.0f; } else if (fader_r_socd == socd::SocdCW) { val = +1.0f; } o_DevStatus->Input.CN11_20 = (val < 0.2f) ? 0xFF : 0; o_DevStatus->Input.CN11_19 = (val > -0.85f && val < 0.35f) ? 0xFF : 0; o_DevStatus->Input.CN9_10 = (val > -0.6f && val < 0.6f) ? 0xFF : 0; o_DevStatus->Input.CN9_9 = (val > -0.35f && val < 0.85f) ? 0xFF : 0; o_DevStatus->Input.CN9_8 = (val > -0.2f) ? 0xFF : 0; // coin o_DevStatus->Input.Coin1Count = eamuse_coin_get_stock(); } void __fastcall aioIob2Bi2xAC1_SetWatchDogTimer(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count) { if (i_pNodeCtl != aioIob2Bi2xAc1) { return aioIob2Bi2xAC1_SetWatchDogTimer_orig(i_pNodeCtl, i_Count); } } void __fastcall aioIob2Bi2xAC1_AddCounter(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) { if (i_pNodeCtl == aioIob2Bi2xAc1 && i_Count == 0) { eamuse_coin_set_stock((uint16_t) i_Count); } else { return aioIob2Bi2xAC1_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count); } } void __fastcall aioIob2Bi2xAC1_SetOutputData(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data) { if (i_pNodeCtl != aioIob2Bi2xAc1) { return aioIob2Bi2xAC1_SetOutputData_orig(i_pNodeCtl, i_CnPin, i_Data); } if (i_CnPin == 0x1) { eamuse_coin_set_block(i_Data == 0xFF); return; } auto &lights = get_lights(); if (i_CnPin == 0x11) { GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_R), i_Data / 255.0f); } else if (i_CnPin == 0x12) { GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_G), i_Data / 255.0f); } else if (i_CnPin == 0x13) { GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_B), i_Data / 255.0f); } } struct PolarisChordLight { int data_index; games::pc::Lights::pc_lights_t light; PolarisChordLight( int data_index, games::pc::Lights::pc_lights_t light) : data_index(data_index), light(light) {} }; static void set_led_value(games::pc::Lights::pc_lights_t light, uint8_t value) { auto &lights = games::pc::get_lights(); GameAPI::Lights::writeLight(RI_MGR, lights.at(light), value / 255.f); } void __fastcall aioIob2Bi2xAC1_SetTapeLedDataPart(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh, uint32_t i_Offset, uint8_t *i_pData, uint32_t i_cntTapeLed, bool i_bReverse) { if (i_pNodeCtl != aioIob2Bi2xAc1) { return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse); } // log_info( // "pc", // "lamp [{}] [{}] = {},{},{}, cnt={}", // i_TapeLedCh, // i_Offset, // i_pData[0], i_pData[1], i_pData[2], // i_cntTapeLed); // [channel 0] // these are button lamps; there are 12 buttons (columns) with this layout: // // 0 5 8 11 14 16 | 18 20 22 25 28 31 // 1 6 9 12 15 17 | 19 21 23 26 29 32 // 2 7 10 13 | 24 27 30 33 // 3 | 34 // 4 | 35 // // 36 RGB lamps * 3 = 108 LEDs // each value ranges from [0, 255] // (we only care about the top row) static PolarisChordLight button_lights_mapping[] = { {(0 * 3) + 0, games::pc::Lights::pc_lights_t::Button1_R}, {(0 * 3) + 1, games::pc::Lights::pc_lights_t::Button1_G}, {(0 * 3) + 2, games::pc::Lights::pc_lights_t::Button1_B}, {(5 * 3) + 0, games::pc::Lights::pc_lights_t::Button2_R}, {(5 * 3) + 1, games::pc::Lights::pc_lights_t::Button2_G}, {(5 * 3) + 2, games::pc::Lights::pc_lights_t::Button2_B}, {(8 * 3) + 0, games::pc::Lights::pc_lights_t::Button3_R}, {(8 * 3) + 1, games::pc::Lights::pc_lights_t::Button3_G}, {(8 * 3) + 2, games::pc::Lights::pc_lights_t::Button3_B}, {(11 * 3) + 0, games::pc::Lights::pc_lights_t::Button4_R}, {(11 * 3) + 1, games::pc::Lights::pc_lights_t::Button4_G}, {(11 * 3) + 2, games::pc::Lights::pc_lights_t::Button4_B}, {(14 * 3) + 0, games::pc::Lights::pc_lights_t::Button5_R}, {(14 * 3) + 1, games::pc::Lights::pc_lights_t::Button5_G}, {(14 * 3) + 2, games::pc::Lights::pc_lights_t::Button5_B}, {(16 * 3) + 0, games::pc::Lights::pc_lights_t::Button6_R}, {(16 * 3) + 1, games::pc::Lights::pc_lights_t::Button6_G}, {(16 * 3) + 2, games::pc::Lights::pc_lights_t::Button6_B}, {(18 * 3) + 0, games::pc::Lights::pc_lights_t::Button7_R}, {(18 * 3) + 1, games::pc::Lights::pc_lights_t::Button7_G}, {(18 * 3) + 2, games::pc::Lights::pc_lights_t::Button7_B}, {(20 * 3) + 0, games::pc::Lights::pc_lights_t::Button8_R}, {(20 * 3) + 1, games::pc::Lights::pc_lights_t::Button8_G}, {(20 * 3) + 2, games::pc::Lights::pc_lights_t::Button8_B}, {(22 * 3) + 0, games::pc::Lights::pc_lights_t::Button9_R}, {(22 * 3) + 1, games::pc::Lights::pc_lights_t::Button9_G}, {(22 * 3) + 2, games::pc::Lights::pc_lights_t::Button9_B}, {(25 * 3) + 0, games::pc::Lights::pc_lights_t::Button10_R}, {(25 * 3) + 1, games::pc::Lights::pc_lights_t::Button10_G}, {(25 * 3) + 2, games::pc::Lights::pc_lights_t::Button10_B}, {(28 * 3) + 0, games::pc::Lights::pc_lights_t::Button11_R}, {(28 * 3) + 1, games::pc::Lights::pc_lights_t::Button11_G}, {(28 * 3) + 2, games::pc::Lights::pc_lights_t::Button11_B}, {(31 * 3) + 0, games::pc::Lights::pc_lights_t::Button12_R}, {(31 * 3) + 1, games::pc::Lights::pc_lights_t::Button12_G}, {(31 * 3) + 2, games::pc::Lights::pc_lights_t::Button12_B}, }; if (i_TapeLedCh == 0 && i_Offset == 0 && i_cntTapeLed == 108) { for (const auto &map : button_lights_mapping) { set_led_value(map.light, i_pData[map.data_index]); } } } void __fastcall aioIob2Bi2x_SetTapeLedDataGroup(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_bfGroup) { if (i_pNodeCtl != aioIob2Bi2xAc1) { return aioIob2Bi2x_SetTapeLedDataGroup_orig(i_pNodeCtl, i_bfGroup); } } void __fastcall aioIob2Bi2x_SetTapeLedDataLimit(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_Channel, uint8_t i_Scale, uint8_t i_Limit) { if (i_pNodeCtl != aioIob2Bi2xAc1) { return aioIob2Bi2x_SetTapeLedDataLimit_orig(i_pNodeCtl, i_Channel, i_Scale, i_Limit); } } static AIO_SCI_COMM *__fastcall aioIob2Bi2x_OpenSciUsbCdc(uint32_t i_SerialNumber) { aioSciComm = new AIO_SCI_COMM; return aioSciComm; } static AIO_IOB2_BI2X_WRFIRM *__fastcall aioIob2Bi2x_CreateWriteFirmContext( uint32_t i_SerialNumber, uint32_t i_bfIob) { aioIob2Bi2xWrfirm = new AIO_IOB2_BI2X_WRFIRM; return aioIob2Bi2xWrfirm; } static void __fastcall aioIob2Bi2x_DestroyWriteFirmContext(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) { if (i_pWrFirm == aioIob2Bi2xWrfirm) { delete aioIob2Bi2xWrfirm; aioIob2Bi2xWrfirm = nullptr; } else { return aioIob2Bi2x_DestroyWriteFirmContext_orig(i_pWrFirm); } } static int32_t __fastcall aioIob2Bi2x_WriteFirmGetState(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) { if (i_pWrFirm == aioIob2Bi2xWrfirm) { return 8; } else { return aioIob2Bi2x_WriteFirmGetState_orig(i_pWrFirm); } } static bool __fastcall aioIob2Bi2x_WriteFirmIsCompleted(int32_t i_State) { if (aioIob2Bi2xWrfirm != nullptr) { return true; } else { return aioIob2Bi2x_WriteFirmIsCompleted_orig(i_State); } } static bool __fastcall aioIob2Bi2x_WriteFirmIsError(int32_t i_State) { if (aioIob2Bi2xWrfirm != nullptr) { return false; } else { return aioIob2Bi2x_WriteFirmIsError_orig(i_State); } } // libaio-iob.dll static AIO_NMGR_IOB2 *__fastcall aioNMgrIob2_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) { if (i_pSci == aioSciComm) { aioNmgrIob2 = new AIO_NMGR_IOB2; return aioNmgrIob2; } else { return aioNMgrIob2_Create_orig(i_pSci, i_bfMode); } } static void __fastcall aioNMgrIob_BeginManage(AIO_NMGR_IOB2 *i_pNodeMgr) { if (i_pNodeMgr != aioNmgrIob2) { return aioNMgrIob_BeginManage_orig(i_pNodeMgr); } } // libaio.dll static void __fastcall aioSci_Destroy(AIO_SCI_COMM *i_pNodeMgr) { if (i_pNodeMgr == aioSciComm) { delete aioSciComm; } else { return aioSci_Destroy_orig(i_pNodeMgr); } } static void __fastcall aioNodeMgr_Destroy(AIO_NMGR_IOB2 *i_pNodeMgr) { if (i_pNodeMgr == aioNmgrIob2) { delete aioNmgrIob2; aioNmgrIob2 = nullptr; } else { return aioNodeMgr_Destroy_orig(i_pNodeMgr); } } static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB2 *i_pNodeMgr) { if (i_pNodeMgr == aioNmgrIob2) { return 1; } else { return aioNodeMgr_GetState_orig(i_pNodeMgr); } } static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State) { if (i_pNodeMgr == aioNmgrIob2) { return true; } else { return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State); } } static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State) { if (i_pNodeMgr == aioNmgrIob2) { return false; } else { return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State); } } static void __fastcall aioNodeCtl_Destroy(AIO_IOB2_BI2X_AC1 *i_pNodeCtl) { if (i_pNodeCtl == aioIob2Bi2xAc1) { delete aioIob2Bi2xAc1; aioIob2Bi2xAc1 = nullptr; } else { return aioNodeCtl_Destroy_orig(i_pNodeCtl); } } static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB2_BI2X_AC1 *i_pNodeCtl) { if (i_pNodeCtl == aioIob2Bi2xAc1) { return 1; } else { return aioNodeCtl_GetState_orig(i_pNodeCtl); } } static bool __fastcall aioNodeCtl_IsReady(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State) { if (i_pNodeCtl == aioIob2Bi2xAc1) { return true; } else { return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State); } } static bool __fastcall aioNodeCtl_IsError(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State) { if (i_pNodeCtl == aioIob2Bi2xAc1) { return false; } else { return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State); } } void bi2x_hook_init() { // avoid double init static bool initialized = false; if (initialized) { return; } else { initialized = true; } // announce log_info("bi2x_hook", "init"); // libaio-iob2_video.dll const auto libaioIob2VideoDll = "libaio-iob2_video.dll"; execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_Create", aioIob2Bi2xAC1_Create, &aioIob2Bi2xAC1_Create_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_GetDeviceStatus", aioIob2Bi2xAC1_GetDeviceStatus, &aioIob2Bi2xAC1_GetDeviceStatus_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetWatchDogTimer", aioIob2Bi2xAC1_SetWatchDogTimer, &aioIob2Bi2xAC1_SetWatchDogTimer_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_AddCounter", aioIob2Bi2xAC1_AddCounter, &aioIob2Bi2xAC1_AddCounter_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetOutputData", aioIob2Bi2xAC1_SetOutputData, &aioIob2Bi2xAC1_SetOutputData_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetTapeLedDataPart", aioIob2Bi2xAC1_SetTapeLedDataPart, &aioIob2Bi2xAC1_SetTapeLedDataPart_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataGroup", aioIob2Bi2x_SetTapeLedDataGroup, &aioIob2Bi2x_SetTapeLedDataGroup_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataLimit", aioIob2Bi2x_SetTapeLedDataLimit, &aioIob2Bi2x_SetTapeLedDataLimit_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_OpenSciUsbCdc", aioIob2Bi2x_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_CreateWriteFirmContext", aioIob2Bi2x_CreateWriteFirmContext, &aioIob2Bi2x_CreateWriteFirmContext_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_DestroyWriteFirmContext", aioIob2Bi2x_DestroyWriteFirmContext, &aioIob2Bi2x_DestroyWriteFirmContext_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmGetState", aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsCompleted", aioIob2Bi2x_WriteFirmIsCompleted, &aioIob2Bi2x_WriteFirmIsCompleted_orig); execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsError", aioIob2Bi2x_WriteFirmIsError, &aioIob2Bi2x_WriteFirmIsError_orig); // libaio-iob.dll const auto libaioIobDll = "libaio-iob.dll"; execexe::trampoline_try(libaioIobDll, "aioNMgrIob2_Create", aioNMgrIob2_Create, &aioNMgrIob2_Create_orig); execexe::trampoline_try(libaioIobDll, "aioNMgrIob_BeginManage", aioNMgrIob_BeginManage, &aioNMgrIob_BeginManage_orig); // libaio.dll const auto libaioDll = "libaio.dll"; execexe::trampoline_try(libaioDll, "aioSci_Destroy", aioSci_Destroy, &aioSci_Destroy_orig); execexe::trampoline_try(libaioDll, "aioNodeMgr_Destroy", aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig); execexe::trampoline_try(libaioDll, "aioNodeMgr_GetState", aioNodeMgr_GetState, &aioNodeMgr_GetState_orig); execexe::trampoline_try(libaioDll, "aioNodeMgr_IsReady", aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig); execexe::trampoline_try(libaioDll, "aioNodeMgr_IsError", aioNodeMgr_IsError, &aioNodeMgr_IsError_orig); execexe::trampoline_try(libaioDll, "aioNodeCtl_Destroy", aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig); execexe::trampoline_try(libaioDll, "aioNodeCtl_GetState", aioNodeCtl_GetState, &aioNodeCtl_GetState_orig); execexe::trampoline_try(libaioDll, "aioNodeCtl_IsReady", aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig); execexe::trampoline_try(libaioDll, "aioNodeCtl_IsError", aioNodeCtl_IsError, &aioNodeCtl_IsError_orig); } } #endif