Update to spice2x-25-10-07 (pre-apply)
> broken commit
This commit is contained in:
@@ -0,0 +1,739 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#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"
|
||||
|
||||
namespace games::pc {
|
||||
bool PC_KNOB_MODE = false;
|
||||
|
||||
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 {
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
};
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left])) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left])) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace games::pc {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
#include "io.h"
|
||||
|
||||
std::vector<Button> &games::pc::get_buttons() {
|
||||
static std::vector<Button> buttons;
|
||||
|
||||
if (buttons.empty()) {
|
||||
buttons = GameAPI::Buttons::getButtons("Polaris Chord");
|
||||
|
||||
GameAPI::Buttons::sortButtons(
|
||||
&buttons,
|
||||
"Service",
|
||||
"Test",
|
||||
"Coin Mech",
|
||||
"Button 1",
|
||||
"Button 2",
|
||||
"Button 3",
|
||||
"Button 4",
|
||||
"Button 5",
|
||||
"Button 6",
|
||||
"Button 7",
|
||||
"Button 8",
|
||||
"Button 9",
|
||||
"Button 10",
|
||||
"Button 11",
|
||||
"Button 12",
|
||||
"Fader-L Left",
|
||||
"Fader-L Right",
|
||||
"Fader-R Left",
|
||||
"Fader-R Right",
|
||||
"Headphone"
|
||||
);
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string games::pc::get_buttons_help() {
|
||||
// keep to max 100 characters wide
|
||||
return
|
||||
" FADER-L FADER-R \n"
|
||||
"\n"
|
||||
" B1 B2 B3 ... B11 B12\n"
|
||||
"\n"
|
||||
" Most menu interactions are on the touch screen; you can use your mouse.\n"
|
||||
;
|
||||
}
|
||||
|
||||
std::vector<Analog> &games::pc::get_analogs() {
|
||||
static std::vector<Analog> analogs;
|
||||
|
||||
if (analogs.empty()) {
|
||||
analogs = GameAPI::Analogs::getAnalogs("Polaris Chord");
|
||||
|
||||
GameAPI::Analogs::sortAnalogs(
|
||||
&analogs,
|
||||
"Fader-L",
|
||||
"Fader-R"
|
||||
);
|
||||
}
|
||||
|
||||
return analogs;
|
||||
}
|
||||
|
||||
std::vector<Light> &games::pc::get_lights() {
|
||||
static std::vector<Light> lights;
|
||||
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Polaris Chord");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
&lights,
|
||||
"IC Reader R",
|
||||
"IC Reader G",
|
||||
"IC Reader B",
|
||||
|
||||
"Button 1 R",
|
||||
"Button 1 G",
|
||||
"Button 1 B",
|
||||
|
||||
"Button 2 R",
|
||||
"Button 2 G",
|
||||
"Button 2 B",
|
||||
|
||||
"Button 3 R",
|
||||
"Button 3 G",
|
||||
"Button 3 B",
|
||||
|
||||
"Button 4 R",
|
||||
"Button 4 G",
|
||||
"Button 4 B",
|
||||
|
||||
"Button 5 R",
|
||||
"Button 5 G",
|
||||
"Button 5 B",
|
||||
|
||||
"Button 6 R",
|
||||
"Button 6 G",
|
||||
"Button 6 B",
|
||||
|
||||
"Button 7 R",
|
||||
"Button 7 G",
|
||||
"Button 7 B",
|
||||
|
||||
"Button 8 R",
|
||||
"Button 8 G",
|
||||
"Button 8 B",
|
||||
|
||||
"Button 9 R",
|
||||
"Button 9 G",
|
||||
"Button 9 B",
|
||||
|
||||
"Button 10 R",
|
||||
"Button 10 G",
|
||||
"Button 10 B",
|
||||
|
||||
"Button 11 R",
|
||||
"Button 11 G",
|
||||
"Button 11 B",
|
||||
|
||||
"Button 12 R",
|
||||
"Button 12 G",
|
||||
"Button 12 B"
|
||||
);
|
||||
}
|
||||
|
||||
return lights;
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace games::pc {
|
||||
namespace Buttons {
|
||||
enum {
|
||||
Service,
|
||||
Test,
|
||||
CoinMech,
|
||||
Button1,
|
||||
Button2,
|
||||
Button3,
|
||||
Button4,
|
||||
Button5,
|
||||
Button6,
|
||||
Button7,
|
||||
Button8,
|
||||
Button9,
|
||||
Button10,
|
||||
Button11,
|
||||
Button12,
|
||||
FaderL_Left,
|
||||
FaderL_Right,
|
||||
FaderR_Left,
|
||||
FaderR_Right,
|
||||
Headphone
|
||||
};
|
||||
}
|
||||
|
||||
namespace Analogs {
|
||||
enum {
|
||||
FaderL,
|
||||
FaderR,
|
||||
};
|
||||
}
|
||||
|
||||
namespace Lights {
|
||||
typedef enum {
|
||||
IC_READER_R,
|
||||
IC_READER_G,
|
||||
IC_READER_B,
|
||||
|
||||
Button1_R,
|
||||
Button1_G,
|
||||
Button1_B,
|
||||
|
||||
Button2_R,
|
||||
Button2_G,
|
||||
Button2_B,
|
||||
|
||||
Button3_R,
|
||||
Button3_G,
|
||||
Button3_B,
|
||||
|
||||
Button4_R,
|
||||
Button4_G,
|
||||
Button4_B,
|
||||
|
||||
Button5_R,
|
||||
Button5_G,
|
||||
Button5_B,
|
||||
|
||||
Button6_R,
|
||||
Button6_G,
|
||||
Button6_B,
|
||||
|
||||
Button7_R,
|
||||
Button7_G,
|
||||
Button7_B,
|
||||
|
||||
Button8_R,
|
||||
Button8_G,
|
||||
Button8_B,
|
||||
|
||||
Button9_R,
|
||||
Button9_G,
|
||||
Button9_B,
|
||||
|
||||
Button10_R,
|
||||
Button10_G,
|
||||
Button10_B,
|
||||
|
||||
Button11_R,
|
||||
Button11_G,
|
||||
Button11_B,
|
||||
|
||||
Button12_R,
|
||||
Button12_G,
|
||||
Button12_B
|
||||
|
||||
} pc_lights_t;
|
||||
}
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::string get_buttons_help();
|
||||
std::vector<Analog> &get_analogs();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
#include "pc.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
#include "bi2x_hook.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/unity_player.h"
|
||||
#include "util/execexe.h"
|
||||
#include "acioemu/handle.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
|
||||
namespace games::pc {
|
||||
std::string PC_INJECT_ARGS = "";
|
||||
bool PC_NO_IO = false;
|
||||
|
||||
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||
static std::wstring portName = L"COM1";
|
||||
|
||||
static decltype(RegisterRawInputDevices) *RegisterRawInputDevices_orig = nullptr;
|
||||
static decltype(EnumDisplaySettingsW) *EnumDisplaySettingsW_orig = nullptr;
|
||||
static decltype(QueryDisplayConfig) *QueryDisplayConfig_orig = nullptr;
|
||||
|
||||
static BOOL WINAPI RegisterRawInputDevices_hook(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize) {
|
||||
|
||||
// if the caller is spice itself, then pass through.
|
||||
if (pRawInputDevices &&
|
||||
(uiNumDevices > 0) &&
|
||||
(pRawInputDevices[0].hwndTarget == RI_MGR->input_hwnd)) {
|
||||
|
||||
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
|
||||
}
|
||||
|
||||
// otherwise, it must be the game; prevent the game from registering for raw input
|
||||
// and hijacking WM_INPUT messages.
|
||||
SetLastError(0xDEADBEEF);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL WINAPI EnumDisplaySettingsW_hook(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode) {
|
||||
auto result = EnumDisplaySettingsW_orig(lpszDeviceName, iModeNum, lpDevMode);
|
||||
|
||||
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Screen-currentResolution.html
|
||||
// "If the player is running in windowed mode, this returns the current dimensions of the game window in pixels and the display refresh rate."
|
||||
// Override it to 1920x1080 to avoid broken UI under Windowed mode
|
||||
if (result && iModeNum == ENUM_CURRENT_SETTINGS) {
|
||||
// Actually make it returns size of the game window, Can I?
|
||||
lpDevMode->dmPelsWidth = 1920;
|
||||
lpDevMode->dmPelsHeight = 1080;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LONG WINAPI QueryDisplayConfig_hook(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*, DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*) {
|
||||
// make unity fallback to EnumDisplaySettingsW as I don't
|
||||
// want to deal with this api which is way more complex
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void PCGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
// add card reader
|
||||
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
|
||||
devicehook_init_trampoline();
|
||||
devicehook_add(acioHandle);
|
||||
|
||||
execexe::init();
|
||||
execexe::init_deferred([] () {
|
||||
execexe::init_port_hook(portName, acioHandle);
|
||||
execexe::load_library("libaio.dll");
|
||||
execexe::load_library("libaio-iob.dll");
|
||||
execexe::load_library("libaio-iob_video.dll");
|
||||
execexe::load_library("libaio-iob2_video.dll");
|
||||
execexe::load_library("win10actlog.dll");
|
||||
|
||||
if (!PC_NO_IO) {
|
||||
bi2x_hook_init();
|
||||
}
|
||||
});
|
||||
|
||||
const auto user32Dll = "user32.dll";
|
||||
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
|
||||
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
|
||||
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
||||
QueryDisplayConfig_hook, &QueryDisplayConfig_orig);
|
||||
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
||||
EnumDisplaySettingsW_hook, &EnumDisplaySettingsW_orig);
|
||||
|
||||
if (GRAPHICS_SHOW_CURSOR) {
|
||||
unity_utils::force_show_cursor(true);
|
||||
}
|
||||
|
||||
unity_utils::set_args(
|
||||
std::format("{} {}{}",
|
||||
GetCommandLineA(),
|
||||
PC_INJECT_ARGS,
|
||||
unity_utils::get_unity_player_args()));
|
||||
}
|
||||
|
||||
void PCGame::detach() {
|
||||
Game::detach();
|
||||
|
||||
devicehook_dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "games/game.h"
|
||||
|
||||
namespace games::pc {
|
||||
extern std::string PC_INJECT_ARGS;
|
||||
extern bool PC_NO_IO;
|
||||
extern bool PC_KNOB_MODE;
|
||||
|
||||
class PCGame : public games::Game {
|
||||
public:
|
||||
PCGame() : Game("Polaris Chord") {}
|
||||
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user