Initial re-upload of spice2x-24-08-24

This commit is contained in:
2024-08-28 11:10:34 -04:00
commit caa9e02285
1181 changed files with 380065 additions and 0 deletions

624
games/ccj/bi2x_hook.cpp Normal file
View File

@@ -0,0 +1,624 @@
#include "bi2x_hook.h"
#include <cstdint>
#include "util/detour.h"
#include "util/logging.h"
#include "rawinput/rawinput.h"
#include "misc/eamuse.h"
#include "games/io.h"
#include "io.h"
#include "util/tapeled.h"
namespace games::ccj {
/*
* class definitions
*/
struct AIO_SCI_COMM {
};
struct AIO_NMGR_IOB2 {
};
struct AIO_IOB2_BI2X_TBS {
};
struct AIO_IOB2_BI2X_WRFIRM {
};
struct AIO_NMGR_IOB__NODEINFO {
uint8_t data[0xA3];
};
struct AIO_IOB2_BI2X_AC1__INPUTDATA {
uint8_t data[247];
};
struct AIO_IOB2_BI2X_AC1__OUTPUTDATA {
uint8_t data[48];
};
struct AIO_IOB2_BI2X_TBS__INPUT {
uint8_t DevIoCounter;
uint8_t bExIoAErr;
uint8_t bExIoBErr;
uint8_t bPcPowerOn;
uint8_t bPcPowerCheck;
uint8_t CoinCount;
uint8_t bTest;
uint8_t bService;
uint8_t bCoinSw;
uint8_t bCoinJam;
uint8_t bHPDetect;
uint16_t StickY;
uint16_t StickX;
uint8_t bStickBtn;
uint8_t bTrigger1;
uint8_t bTrigger2;
uint8_t bButton0;
uint8_t bButton1;
uint8_t bButton2;
uint8_t bButton3;
};
struct AIO_IOB2_BI2X_TBS__DEVSTATUS {
uint8_t InputCounter;
uint8_t OutputCounter;
uint8_t IoResetCounter;
uint8_t TapeLedCounter;
AIO_IOB2_BI2X_TBS__INPUT Input;
AIO_IOB2_BI2X_AC1__INPUTDATA InputData;
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
};
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value);
/*
* typedefs
*/
// libaio-iob2_video.dll
typedef AIO_IOB2_BI2X_TBS* (__fastcall *aioIob2Bi2xTBS_Create_t)(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId);
typedef void (__fastcall *aioIob2Bi2xTBS_GetDeviceStatus_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl,
AIO_IOB2_BI2X_TBS__DEVSTATUS *o_DevStatus);
typedef void (__fastcall *aioIob2Bi2xTBS_IoReset_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_bfIoReset);
typedef void (__fastcall *aioIob2Bi2xAC1_IoReset_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_bfIoReset);
typedef void (__fastcall *aioIob2Bi2xTBS_SetWatchDogTimer_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint8_t i_Count);
typedef void (__fastcall *aioIob2Bi2xAC1_SetWatchDogTimer_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint8_t i_Count);
typedef void (__fastcall *aioIob2Bi2xTBS_ControlCoinBlocker_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_Slot,
bool i_bOpen);
typedef void (__fastcall *aioIob2Bi2xTBS_AddCounter_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_Counter,
uint32_t i_Count);
typedef void (__fastcall *aioIob2Bi2xTBS_SetAmpVolume_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_Amp,
uint32_t i_Volume);
typedef void (__fastcall *aioIob2Bi2xTBS_EnableUsbCharger_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, bool i_bEnable);
typedef void (__fastcall *aioIob2Bi2xTBS_SetIrLed_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, bool i_bOn);
typedef void (__fastcall *aioIob2Bi2xTBS_SetButton0Lamp_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, bool i_bOn);
typedef void (__fastcall *aioIob2Bi2xTBS_SetIccrLed_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_RGB);
typedef void (__fastcall *aioIob2Bi2xTBS_SetStickLed_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_RGB);
typedef void (__fastcall *aioIob2Bi2xTBS_SetTapeLedData_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_TapeLed, uint8_t *i_pData);
typedef AIO_SCI_COMM* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint32_t i_SerialNumber);
typedef AIO_IOB2_BI2X_WRFIRM* (__fastcall *aioIob2Bi2x_CreateWriteFirmContext_t)(uint32_t i_SerialNumber,
uint32_t i_bfIob);
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);
typedef void (__fastcall *aioNCtlIob_GetNodeInfo_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl,
AIO_NMGR_IOB__NODEINFO *o_NodeInfo);
// libaio.dll
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_TBS *i_pNodeCtl);
typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl);
typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, int32_t i_State);
typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB2_BI2X_TBS *i_pNodeCtl, int32_t i_State);
/*
* function pointers
*/
// libaio-iob2_video.dll
static aioIob2Bi2xTBS_Create_t aioIob2Bi2xTBS_Create_orig = nullptr;
static aioIob2Bi2xTBS_GetDeviceStatus_t aioIob2Bi2xTBS_GetDeviceStatus_orig = nullptr;
static aioIob2Bi2xTBS_IoReset_t aioIob2Bi2xTBS_IoReset_orig = nullptr;
static aioIob2Bi2xAC1_IoReset_t aioIob2Bi2xAC1_IoReset_orig = nullptr;
static aioIob2Bi2xTBS_SetWatchDogTimer_t aioIob2Bi2xTBS_SetWatchDogTimer_orig = nullptr;
static aioIob2Bi2xAC1_SetWatchDogTimer_t aioIob2Bi2xAC1_SetWatchDogTimer_orig = nullptr;
static aioIob2Bi2xTBS_ControlCoinBlocker_t aioIob2Bi2xTBS_ControlCoinBlocker_orig = nullptr;
static aioIob2Bi2xTBS_AddCounter_t aioIob2Bi2xTBS_AddCounter_orig = nullptr;
static aioIob2Bi2xTBS_SetAmpVolume_t aioIob2Bi2xTBS_SetAmpVolume_orig = nullptr;
static aioIob2Bi2xTBS_EnableUsbCharger_t aioIob2Bi2xTBS_EnableUsbCharger_orig = nullptr;
static aioIob2Bi2xTBS_SetIrLed_t aioIob2Bi2xTBS_SetIrLed_orig = nullptr;
static aioIob2Bi2xTBS_SetButton0Lamp_t aioIob2Bi2xTBS_SetButton0Lamp_orig = nullptr;
static aioIob2Bi2xTBS_SetIccrLed_t aioIob2Bi2xTBS_SetIccrLed_orig = nullptr;
static aioIob2Bi2xTBS_SetStickLed_t aioIob2Bi2xTBS_SetStickLed_orig = nullptr;
static aioIob2Bi2xTBS_SetTapeLedData_t aioIob2Bi2xTBS_SetTapeLedData_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;
static aioNCtlIob_GetNodeInfo_t aioNCtlIob_GetNodeInfo_orig = nullptr;
// libaio.dll
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_SCI_COMM *aioSciComm;
static AIO_NMGR_IOB2 *aioNmgrIob2;
static AIO_IOB2_BI2X_TBS *aioIob2Bi2xTbs;
static AIO_IOB2_BI2X_WRFIRM *aioIob2Bi2xWrfirm;
static uint8_t count = 0;
/*
* implementations
*/
static AIO_IOB2_BI2X_TBS* __fastcall aioIob2Bi2xTBS_Create(
AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId) {
if (i_pNodeMgr == aioNmgrIob2) {
log_info("bi2x_hook", "node created");
aioIob2Bi2xTbs = new AIO_IOB2_BI2X_TBS;
return aioIob2Bi2xTbs;
} else {
return aioIob2Bi2xTBS_Create_orig(i_pNodeMgr, i_DevId);
}
}
static void __fastcall aioIob2Bi2xTBS_GetDeviceStatus(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, AIO_IOB2_BI2X_TBS__DEVSTATUS *o_DevStatus) {
RI_MGR->devices_flush_output();
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus);
}
memset(o_DevStatus, 0x00, sizeof(AIO_IOB2_BI2X_TBS__DEVSTATUS));
o_DevStatus->Input.DevIoCounter = count;
count++;
o_DevStatus->Input.StickX = 32768;
o_DevStatus->Input.StickY = 32768;
auto &analogs = get_analogs();
if (analogs[Analogs::Joystick_X].isSet() || analogs[Analogs::Joystick_Y].isSet()) {
o_DevStatus->Input.StickX =
65535 - (uint16_t) (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::Joystick_X]) * 65535);
o_DevStatus->Input.StickY =
65535 - (uint16_t) (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::Joystick_Y]) * 65535);
}
auto &buttons = get_buttons();
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]))
o_DevStatus->Input.bTest = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]))
o_DevStatus->Input.bService = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]))
o_DevStatus->Input.bCoinSw = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Joystick_Up]))
o_DevStatus->Input.StickY = 65535;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Joystick_Down]))
o_DevStatus->Input.StickY = 0;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Joystick_Left]))
o_DevStatus->Input.StickX = 65535;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Joystick_Right]))
o_DevStatus->Input.StickX = 0;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button_Dash]))
o_DevStatus->Input.bTrigger1 = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button_Special]))
o_DevStatus->Input.bButton0 = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button_Action]))
o_DevStatus->Input.bButton1 = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button_Jump]))
o_DevStatus->Input.bButton2 = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button_Slide]))
o_DevStatus->Input.bButton3 = 1;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphones]))
o_DevStatus->Input.bHPDetect = 1;
o_DevStatus->Input.CoinCount = eamuse_coin_get_stock();
}
static void __fastcall aioIob2Bi2xAC1_IoReset(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_bfIoReset) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
} else {
return aioIob2Bi2xAC1_IoReset_orig(i_pNodeCtl, i_bfIoReset);
}
}
static void __fastcall aioIob2Bi2xTBS_IoReset(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_bfIoReset) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
return aioIob2Bi2xAC1_IoReset(i_pNodeCtl, i_bfIoReset);
} else {
return aioIob2Bi2xTBS_IoReset_orig(i_pNodeCtl, i_bfIoReset);
}
}
static void __fastcall aioIob2Bi2xAC1_SetWatchDogTimer(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint8_t i_Count) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
} else {
return aioIob2Bi2xAC1_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
}
}
static void __fastcall aioIob2Bi2xTBS_SetWatchDogTimer(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint8_t i_Count) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
return aioIob2Bi2xAC1_SetWatchDogTimer(i_pNodeCtl, i_Count);
} else {
return aioIob2Bi2xTBS_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
}
}
static void __fastcall aioIob2Bi2xTBS_ControlCoinBlocker(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_Slot, bool i_bOpen) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
eamuse_coin_set_block(!i_bOpen);
} else {
return aioIob2Bi2xTBS_ControlCoinBlocker_orig(i_pNodeCtl, i_Slot, i_bOpen);
}
}
static void __fastcall aioIob2Bi2xTBS_AddCounter(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) {
if (i_pNodeCtl == aioIob2Bi2xTbs && i_Count == 0) {
eamuse_coin_set_stock((uint16_t) i_Count);
} else {
return aioIob2Bi2xTBS_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count);
}
}
static void __fastcall aioIob2Bi2xTBS_SetAmpVolume(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_Amp, uint32_t i_Volume) {
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_SetAmpVolume_orig(i_pNodeCtl, i_Amp, i_Volume);
}
}
static void __fastcall aioIob2Bi2xTBS_EnableUsbCharger(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, bool i_bEnable) {
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_EnableUsbCharger_orig(i_pNodeCtl, i_bEnable);
}
}
static void __fastcall aioIob2Bi2xTBS_SetIrLed(AIO_IOB2_BI2X_TBS *i_pNodeCtl, bool i_bOn) {
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_SetIrLed_orig(i_pNodeCtl, i_bOn);
}
// handle ir led
}
static void __fastcall aioIob2Bi2xTBS_SetButton0Lamp(AIO_IOB2_BI2X_TBS *i_pNodeCtl, bool i_bOn) {
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_SetButton0Lamp_orig(i_pNodeCtl, i_bOn);
}
auto &lights = get_lights();
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::SpecialButton), (i_bOn ? 1.f : 0.f));
}
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value) {
auto &lights = get_lights();
GameAPI::Lights::writeLight(RI_MGR, lights.at(light), value / 255);
}
static void __fastcall aioIob2Bi2xTBS_SetIccrLed(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_RGB) {
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_SetIccrLed_orig(i_pNodeCtl, i_RGB);
}
write_iccr_led(Lights::CardReader_B, i_RGB);
write_iccr_led(Lights::CardReader_G, i_RGB >> 8);
write_iccr_led(Lights::CardReader_R, i_RGB >> 16);
}
static void __fastcall aioIob2Bi2xTBS_SetStickLed(AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_RGB) {
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_SetStickLed_orig(i_pNodeCtl, i_RGB);
}
// handle stick led
}
static void __fastcall aioIob2Bi2xTBS_SetTapeLedData(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, uint32_t i_TapeLed, uint8_t *i_pData) {
if (i_pNodeCtl != aioIob2Bi2xTbs) {
return aioIob2Bi2xTBS_SetTapeLedData_orig(i_pNodeCtl, i_TapeLed, i_pData);
}
/*
* index mapping
* 0 - title panel - 144 bytes - 48 colors
* 1 - side panel - 147 bytes - 49 colors
*
* data is stored in RGB order, 3 bytes per color
*
* TODO: expose this data via API
*/
static struct TapeLedMapping {
size_t data_size;
int r, g, b;
TapeLedMapping(size_t data_size, int r, int g, int b)
: data_size(data_size), r(r), g(g), b(b) {}
} mapping[] = {
{ 48, Lights::TitlePanel_R, Lights::TitlePanel_G, Lights::TitlePanel_B },
{ 49, Lights::SidePanel_R, Lights::SidePanel_G, Lights::SidePanel_B },
};
if (tapeledutils::is_enabled() && i_TapeLed < std::size(mapping)) {
auto &map = mapping[i_TapeLed];
// pick a color to use
const auto rgb = tapeledutils::pick_color_from_led_tape(i_pData, map.data_size);
// program the lights into API
auto &lights = get_lights();
GameAPI::Lights::writeLight(RI_MGR, lights[map.r], rgb.r);
GameAPI::Lights::writeLight(RI_MGR, lights[map.g], rgb.g);
GameAPI::Lights::writeLight(RI_MGR, lights[map.b], rgb.b);
}
}
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;
return aioIob2Bi2x_WriteFirmIsCompleted_orig(i_State);
}
static bool __fastcall aioIob2Bi2x_WriteFirmIsError(int32_t i_State) {
if (aioIob2Bi2xWrfirm != nullptr)
return false;
return aioIob2Bi2x_WriteFirmIsError_orig(i_State);
}
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) {
} else {
return aioNMgrIob_BeginManage_orig(i_pNodeMgr);
}
}
static void __fastcall aioNCtlIob_GetNodeInfo(
AIO_IOB2_BI2X_TBS *i_pNodeCtl, AIO_NMGR_IOB__NODEINFO *o_NodeInfo) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
memset(o_NodeInfo, 0, sizeof(AIO_NMGR_IOB__NODEINFO));
} else {
return aioNCtlIob_GetNodeInfo_orig(i_pNodeCtl, o_NodeInfo);
}
}
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_TBS *i_pNodeCtl) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
delete aioIob2Bi2xTbs;
aioIob2Bi2xTbs = nullptr;
} else {
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
}
}
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB2_BI2X_TBS *i_pNodeCtl) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
return 1;
} else {
return aioNodeCtl_GetState_orig(i_pNodeCtl);
}
}
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB2_BI2X_TBS *i_pNodeCtl, int32_t i_State) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
return true;
} else {
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
}
}
static bool __fastcall aioNodeCtl_IsError(AIO_IOB2_BI2X_TBS *i_pNodeCtl, int32_t i_State) {
if (i_pNodeCtl == aioIob2Bi2xTbs) {
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";
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_Create",
aioIob2Bi2xTBS_Create, &aioIob2Bi2xTBS_Create_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_GetDeviceStatus",
aioIob2Bi2xTBS_GetDeviceStatus, &aioIob2Bi2xTBS_GetDeviceStatus_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_IoReset",
aioIob2Bi2xTBS_IoReset, &aioIob2Bi2xTBS_IoReset_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_IoReset",
aioIob2Bi2xAC1_IoReset, &aioIob2Bi2xAC1_IoReset_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_SetWatchDogTimer",
aioIob2Bi2xTBS_SetWatchDogTimer, &aioIob2Bi2xTBS_SetWatchDogTimer_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetWatchDogTimer",
aioIob2Bi2xAC1_SetWatchDogTimer, &aioIob2Bi2xAC1_SetWatchDogTimer_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_ControlCoinBlocker",
aioIob2Bi2xTBS_ControlCoinBlocker, &aioIob2Bi2xTBS_ControlCoinBlocker_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_AddCounter",
aioIob2Bi2xTBS_AddCounter, &aioIob2Bi2xTBS_AddCounter_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_SetAmpVolume",
aioIob2Bi2xTBS_SetAmpVolume, &aioIob2Bi2xTBS_SetAmpVolume_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_EnableUsbCharger",
aioIob2Bi2xTBS_EnableUsbCharger, &aioIob2Bi2xTBS_EnableUsbCharger_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_SetIrLed",
aioIob2Bi2xTBS_SetIrLed, &aioIob2Bi2xTBS_SetIrLed_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_SetButton0Lamp",
aioIob2Bi2xTBS_SetButton0Lamp, &aioIob2Bi2xTBS_SetButton0Lamp_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_SetIccrLed",
aioIob2Bi2xTBS_SetIccrLed, &aioIob2Bi2xTBS_SetIccrLed_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_SetStickLed",
aioIob2Bi2xTBS_SetStickLed, &aioIob2Bi2xTBS_SetStickLed_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xTBS_SetTapeLedData",
aioIob2Bi2xTBS_SetTapeLedData, &aioIob2Bi2xTBS_SetTapeLedData_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_OpenSciUsbCdc",
aioIob2Bi2x_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_CreateWriteFirmContext",
aioIob2Bi2x_CreateWriteFirmContext, &aioIob2Bi2x_CreateWriteFirmContext_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_DestroyWriteFirmContext",
aioIob2Bi2x_DestroyWriteFirmContext, &aioIob2Bi2x_DestroyWriteFirmContext_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmGetState",
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsCompleted",
aioIob2Bi2x_WriteFirmIsCompleted, &aioIob2Bi2x_WriteFirmIsCompleted_orig);
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsError",
aioIob2Bi2x_WriteFirmIsError, &aioIob2Bi2x_WriteFirmIsError_orig);
// libaio-iob.dll
const auto libaioIobDll = "libaio-iob.dll";
detour::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
detour::trampoline_try(libaioIobDll, "aioNMgrIob_BeginManage",
aioNMgrIob_BeginManage, &aioNMgrIob_BeginManage_orig);
detour::trampoline_try(libaioIobDll, "aioNCtlIob_GetNodeInfo",
aioNCtlIob_GetNodeInfo, &aioNCtlIob_GetNodeInfo_orig);
// libaio.dll
const auto libaioDll = "libaio.dll";
detour::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
detour::trampoline_try(libaioDll, "aioNodeMgr_GetState",
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
detour::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
detour::trampoline_try(libaioDll, "aioNodeMgr_IsError",
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
detour::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
detour::trampoline_try(libaioDll, "aioNodeCtl_GetState",
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
detour::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
}
}

5
games/ccj/bi2x_hook.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
namespace games::ccj {
void bi2x_hook_init();
}

101
games/ccj/ccj.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include "ccj.h"
#include <format>
#include "util/libutils.h"
#include "util/fileutils.h"
#include "util/utils.h"
#include "util/detour.h"
#include "acioemu/handle.h"
#include "misc/wintouchemu.h"
#include "bi2x_hook.h"
#include "trackball.h"
namespace games::ccj {
// nomatchselect: disables debug menu that says "Select Match Mode"
// q1: disables v-sync
std::string CCJ_INJECT_ARGS = "-nomatchselect";
static acioemu::ACIOHandle *acioHandle = nullptr;
static std::string commandLine;
static decltype(AddVectoredExceptionHandler) *AddVectoredExceptionHandler_orig = nullptr;
static decltype(CreateFileW) *execexe_CreateFileW_orig = nullptr;
static decltype(ShowCursor) *ShowCursor_orig = nullptr;
static decltype(GetCommandLineA) *GetCommandLineA_orig = nullptr;
static HANDLE WINAPI execexe_CreateFileW_hook(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
const auto fileName = ws2s(lpFileName);
if (fileName == "COM1" && acioHandle->open(lpFileName)) {
SetLastError(0);
return (HANDLE) acioHandle;
} else {
return execexe_CreateFileW_orig(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}
}
static int WINAPI ShowCursor_hook(BOOL bShow) {
return 1;
}
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
return INVALID_HANDLE_VALUE;
}
static LPSTR WINAPI GetCommandLineA_hook() {
return (LPSTR)commandLine.c_str();
}
void CCJGame::attach() {
Game::attach();
// create required files
fileutils::dir_create_recursive("dev/raw/log");
fileutils::bin_write("dev/raw/bootio", nullptr, 0);
fileutils::bin_write("dev/raw/log/output_log.txt", nullptr, 0);
// preload libraries
libutils::load_library("execexe.dll");
libutils::load_library("libaio.dll");
libutils::load_library("libaio-iob.dll");
libutils::load_library("libaio-iob2_video.dll");
libutils::load_library("win10actlog.dll");
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
// insert BI2X hooks
bi2x_hook_init();
// insert trackball hooks
trackball_hook_init();
// add card reader
acioHandle = new acioemu::ACIOHandle(L"COM1");
devicehook_init_trampoline();
devicehook_add(acioHandle);
}
void CCJGame::post_attach() {
Game::post_attach();
detour::trampoline_try("kernel32.dll", "AddVectoredExceptionHandler",
(void*)AddVectoredExceptionHandler_hook,(void**)&AddVectoredExceptionHandler_orig);
detour::trampoline_try("kernel32.dll", "GetCommandLineA",
(void*)GetCommandLineA_hook, (void**)&GetCommandLineA_orig);
detour::trampoline_try("user32.dll", "ShowCursor",
(void*)ShowCursor_hook, (void**)&ShowCursor_orig);
commandLine = std::format("{} {}", GetCommandLineA_orig(), CCJ_INJECT_ARGS);
trackball_thread_start();
}
void CCJGame::detach() {
Game::detach();
devicehook_dispose();
trackball_thread_stop();
}
}

16
games/ccj/ccj.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "games/game.h"
namespace games::ccj {
extern std::string CCJ_INJECT_ARGS;
class CCJGame : public games::Game {
public:
CCJGame() : Game("Chase Chase Jokers") {}
virtual void attach() override;
virtual void post_attach() override;
virtual void detach() override;
};
}

73
games/ccj/io.cpp Normal file
View File

@@ -0,0 +1,73 @@
#include "io.h"
std::vector<Button> &games::ccj::get_buttons() {
static std::vector<Button> buttons;
if (buttons.empty()) {
buttons = GameAPI::Buttons::getButtons("Chase Chase Jokers");
GameAPI::Buttons::sortButtons(
&buttons,
"Service",
"Test",
"Coin Mech",
"Joystick Up",
"Joystick Down",
"Joystick Left",
"Joystick Right",
"Dash",
"Action",
"Jump",
"Slide",
"Special",
"Headphones",
"Trackball Up",
"Trackball Down",
"Trackball Left",
"Trackball Right"
);
}
return buttons;
}
std::vector<Analog> &games::ccj::get_analogs() {
static std::vector<Analog> analogs;
if (analogs.empty()) {
analogs = GameAPI::Analogs::getAnalogs("Chase Chase Jokers");
GameAPI::Analogs::sortAnalogs(
&analogs,
"Joystick X",
"Joystick Y",
"Trackball DX",
"Trackball DY"
);
}
return analogs;
}
std::vector<Light> &games::ccj::get_lights() {
static std::vector<Light> lights;
if (lights.empty()) {
lights = GameAPI::Lights::getLights("Chase Chase Jokers");
GameAPI::Lights::sortLights(
&lights,
"Title Panel R",
"Title Panel G",
"Title Panel B",
"Side Panel R",
"Side Panel G",
"Side Panel B",
"Card Reader R",
"Card Reader G",
"Card Reader B",
"Special Button"
);
}
return lights;
}

58
games/ccj/io.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#include <vector>
#include "cfg/api.h"
namespace games::ccj {
namespace Buttons {
enum {
Service,
Test,
CoinMech,
Joystick_Up,
Joystick_Down,
Joystick_Left,
Joystick_Right,
Button_Dash,
Button_Action,
Button_Jump,
Button_Slide,
Button_Special,
Headphones,
Trackball_Up,
Trackball_Down,
Trackball_Left,
Trackball_Right
};
}
namespace Analogs {
enum {
Joystick_X,
Joystick_Y,
Trackball_DX,
Trackball_DY
};
}
// all lights in correct order
namespace Lights {
typedef enum {
TitlePanel_R,
TitlePanel_G,
TitlePanel_B,
SidePanel_R,
SidePanel_G,
SidePanel_B,
CardReader_R,
CardReader_G,
CardReader_B,
SpecialButton,
} ccj_lights_t;
}
// getters
std::vector<Button> &get_buttons();
std::vector<Analog> &get_analogs();
std::vector<Light> &get_lights();
}

267
games/ccj/trackball.cpp Normal file
View File

@@ -0,0 +1,267 @@
#include "trackball.h"
#include <chrono>
#include <thread>
#include "util/detour.h"
#include "util/logging.h"
#include "rawinput/rawinput.h"
#include "games/io.h"
#include "io.h"
namespace games::ccj {
bool MOUSE_TRACKBALL = false;
bool MOUSE_TRACKBALL_USE_TOGGLE = false;
uint8_t TRACKBALL_SENSITIVITY = 10;
static HANDLE fakeHandle = (HANDLE)0xDEADBEEF;
static HWND hWnd = nullptr;
static WNDPROC wndProc = nullptr;
static std::thread *tbThread = nullptr;
static bool tbThreadRunning = false;
static const wchar_t *fakeDeviceName = L"VID_1241&PID_1111";
static const wchar_t *windowName = L"ChaseProject";
static decltype(GetRawInputDeviceList) *GetRawInputDeviceList_orig = nullptr;
static decltype(GetRawInputDeviceInfoW) *GetRawInputDeviceInfoW_orig = nullptr;
static decltype(SetWindowLongPtrW) *SetWindowLongPtrW_orig = nullptr;
static decltype(GetRawInputData) *GetRawInputData_orig = nullptr;
static decltype(RegisterRawInputDevices) *RegisterRawInputDevices_orig = nullptr;
static UINT WINAPI GetRawInputDeviceList_hook(PRAWINPUTDEVICELIST pRawInputDeviceList, PUINT puiNumDevices,
UINT cbSize) {
auto result = GetRawInputDeviceList_orig(pRawInputDeviceList, puiNumDevices, cbSize);
if (result == 0xFFFFFFFF)
return result;
if (pRawInputDeviceList == NULL) {
(*puiNumDevices)++;
} else if (result < *puiNumDevices) {
pRawInputDeviceList[result] = {fakeHandle, 0};
result++;
}
return result;
}
static UINT WINAPI GetRawInputDeviceInfoW_hook(HANDLE hDevice, UINT uiCommand, LPVOID pData, PUINT pcbSize) {
if (hDevice != fakeHandle || uiCommand != RIDI_DEVICENAME)
return GetRawInputDeviceInfoW_orig(hDevice, uiCommand, pData, pcbSize);
const auto requiredLen = (wcslen(fakeDeviceName) + 1) * sizeof(wchar_t);
if (*pcbSize < requiredLen) {
*pcbSize = requiredLen;
return 0xFFFFFFFF;
}
if (pData == NULL) {
*pcbSize = requiredLen;
return 0;
}
wcscpy((wchar_t*)pData, fakeDeviceName);
return requiredLen;
}
static LONG_PTR WINAPI SetWindowLongPtrW_hook(HWND _hWnd, int nIndex, LONG_PTR dwNewLong) {
wchar_t buffer[256];
if (nIndex != GWLP_WNDPROC || GetWindowTextW(_hWnd, buffer, 256) == 0 || !wcswcs(buffer, windowName))
return SetWindowLongPtrW_orig(_hWnd, nIndex, dwNewLong);
hWnd = _hWnd;
wndProc = (WNDPROC)dwNewLong;
return SetWindowLongPtrW_orig(_hWnd, nIndex, dwNewLong);
}
static UINT WINAPI GetRawInputData_hook(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader) {
if (hRawInput != fakeHandle)
return GetRawInputData_orig(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader);
if (pData == NULL) {
if (uiCommand == RID_HEADER)
*pcbSize = sizeof(RAWINPUTHEADER);
else
*pcbSize = sizeof(RAWINPUT);
return 0;
}
const RAWINPUTHEADER header = { RIM_TYPEMOUSE, sizeof(RAWINPUT), fakeHandle, 0 };
if (uiCommand == RID_HEADER) {
if (*pcbSize < sizeof(RAWINPUTHEADER)) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0xFFFFFFFF;
}
*((RAWINPUTHEADER*)pData) = header;
return sizeof(RAWINPUTHEADER);
} else if (uiCommand == RID_INPUT) {
if (*pcbSize < sizeof(RAWINPUT)) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0xFFFFFFFF;
}
RAWMOUSE rawMouse {};
if (MOUSE_TRACKBALL) {
static bool active = false;
static bool lastState = false;
static std::chrono::time_point<std::chrono::steady_clock> lastModified = std::chrono::steady_clock::now();
static std::chrono::milliseconds debounceDuration(100);
auto currentTime = std::chrono::steady_clock::now();
bool pressed = GetAsyncKeyState(VK_RBUTTON) & 0x8000;
bool focused = GetForegroundWindow() == hWnd;
if (focused && MOUSE_TRACKBALL_USE_TOGGLE && pressed && (currentTime - lastModified > debounceDuration)) {
active = !active;
lastModified = currentTime;
}
if (focused && ((MOUSE_TRACKBALL_USE_TOGGLE && active) || (!MOUSE_TRACKBALL_USE_TOGGLE && pressed))) {
POINT cursor;
RECT client;
GetClientRect(hWnd, &client);
int sx = client.right - client.left;
int sy = client.bottom - client.top;
GetCursorPos(&cursor);
ScreenToClient(hWnd, &cursor);
static int lastX = cursor.x;
static int lastY = cursor.y;
if (!lastState) {
lastX = cursor.x;
lastY = cursor.y;
lastState = true;
}
rawMouse.usFlags = MOUSE_MOVE_RELATIVE;
rawMouse.lLastX = (int)((float)(cursor.x - lastX) * (float)TRACKBALL_SENSITIVITY / 20.0f);
rawMouse.lLastY = (int)((float)(lastY - cursor.y) * (float)TRACKBALL_SENSITIVITY / 20.0f);
bool updateCursor = false;
if (cursor.x <= 0) {
updateCursor = true;
cursor.x = sx - 5;
} else if (cursor.x >= sx - 1) {
updateCursor = true;
cursor.x = 5;
}
if (cursor.y <= 0) {
updateCursor = true;
cursor.y = sy - 5;
} else if (cursor.y >= sy - 1) {
updateCursor = true;
cursor.y = 5;
}
lastX = cursor.x;
lastY = cursor.y;
if (updateCursor) {
ClientToScreen(hWnd, &cursor);
SetCursorPos(cursor.x, cursor.y);
}
} else if (lastState && !active) {
lastState = false;
}
} else {
rawMouse.usFlags = MOUSE_MOVE_RELATIVE;
auto &analogs = get_analogs();
if (analogs[Analogs::Trackball_DX].isSet() || analogs[Analogs::Trackball_DY].isSet()) {
float x = GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::Trackball_DX]) * 2.0f - 1.0f;
float y = GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::Trackball_DY]) * 2.0f - 1.0f;
rawMouse.lLastX = (long) (x * (float) TRACKBALL_SENSITIVITY);
rawMouse.lLastY = (long) (-y * (float) TRACKBALL_SENSITIVITY);
}
auto &buttons = get_buttons();
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Trackball_Up]))
rawMouse.lLastY = TRACKBALL_SENSITIVITY;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Trackball_Down]))
rawMouse.lLastY = -TRACKBALL_SENSITIVITY;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Trackball_Left]))
rawMouse.lLastX = -TRACKBALL_SENSITIVITY;
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Trackball_Right]))
rawMouse.lLastX = TRACKBALL_SENSITIVITY;
}
*((RAWINPUT*)pData) = { header, { rawMouse } };
return sizeof(RAWINPUT);
}
return 0xFFFFFFFF;
}
static BOOL WINAPI RegisterRawInputDevices_hook(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize) {
if (uiNumDevices == 2 && pRawInputDevices[1].usUsage == HID_USAGE_GENERIC_GAMEPAD)
uiNumDevices = 1;
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
}
void trackball_hook_init() {
// avoid double init
static bool initialized = false;
if (initialized) {
return;
} else {
initialized = true;
}
// announce
log_info("trackball", "init");
// user32
const auto user32Dll = "user32.dll";
detour::trampoline_try(user32Dll, "GetRawInputDeviceList",
GetRawInputDeviceList_hook, &GetRawInputDeviceList_orig);
detour::trampoline_try(user32Dll, "GetRawInputDeviceInfoW",
GetRawInputDeviceInfoW_hook, &GetRawInputDeviceInfoW_orig);
detour::trampoline_try(user32Dll, "SetWindowLongPtrW",
SetWindowLongPtrW_hook, &SetWindowLongPtrW_orig);
detour::trampoline_try(user32Dll, "GetRawInputData",
GetRawInputData_hook, &GetRawInputData_orig);
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
}
void trackball_thread_start() {
using namespace std::chrono_literals;
tbThreadRunning = true;
log_info("trackball", "thread start, use mouse: {}, toggle: {}", MOUSE_TRACKBALL, MOUSE_TRACKBALL_USE_TOGGLE);
tbThread = new std::thread([&] {
while (tbThreadRunning) {
if (hWnd && wndProc) {
wndProc(hWnd, WM_INPUT, RIM_INPUT, (LPARAM)fakeHandle);
}
if (!tbThreadRunning)
break;
std::this_thread::sleep_for(10ms);
}
});
}
void trackball_thread_stop() {
tbThreadRunning = false;
if (tbThread)
tbThread->join();
log_info("trackball", "thread stop");
delete tbThread;
}
}

12
games/ccj/trackball.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
namespace games::ccj {
extern bool MOUSE_TRACKBALL;
extern bool MOUSE_TRACKBALL_USE_TOGGLE;
extern uint8_t TRACKBALL_SENSITIVITY;
void trackball_hook_init();
void trackball_thread_start();
void trackball_thread_stop();
}