mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
gitadora: Added support for GITADORA Delta (#476)
## Link to GitHub Issue, if one exists None ## Description of change - Added bi2x_hook - Added J32D (drum) and J33I (guitar) emulations - AMI2000 card reader (this game uses this instead of ICCA) ## Testing Played through a 15-minute session on Drummania. Guitar mode works with keyboard input.
This commit is contained in:
@@ -401,6 +401,10 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
games/nost/poke.cpp
|
||||
games/gitadora/gitadora.cpp
|
||||
games/gitadora/io.cpp
|
||||
games/gitadora/handle.cpp
|
||||
games/gitadora/j32d.cpp
|
||||
games/gitadora/j33i.cpp
|
||||
games/gitadora/bi2x_hook.cpp
|
||||
games/mga/mga.cpp
|
||||
games/mga/io.cpp
|
||||
games/mga/gunio.cpp
|
||||
@@ -533,6 +537,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
misc/sciunit.cpp
|
||||
misc/sde.cpp
|
||||
misc/wintouchemu.cpp
|
||||
misc/ami2000.cpp
|
||||
|
||||
# nvapi
|
||||
nvapi/nvapi.cpp
|
||||
|
||||
@@ -0,0 +1,664 @@
|
||||
#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"
|
||||
#include <typeinfo>
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
// /*
|
||||
// * class definitions
|
||||
// */
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB5 {
|
||||
};
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
};
|
||||
|
||||
struct AIO_IOB5_Y32D {
|
||||
};
|
||||
|
||||
// struct AIO_IOB5_Y33I {
|
||||
// };
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB__NODEINFO {
|
||||
uint8_t data[0xA8];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
};
|
||||
|
||||
struct AIO_SCI_COMM_T {
|
||||
uint64_t unk_0;
|
||||
struct AIO_SCI_COMM *aioSciComm;
|
||||
};
|
||||
|
||||
struct AIO_COMM_STATUS {
|
||||
uint64_t unk[5];
|
||||
};
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
struct AIO_IOB5_Y32D__DEVSTATUS {
|
||||
uint8_t unk[0xB1];
|
||||
};
|
||||
|
||||
/*
|
||||
* 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_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 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);
|
||||
|
||||
// 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_IOB5_Y32D *i_pNodeCtl,
|
||||
AIO_NMGR_IOB__NODEINFO *o_NodeInfo);
|
||||
|
||||
// libaio.dll
|
||||
typedef AIO_SCI_COMM_T *(__fastcall *aioSciComm_Open_t)(AIO_SCI_COMM_T *unk);
|
||||
typedef void(__fastcall *aioSci_Destroy_t)(AIO_SCI_COMM *i_pNodeMgr);
|
||||
typedef void(__fastcall *aioSci_GetCommStatus_t)(AIO_SCI_COMM *i_pNodeMgr, AIO_COMM_STATUS *i_Status);
|
||||
typedef void(__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB5 *i_pNodeMgr);
|
||||
typedef int32_t(__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB5 *i_pNodeMgr);
|
||||
typedef bool(__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State);
|
||||
typedef bool(__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State);
|
||||
typedef void(__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB5_Y32D *i_pNodeCtl);
|
||||
typedef int32_t(__fastcall *aioNodeCtl_GetState_t)(AIO_IOB5_Y32D *i_pNodeCtl);
|
||||
typedef bool(__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State);
|
||||
typedef bool(__fastcall *aioNodeCtl_IsError_t)(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State);
|
||||
typedef void(__fastcall *aioNodeCtl_UpdateDevicesStatus_t)();
|
||||
|
||||
// libaio-iob5.dll
|
||||
typedef AIO_NMGR_IOB5 *(__fastcall *aioNMgrIob5_Create_t)(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode);
|
||||
|
||||
// libaio-iob5_y32.dll
|
||||
typedef AIO_IOB5_Y32D *(__fastcall *aioIob5Y32d_Create_t)(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode);
|
||||
// typedef AIO_IOB5_Y33I *(__fastcall *aioIob5Y33i_Create_t)(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode);
|
||||
|
||||
typedef void(__fastcall *aioIob5Y32d_GetDeviceStatus_t)(AIO_IOB5_Y32D *i_pNodeCtl,
|
||||
AIO_IOB5_Y32D__DEVSTATUS *o_DevStatus);
|
||||
// typedef void(__fastcall *aioIob5Y33i_GetDeviceStatus_t)(AIO_IOB5_Y33I *i_pNodeCtl,
|
||||
// AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus);
|
||||
|
||||
/*
|
||||
* 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_SetTapeLedDataLimit_t aioIob2Bi2x_SetTapeLedDataLimit_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;
|
||||
|
||||
// libaio.dll
|
||||
static aioSciComm_Open_t aioSciComm_Open_orig = nullptr;
|
||||
static aioSci_Destroy_t aioSci_Destroy_orig = nullptr;
|
||||
static aioSci_GetCommStatus_t aioSci_GetCommStatus_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;
|
||||
static aioNodeCtl_UpdateDevicesStatus_t aioNodeCtl_UpdateDevicesStatus_orig = nullptr;
|
||||
|
||||
// libaio-iob5.dll
|
||||
static aioNMgrIob5_Create_t aioNMgrIob5_Create_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-iob5_y32.dll
|
||||
static aioIob5Y32d_Create_t aioIob5Y32d_Create_orig = nullptr;
|
||||
static aioIob5Y32d_GetDeviceStatus_t aioIob5Y32d_GetDeviceStatus_orig = nullptr;
|
||||
|
||||
/*
|
||||
* variables
|
||||
*/
|
||||
static AIO_IOB2_BI2X_AC1 *aioIob2Bi2xAc1;
|
||||
static AIO_NMGR_IOB2 *aioNmgrIob2;
|
||||
static AIO_NMGR_IOB5 *aioNmgrIob5;
|
||||
static AIO_IOB5_Y32D *aioIob5Y32d;
|
||||
static AIO_SCI_COMM *aioSciComm;
|
||||
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) {
|
||||
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();
|
||||
// struct may be misaligned
|
||||
o_DevStatus->Input.CN8_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN9_8 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN9_9 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Coin]) ? 0 : 0xFF;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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_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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
} else {
|
||||
return aioNMgrIob_BeginManage_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNCtlIob_GetNodeInfo(
|
||||
AIO_IOB5_Y32D *i_pNodeCtl, AIO_NMGR_IOB__NODEINFO *o_NodeInfo) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d) {
|
||||
memset(o_NodeInfo, 0, sizeof(AIO_NMGR_IOB__NODEINFO));
|
||||
} else {
|
||||
return aioNCtlIob_GetNodeInfo_orig(i_pNodeCtl, o_NodeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// libaio.dll
|
||||
|
||||
static AIO_SCI_COMM_T *__fastcall aioSciComm_Open(AIO_SCI_COMM_T *unk) {
|
||||
|
||||
aioSciComm = new AIO_SCI_COMM;
|
||||
memset(unk, 0, sizeof(AIO_SCI_COMM_T));
|
||||
unk->aioSciComm = aioSciComm;
|
||||
return unk;
|
||||
}
|
||||
|
||||
static void __fastcall aioSci_GetCommStatus(AIO_SCI_COMM *i_pNodeMgr, AIO_COMM_STATUS *i_Status) {
|
||||
|
||||
if (i_pNodeMgr == aioSciComm) {
|
||||
memset(i_Status, 0, sizeof(AIO_COMM_STATUS));
|
||||
} else {
|
||||
return aioSci_GetCommStatus_orig(i_pNodeMgr, i_Status);
|
||||
}
|
||||
}
|
||||
|
||||
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_IOB5 *i_pNodeMgr) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
|
||||
delete aioNmgrIob5;
|
||||
aioNmgrIob5 = nullptr;
|
||||
} else {
|
||||
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB5 *i_pNodeMgr) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeMgr_GetState_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeCtl_Destroy(AIO_IOB5_Y32D *i_pNodeCtl) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
delete aioIob5Y32d;
|
||||
aioIob5Y32d = nullptr;
|
||||
} else {
|
||||
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB5_Y32D *i_pNodeCtl) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeCtl_GetState_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsError(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeCtl_UpdateDevicesStatus() {
|
||||
}
|
||||
|
||||
// libaio-iob5.dll
|
||||
|
||||
static AIO_NMGR_IOB5 *__fastcall aioNMgrIob5_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||
|
||||
if (i_pSci == aioSciComm) {
|
||||
aioNmgrIob5 = new AIO_NMGR_IOB5;
|
||||
return aioNmgrIob5;
|
||||
} else {
|
||||
return aioNMgrIob5_Create_orig(i_pSci, i_bfMode);
|
||||
}
|
||||
}
|
||||
|
||||
// libaio-iob5_y32.dll
|
||||
static AIO_IOB5_Y32D *__fastcall aioIob5Y32d_Create(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode) {
|
||||
|
||||
if (i_pSci == aioNmgrIob5) {
|
||||
aioIob5Y32d = new AIO_IOB5_Y32D;
|
||||
return aioIob5Y32d;
|
||||
} else {
|
||||
return aioIob5Y32d_Create_orig(i_pSci, i_bfMode);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob5Y32d_GetDeviceStatus(AIO_IOB5_Y32D *i_pNodeCtl,
|
||||
AIO_IOB5_Y32D__DEVSTATUS *o_DevStatus) {
|
||||
|
||||
if (i_pNodeCtl != aioIob5Y32d) {
|
||||
return aioIob5Y32d_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus);
|
||||
}
|
||||
|
||||
memset(o_DevStatus, 0, sizeof(AIO_IOB5_Y32D__DEVSTATUS));
|
||||
}
|
||||
|
||||
void bi2x_hook_init() {
|
||||
|
||||
// avoid double init
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// announce
|
||||
log_info("bi2x_hook", "init");
|
||||
|
||||
// libaio-iob5_y32.dll
|
||||
const auto libaioIob5Y32Dll = "libaio-iob5_y32.dll";
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32d_Create",
|
||||
aioIob5Y32d_Create, &aioIob5Y32d_Create_orig);
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32i_Create",
|
||||
aioIob5Y32d_Create, &aioIob5Y32d_Create_orig);
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32d_GetDeviceStatus",
|
||||
aioIob5Y32d_GetDeviceStatus, &aioIob5Y32d_GetDeviceStatus_orig);
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32i_GetDeviceStatus",
|
||||
aioIob5Y32d_GetDeviceStatus, &aioIob5Y32d_GetDeviceStatus_orig);
|
||||
|
||||
// libaio-iob5.dll
|
||||
const auto libaioIob5Dll = "libaio-iob5.dll";
|
||||
detour::trampoline_try(libaioIob5Dll, "aioNMgrIob5_Create",
|
||||
aioNMgrIob5_Create, &aioNMgrIob5_Create_orig);
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
const auto libaioIob2VideoDll = "libaio-iob2_video.dll";
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_Create",
|
||||
aioIob2Bi2xAC1_Create, &aioIob2Bi2xAC1_Create_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_GetDeviceStatus",
|
||||
aioIob2Bi2xAC1_GetDeviceStatus, &aioIob2Bi2xAC1_GetDeviceStatus_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetWatchDogTimer",
|
||||
aioIob2Bi2xAC1_SetWatchDogTimer, &aioIob2Bi2xAC1_SetWatchDogTimer_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_AddCounter",
|
||||
aioIob2Bi2xAC1_AddCounter, &aioIob2Bi2xAC1_AddCounter_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetOutputData",
|
||||
aioIob2Bi2xAC1_SetOutputData, &aioIob2Bi2xAC1_SetOutputData_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetTapeLedDataPart",
|
||||
aioIob2Bi2xAC1_SetTapeLedDataPart, &aioIob2Bi2xAC1_SetTapeLedDataPart_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataLimit",
|
||||
aioIob2Bi2x_SetTapeLedDataLimit, &aioIob2Bi2x_SetTapeLedDataLimit_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?CreateWriteFirmContext@AIO_IOB2_BI2X@@SAPEAUWRFIRM@1@II@Z",
|
||||
aioIob2Bi2x_CreateWriteFirmContext, &aioIob2Bi2x_CreateWriteFirmContext_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?DestroyWriteFirmContext@AIO_IOB2_BI2X@@SAXPEAUWRFIRM@1@@Z",
|
||||
aioIob2Bi2x_DestroyWriteFirmContext, &aioIob2Bi2x_DestroyWriteFirmContext_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?WriteFirmGetState@AIO_IOB2_BI2X@@SAHPEAUWRFIRM@1@@Z",
|
||||
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?WriteFirmIsCompleted@AIO_IOB2_BI2X@@SA_NH@Z",
|
||||
aioIob2Bi2x_WriteFirmIsCompleted, &aioIob2Bi2x_WriteFirmIsCompleted_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, "?Open@AIO_SCI_COMM@@SA?AU?$AC_ERESULT_T@PEAVAIO_SCI_COMM@@@@PEBD0AEBUSETTING@1@@Z",
|
||||
aioSciComm_Open, &aioSciComm_Open_orig);
|
||||
detour::trampoline_try(libaioDll, "aioSci_GetCommStatus",
|
||||
aioSci_GetCommStatus, &aioSci_GetCommStatus_orig);
|
||||
detour::trampoline_try(libaioDll, "aioSci_Destroy",
|
||||
aioSci_Destroy, &aioSci_Destroy_orig);
|
||||
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);
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_UpdateDevicesStatus",
|
||||
aioNodeCtl_UpdateDevicesStatus, &aioNodeCtl_UpdateDevicesStatus_orig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace games::gitadora {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
class GitadoraDevice : public acioemu::ACIODeviceEmu {
|
||||
public:
|
||||
bool is_ready = false;
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "gitadora.h"
|
||||
|
||||
#include "handle.h"
|
||||
#include "bi2x_hook.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cfg/configurator.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/cpuutils.h"
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "hooks/setupapihook.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
@@ -214,6 +215,29 @@ namespace games::gitadora {
|
||||
system_module, "sys_debug_dip_set_param"));
|
||||
|
||||
#ifdef SPICE64
|
||||
// gitadora arena model
|
||||
auto aio = libutils::try_library("libaio.dll");
|
||||
if (aio != nullptr) {
|
||||
SETUPAPI_SETTINGS settings{};
|
||||
settings.class_guid[0] = 0x86E0D1E0;
|
||||
settings.class_guid[1] = 0x11D08089;
|
||||
settings.class_guid[2] = 0x0008E49C;
|
||||
settings.class_guid[3] = 0x731F303E;
|
||||
const char property[] = "1CCF(8050)_000";
|
||||
const char property_hardwareid[] = "USB\\VID_1CCF&PID_8050&MI_00\\000";
|
||||
memcpy(settings.property_devicedesc, property, sizeof(property));
|
||||
memcpy(settings.property_hardwareid, property_hardwareid, sizeof(property_hardwareid));
|
||||
setupapihook_init(avs::game::DLL_INSTANCE);
|
||||
setupapihook_add(settings);
|
||||
|
||||
// Gitadora IO(J32D/J33I) board emulation
|
||||
devicehook_init(avs::game::DLL_INSTANCE);
|
||||
devicehook_add(new GitaDoraSerialHandle());
|
||||
|
||||
// test/service/coin buttons
|
||||
bi2x_hook_init();
|
||||
return;
|
||||
}
|
||||
|
||||
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@ namespace games::gitadora {
|
||||
static inline bool is_drum() {
|
||||
return (
|
||||
avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B')
|
||||
(avs::game::is_model("M32") && (avs::game::SPEC[0] == 'B' || avs::game::SPEC[0] == 'D'))
|
||||
);
|
||||
}
|
||||
static inline bool is_guitar() {
|
||||
return (
|
||||
avs::game::is_model({ "J33", "K33", "L33" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'A')
|
||||
(avs::game::is_model("M32") && (avs::game::SPEC[0] == 'A' || avs::game::SPEC[0] == 'C'))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "handle.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/time.h"
|
||||
#include "io.h"
|
||||
#include "gitadora.h"
|
||||
#include "j32d.h"
|
||||
#include "j33i.h"
|
||||
#include "device.h"
|
||||
|
||||
|
||||
using namespace acioemu;
|
||||
static games::gitadora::GitadoraDevice *device;
|
||||
|
||||
bool games::gitadora::GitaDoraSerialHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"COM1") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_drum = games::gitadora::is_drum();
|
||||
|
||||
if (is_drum) {
|
||||
device = new J32DSerialDevice();
|
||||
} else {
|
||||
device = new J33ISerialDevice();
|
||||
}
|
||||
|
||||
acio_emu.add_device(device);
|
||||
log_info("gitadora", "Opened COM1 ({} DEVICE)", is_drum ? "J32D" : "J33I");
|
||||
return true;
|
||||
}
|
||||
|
||||
int games::gitadora::GitaDoraSerialHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
|
||||
// the game does not send cmd 0x12F to the IO
|
||||
// but it is expected to receive 0x12F for user input
|
||||
// so i manually created a payload so that parse_msg can be used
|
||||
// to write to the response_buffer
|
||||
if (device->is_ready) {
|
||||
uint8_t chk = 0;
|
||||
MessageData* msg = ACIODeviceEmu::create_msg(0x1, 0x12F, 0, 0);
|
||||
msg->addr = 0x1;
|
||||
auto write_buffer = reinterpret_cast<uint8_t *>(msg);
|
||||
while (acio_emu.read() != std::nullopt); // there may be old stuffs in the buffer
|
||||
acio_emu.write(0xAA);
|
||||
acio_emu.write(0xAA);
|
||||
|
||||
for(DWORD i = 0; i < 5; i++) {
|
||||
chk += write_buffer[i];
|
||||
acio_emu.write(write_buffer[i]);
|
||||
}
|
||||
// direct copy and paste from write_msg
|
||||
if (chk == ACIO_SOF || chk == ACIO_ESCAPE) {
|
||||
acio_emu.write(ACIO_ESCAPE);
|
||||
acio_emu.write(~chk);
|
||||
} else {
|
||||
acio_emu.write(chk);
|
||||
}
|
||||
}
|
||||
|
||||
// read from emu
|
||||
DWORD bytes_read = 0;
|
||||
while (bytes_read < nNumberOfBytesToRead) {
|
||||
auto cur_byte = acio_emu.read();
|
||||
|
||||
if (cur_byte.has_value()) {
|
||||
buffer[bytes_read++] = cur_byte.value();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Read IO Data: {}", bin2hex(buffer, bytes_read));
|
||||
#endif
|
||||
// return amount of bytes read
|
||||
return (int)bytes_read;
|
||||
}
|
||||
|
||||
|
||||
int games::gitadora::GitaDoraSerialHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite)
|
||||
{
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
// write to emu
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
acio_emu.write(buffer[i]);
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Write IO Data: {}", bin2hex(buffer, nNumberOfBytesToWrite));
|
||||
#endif
|
||||
// return all data written
|
||||
return (int)nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int games::gitadora::GitaDoraSerialHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer, DWORD nOutBufferSize) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool games::gitadora::GitaDoraSerialHandle::close() {
|
||||
|
||||
log_info("gitadora", "Closed COM1");
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
class GitaDoraSerialHandle : public CustomHandle {
|
||||
private:
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
bool close() override;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
#include "io.h"
|
||||
#include "j32d.h"
|
||||
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
games::gitadora::J32DSerialDevice::J32DSerialDevice() {
|
||||
this->node_count = 1;
|
||||
log_info("gitadora", "J32D device created.");
|
||||
}
|
||||
|
||||
bool games::gitadora::J32DSerialDevice::parse_msg(
|
||||
MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "GTDR ADDR: {}, CMD: 0x{:04x}", msg_in->addr, msg_in->cmd.code);
|
||||
#endif
|
||||
|
||||
static uint8_t pid = 0;
|
||||
// check command
|
||||
switch (msg_in->cmd.code) {
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
this->set_version(msg, 0x20a, 0, 0, 1, 0, "J32D");
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_STARTUP:
|
||||
case ACIO_CMD_CLEAR:
|
||||
case 0x0120: {
|
||||
if (msg_in->cmd.code == 0x0120) {
|
||||
pid = msg_in->cmd.pid;
|
||||
this->is_ready = true;
|
||||
}
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
// IO
|
||||
case 0x012F: {
|
||||
auto msg = this->create_msg(msg_in, 0x11);
|
||||
msg->cmd.pid = pid;
|
||||
// don't know if it's needed/correct,
|
||||
// without it the game seems to work just fine
|
||||
pid += 1;
|
||||
J32D_IO_STATUS payload;
|
||||
memset(&payload, 0, sizeof(J32D_IO_STATUS));
|
||||
auto &buttons = get_buttons();
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiTom])) {
|
||||
payload.high_tom = 4092;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLowTom])) {
|
||||
payload.low_tom = 4092;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumSnare])) {
|
||||
payload.snare = 4092;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumFloorTom])) {
|
||||
payload.floor_tom = 4092;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLeftCymbal])) {
|
||||
payload.left_cymbal = 4092;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumRightCymbal])) {
|
||||
payload.right_cymbal = 4092;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHat]) ||
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHatClosed]) ||
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHatHalfOpen])) {
|
||||
payload.hihat = 4092;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumBassPedal])) {
|
||||
payload.pedals |= 1;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLeftPedal])) {
|
||||
payload.pedals |= 2;
|
||||
}
|
||||
|
||||
memcpy(msg->cmd.raw, &payload, sizeof(J32D_IO_STATUS));
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Unhandled cmd {} received.", msg_in->cmd.code);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "acioemu/device.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
class J32DSerialDevice : public GitadoraDevice {
|
||||
private:
|
||||
uint8_t coin_counter = 0;
|
||||
public:
|
||||
J32DSerialDevice();
|
||||
bool parse_msg(acioemu::MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) override;
|
||||
struct J32D_IO_STATUS {
|
||||
uint16_t high_tom;
|
||||
uint16_t low_tom;
|
||||
uint16_t snare;
|
||||
uint16_t floor_tom;
|
||||
uint16_t left_cymbal;
|
||||
uint16_t right_cymbal;
|
||||
uint16_t hihat;
|
||||
uint16_t unk7;
|
||||
uint8_t pedals;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/time.h"
|
||||
#include "io.h"
|
||||
#include "j33i.h"
|
||||
|
||||
|
||||
#define GUITAR_BTN_R 0x7
|
||||
#define GUITAR_BTN_G 0x6
|
||||
#define GUITAR_BTN_B 0x5
|
||||
#define GUITAR_BTN_Y 0x4
|
||||
#define GUITAR_BTN_P 0x3
|
||||
#define GUITAR_PICK_DOWN 0xC
|
||||
#define GUITAR_PICK_UP 0xD
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
games::gitadora::J33ISerialDevice::J33ISerialDevice() {
|
||||
this->node_count = 1;
|
||||
log_info("gitadora", "J33I device created.");
|
||||
}
|
||||
|
||||
bool games::gitadora::J33ISerialDevice::parse_msg(
|
||||
MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "GTDR ADDR: {}, CMD: 0x{:04x}", msg_in->addr, msg_in->cmd.code);
|
||||
#endif
|
||||
|
||||
static uint8_t pid = 0;
|
||||
// check command
|
||||
switch (msg_in->cmd.code)
|
||||
{
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
this->set_version(msg, 0x10a, 0, 1, 0, 2, "J33I");
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_STARTUP:
|
||||
case ACIO_CMD_CLEAR:
|
||||
case 0x0120: {
|
||||
if (msg_in->cmd.code == 0x0120) {
|
||||
pid = msg_in->cmd.pid;
|
||||
this->is_ready = true;
|
||||
}
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
// IO
|
||||
case 0x012F: {
|
||||
auto msg = this->create_msg(msg_in, 0x8);
|
||||
msg->cmd.pid = pid;
|
||||
pid += 1;
|
||||
J33I_IO_STATUS payload;
|
||||
memset(&payload, 0, sizeof(J33I_IO_STATUS));
|
||||
|
||||
bool picked = false;
|
||||
|
||||
auto &buttons = get_buttons();
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1R])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_R;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1G])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_G;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1B])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_B;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1Y])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_Y;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1P])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_P;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickUp])) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
picked = true;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickDown]) && !picked) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||
}
|
||||
|
||||
memcpy(msg->cmd.raw, &payload, sizeof(J33I_IO_STATUS));
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Unhandled cmd {} received.", msg_in->cmd.code);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/device.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
class J33ISerialDevice : public GitadoraDevice {
|
||||
private:
|
||||
uint8_t coin_counter = 0;
|
||||
public:
|
||||
J33ISerialDevice();
|
||||
bool parse_msg(acioemu::MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) override;
|
||||
|
||||
// there may be more, but the game doesn't seem to care
|
||||
// TODO: add support for gyroscope, knob and wail
|
||||
struct J33I_IO_STATUS {
|
||||
uint16_t gyroscope_x;
|
||||
uint16_t gyroscope_y;
|
||||
uint16_t gyroscope_z;
|
||||
uint16_t buttons;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -399,6 +399,12 @@ static BOOL WINAPI ClearCommError_hook(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT
|
||||
lpStat->cbInQue = custom_handle->bytes_available();
|
||||
}
|
||||
|
||||
// gitadora arena model needs this, or else
|
||||
// the game will keep spamming 0xAA
|
||||
if (avs::game::is_model("M32") && (avs::game::SPEC[0] == 'C' || avs::game::SPEC[0] == 'D') && lpErrors) {
|
||||
*lpErrors = 0;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
#include "misc/device.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/extdev.h"
|
||||
#include "misc/ami2000.h"
|
||||
#include "misc/sciunit.h"
|
||||
#include "misc/sde.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
@@ -195,6 +196,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
bool attach_icca = false;
|
||||
bool attach_device = false;
|
||||
bool attach_extdev = false;
|
||||
bool attach_ami2000 = false;
|
||||
bool attach_sciunit = false;
|
||||
bool attach_cpusbxpkm_printer = false;
|
||||
bool attach_iidx = false;
|
||||
@@ -428,8 +430,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::EnableEXTDEVModule].value_bool()) {
|
||||
attach_extdev = true;
|
||||
}
|
||||
if (options[launcher::Options::EnableSCIUNITModule].value_bool()) {
|
||||
attach_sciunit = true;
|
||||
if (options[launcher::Options::EnableAMI2000Module].value_bool()) {
|
||||
attach_ami2000 = true;
|
||||
}
|
||||
if (options[launcher::Options::EnableDevicePassthrough].value_bool()) {
|
||||
hooks::device::ENABLE = false;
|
||||
@@ -1640,6 +1642,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
attach_io = true;
|
||||
attach_device = true;
|
||||
attach_extdev = true;
|
||||
attach_ami2000 = true;
|
||||
attach_gitadora = true;
|
||||
break;
|
||||
}
|
||||
@@ -2202,6 +2205,11 @@ int main_implementation(int argc, char *argv[]) {
|
||||
extdev_attach();
|
||||
}
|
||||
|
||||
// ami 2000 card reader attach
|
||||
if (attach_io || attach_ami2000) {
|
||||
ami2000_attach();
|
||||
}
|
||||
|
||||
// sci unit attach
|
||||
if (attach_io || attach_sciunit) {
|
||||
sciunit_attach();
|
||||
|
||||
@@ -1321,6 +1321,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Bool,
|
||||
.category = "I/O Modules",
|
||||
},
|
||||
{
|
||||
.title = "Enable AMI2000 Module",
|
||||
.name = "ami2000",
|
||||
.desc = "Manually enable AMI2000 emulation",
|
||||
.type = OptionType::Bool,
|
||||
.category = "I/O Modules",
|
||||
},
|
||||
{
|
||||
.title = "Enable SCIUNIT Module",
|
||||
.name = "sciunit",
|
||||
|
||||
@@ -147,6 +147,7 @@ namespace launcher {
|
||||
EnableICCAModule,
|
||||
EnableDEVICEModule,
|
||||
EnableEXTDEVModule,
|
||||
EnableAMI2000Module,
|
||||
EnableSCIUNITModule,
|
||||
EnableDevicePassthrough,
|
||||
ForceWinTouch,
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "ami2000.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "acio/icca/icca.h"
|
||||
|
||||
|
||||
static int CARD_TYPE = 0;
|
||||
static uint8_t CARD_UID[8];
|
||||
|
||||
static void __fastcall AMI2000_Initialize() {
|
||||
log_info("ami2000", "ami2000 initialize.");
|
||||
}
|
||||
|
||||
static void __fastcall AMI2000_Startup() {
|
||||
log_info("ami2000", "ami2000 startup.");
|
||||
}
|
||||
|
||||
static void __fastcall AMI2000_Cleanup() {
|
||||
log_info("ami2000", "ami2000 cleanup.");
|
||||
}
|
||||
|
||||
static int __fastcall AMI2000_IsDeviceError() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __fastcall AMI2000_ReadCtrl() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *__fastcall AMI2000_GetRomVersion() {
|
||||
return "DUMMY";
|
||||
}
|
||||
|
||||
static int __fastcall AMI2000_GetCardType() {
|
||||
return CARD_TYPE;
|
||||
}
|
||||
|
||||
static uint64_t __fastcall AMI2000_GetCardIdentifier() {
|
||||
if (eamuse_card_insert_consume(1, 0)) {
|
||||
eamuse_get_card(1, 0, CARD_UID);
|
||||
CARD_TYPE = is_card_uid_felica(CARD_UID) ? 2 : 1;
|
||||
log_info("ami2000", "AMI2000_GetCardIdentifier: {:X}", bswap64(*(uint64_t*)CARD_UID));
|
||||
} else {
|
||||
CARD_TYPE = 0;
|
||||
return 0;
|
||||
}
|
||||
return bswap64(*(uint64_t *)CARD_UID);
|
||||
}
|
||||
|
||||
|
||||
void ami2000_attach() {
|
||||
auto ami2000 = libutils::try_module("iccr-ami2000-api.dll");
|
||||
|
||||
if (!ami2000) {
|
||||
log_warning("ami2000", "module not found, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
// card unit
|
||||
detour::inline_hook((void *)AMI2000_Initialize, libutils::try_proc(
|
||||
ami2000, "AMI2000_Initialize"));
|
||||
detour::inline_hook((void *)AMI2000_Startup, libutils::try_proc(
|
||||
ami2000, "AMI2000_Startup"));
|
||||
detour::inline_hook((void *)AMI2000_IsDeviceError, libutils::try_proc(
|
||||
ami2000, "AMI2000_IsDeviceError"));
|
||||
detour::inline_hook((void *)AMI2000_ReadCtrl, libutils::try_proc(
|
||||
ami2000, "AMI2000_ReadCtrl"));
|
||||
detour::inline_hook((void *)AMI2000_Cleanup, libutils::try_proc(
|
||||
ami2000, "AMI2000_Cleanup"));
|
||||
detour::inline_hook((void *)AMI2000_GetRomVersion, libutils::try_proc(
|
||||
ami2000, "AMI2000_GetRomVersion"));
|
||||
detour::inline_hook((void *)AMI2000_GetCardType, libutils::try_proc(
|
||||
ami2000, "AMI2000_GetCardType"));
|
||||
detour::inline_hook((void *)AMI2000_GetCardIdentifier, libutils::try_proc(
|
||||
ami2000, "AMI2000_GetCardIdentifier"));
|
||||
|
||||
log_info("ami2000", "attached");
|
||||
}
|
||||
|
||||
void ami2000_detach()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void ami2000_attach();
|
||||
void ami2000_detach();
|
||||
@@ -351,3 +351,22 @@ static inline std::string wchar_to_u8(const wchar_t* wstr) {
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, out.data(), size - 1, nullptr, nullptr);
|
||||
return out;
|
||||
}
|
||||
|
||||
// convert litte vs big endian
|
||||
inline uint64_t bswap64(uint64_t x) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(x);
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
return _byteswap_uint64(x);
|
||||
#endif
|
||||
|
||||
return (x >> 56) |
|
||||
((x >> 40) & 0x000000000000FF00ULL) |
|
||||
((x >> 24) & 0x0000000000FF0000ULL) |
|
||||
((x >> 8) & 0x00000000FF000000ULL) |
|
||||
((x << 8) & 0x000000FF00000000ULL) |
|
||||
((x << 24) & 0x0000FF0000000000ULL) |
|
||||
((x << 40) & 0x00FF000000000000ULL) |
|
||||
(x << 56);
|
||||
}
|
||||
Reference in New Issue
Block a user