mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 14:50:41 -07:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8de7e5a9e4 | |||
| b8e99e75e5 | |||
| 46dc22dd63 | |||
| 69d78e0a87 | |||
| ab3d41432a | |||
| 73e10cac0c | |||
| c6242233a7 | |||
| 6b8335780f | |||
| 21f469b260 | |||
| 07a0df5fdc | |||
| d4f1768bbd | |||
| 96e7e4949f | |||
| 24d3062c66 | |||
| 6cd212aac2 | |||
| 064031bc12 | |||
| 4c8d4ebab6 | |||
| 462a07680b | |||
| 081def30db | |||
| 6776b08bce | |||
| 6cb507f4fa | |||
| de05af052f |
@@ -49,6 +49,8 @@ Watch out for legacy OS compatibility. Currently, the minimum support floor is W
|
|||||||
|
|
||||||
### Code style requirements
|
### Code style requirements
|
||||||
* Indents are four spaces.
|
* Indents are four spaces.
|
||||||
|
* Try to keep under 100 characters per line. This includes comments and string literals.
|
||||||
|
* Avoid making unnecessary formatting or whitespace changes in a PR; e.g., watch out for text editor automatically stripping trailing whitespace. It pollutes the diff and slows down code review.
|
||||||
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
||||||
|
|
||||||
OK:
|
OK:
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static")
|
||||||
else()
|
else()
|
||||||
|
|
||||||
|
# warnings as errors
|
||||||
|
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
|
||||||
|
|
||||||
# warnings
|
# warnings
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # enable stuff
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # enable stuff
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pointer-arith") # but we love pointer arithmetic :)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pointer-arith") # but we love pointer arithmetic :)
|
||||||
@@ -398,6 +401,10 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/nost/poke.cpp
|
games/nost/poke.cpp
|
||||||
games/gitadora/gitadora.cpp
|
games/gitadora/gitadora.cpp
|
||||||
games/gitadora/io.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/mga.cpp
|
||||||
games/mga/io.cpp
|
games/mga/io.cpp
|
||||||
games/mga/gunio.cpp
|
games/mga/gunio.cpp
|
||||||
@@ -470,10 +477,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
hooks/audio/acm.cpp
|
hooks/audio/acm.cpp
|
||||||
hooks/audio/audio.cpp
|
hooks/audio/audio.cpp
|
||||||
hooks/audio/buffer.cpp
|
hooks/audio/buffer.cpp
|
||||||
|
hooks/audio/mme.cpp
|
||||||
hooks/audio/util.cpp
|
hooks/audio/util.cpp
|
||||||
hooks/audio/backends/dsound/dsound_backend.cpp
|
hooks/audio/backends/dsound/dsound_backend.cpp
|
||||||
hooks/audio/backends/mmdevice/audio_endpoint_volume.cpp
|
hooks/audio/backends/mmdevice/audio_endpoint_volume.cpp
|
||||||
hooks/audio/backends/mmdevice/device.cpp
|
hooks/audio/backends/mmdevice/device.cpp
|
||||||
|
hooks/audio/backends/mmdevice/device_collection.cpp
|
||||||
hooks/audio/backends/mmdevice/device_enumerator.cpp
|
hooks/audio/backends/mmdevice/device_enumerator.cpp
|
||||||
hooks/audio/backends/wasapi/audio_client.cpp
|
hooks/audio/backends/wasapi/audio_client.cpp
|
||||||
hooks/audio/backends/wasapi/audio_render_client.cpp
|
hooks/audio/backends/wasapi/audio_render_client.cpp
|
||||||
@@ -530,6 +539,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
misc/sciunit.cpp
|
misc/sciunit.cpp
|
||||||
misc/sde.cpp
|
misc/sde.cpp
|
||||||
misc/wintouchemu.cpp
|
misc/wintouchemu.cpp
|
||||||
|
misc/ami2000.cpp
|
||||||
|
|
||||||
# nvapi
|
# nvapi
|
||||||
nvapi/nvapi.cpp
|
nvapi/nvapi.cpp
|
||||||
|
|||||||
@@ -1108,6 +1108,8 @@ namespace avs {
|
|||||||
|
|
||||||
// SOUND VOLTEX
|
// SOUND VOLTEX
|
||||||
{"soundvoltex.dll", 0x10000000},
|
{"soundvoltex.dll", 0x10000000},
|
||||||
|
// GITADORA
|
||||||
|
{"gdxg.dll", 0x2000000},
|
||||||
#endif
|
#endif
|
||||||
// jubeat
|
// jubeat
|
||||||
{"jubeat.dll", 0x2000000},
|
{"jubeat.dll", 0x2000000},
|
||||||
|
|||||||
@@ -494,7 +494,9 @@ namespace avs {
|
|||||||
std::ostringstream init_code;
|
std::ostringstream init_code;
|
||||||
init_code << EA3_MODEL;
|
init_code << EA3_MODEL;
|
||||||
init_code << EA3_DEST;
|
init_code << EA3_DEST;
|
||||||
if (strcmp(EA3_MODEL, "MDX") == 0 && strcmp(EA3_SPEC, "I") == 0) {
|
if (avs::game::DLL_NAME == "arkmdxbio2.dll" &&
|
||||||
|
strcmp(EA3_MODEL, "MDX") == 0 && strcmp(EA3_SPEC, "I") == 0) {
|
||||||
|
// ddr gold cab
|
||||||
init_code << "G";
|
init_code << "G";
|
||||||
init_code << "B";
|
init_code << "B";
|
||||||
} else {
|
} else {
|
||||||
@@ -529,7 +531,7 @@ namespace avs {
|
|||||||
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
||||||
|
|
||||||
// call the game init
|
// call the game init
|
||||||
log_info("avs-ea3", "calling entry init");
|
log_info("avs-ea3", "calling entry init (init code = {})", init_code_str);
|
||||||
if (!avs::game::entry_init(init_code_str.data(), app_param)) {
|
if (!avs::game::entry_init(init_code_str.data(), app_param)) {
|
||||||
log_fatal("avs-ea3", "entry init failed :(");
|
log_fatal("avs-ea3", "entry init failed :(");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,28 +253,25 @@ namespace games::ddr {
|
|||||||
if(((dwIoControlCode>>16)&0xFFFF) == FILE_DEVICE_UNKNOWN) {
|
if(((dwIoControlCode>>16)&0xFFFF) == FILE_DEVICE_UNKNOWN) {
|
||||||
switch((dwIoControlCode>>2)&0x3FFF) {
|
switch((dwIoControlCode>>2)&0x3FFF) {
|
||||||
case P4IO_IOCTL_GET_INPUTS: {
|
case P4IO_IOCTL_GET_INPUTS: {
|
||||||
|
|
||||||
|
// Prevents this function from being called at its normal 2000-2500 kHz cadence and overloading the CPU, instead reduces it to 250 Hz
|
||||||
|
Sleep(4);
|
||||||
|
|
||||||
memset(lpOutBuffer, 0, 16);
|
memset(lpOutBuffer, 0, 16);
|
||||||
auto controls = (uint32_t*) lpOutBuffer;
|
auto controls = (uint32_t*) lpOutBuffer;
|
||||||
|
|
||||||
const std::pair<size_t, uint8_t> buttonMapping[] =
|
const std::pair<size_t, uint8_t> buttonMapping[] =
|
||||||
{
|
{
|
||||||
|
// Panel input comes from MDXF Control Status Buffer functions
|
||||||
{Buttons::SERVICE, 25}, //Good
|
{Buttons::SERVICE, 25}, //Good
|
||||||
{Buttons::TEST, 28}, //Good
|
{Buttons::TEST, 28}, //Good
|
||||||
{Buttons::COIN_MECH, 24}, //Good
|
{Buttons::COIN_MECH, 24}, //Good
|
||||||
{Buttons::P1_START, 0}, //Good
|
{Buttons::P1_START, 0}, //Good
|
||||||
{Buttons::P1_PANEL_UP, 5}, //Good
|
|
||||||
{Buttons::P1_PANEL_DOWN, 6}, //Good
|
|
||||||
{Buttons::P1_PANEL_LEFT, 7}, //Goood
|
|
||||||
{Buttons::P1_PANEL_RIGHT, 16}, //Good
|
|
||||||
{Buttons::P1_MENU_UP, 1}, //Good
|
{Buttons::P1_MENU_UP, 1}, //Good
|
||||||
{Buttons::P1_MENU_DOWN, 2}, //Good
|
{Buttons::P1_MENU_DOWN, 2}, //Good
|
||||||
{Buttons::P1_MENU_LEFT, 3}, //Good
|
{Buttons::P1_MENU_LEFT, 3}, //Good
|
||||||
{Buttons::P1_MENU_RIGHT, 4}, //Good
|
{Buttons::P1_MENU_RIGHT, 4}, //Good
|
||||||
{Buttons::P2_START, 8}, //Good
|
{Buttons::P2_START, 8}, //Good
|
||||||
{Buttons::P2_PANEL_UP, 13}, //Good
|
|
||||||
{Buttons::P2_PANEL_DOWN, 14}, //Good
|
|
||||||
{Buttons::P2_PANEL_LEFT, 15}, //Good
|
|
||||||
{Buttons::P2_PANEL_RIGHT, 20}, //Good
|
|
||||||
{Buttons::P2_MENU_UP, 9}, //Good
|
{Buttons::P2_MENU_UP, 9}, //Good
|
||||||
{Buttons::P2_MENU_DOWN, 10}, //Good
|
{Buttons::P2_MENU_DOWN, 10}, //Good
|
||||||
{Buttons::P2_MENU_LEFT, 11}, //Good
|
{Buttons::P2_MENU_LEFT, 11}, //Good
|
||||||
|
|||||||
@@ -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;
|
||||||
|
o_DevStatus->Input.CN12_14 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]) ? 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,14 +1,16 @@
|
|||||||
#include "gitadora.h"
|
#include "gitadora.h"
|
||||||
|
#include "handle.h"
|
||||||
|
#include "bi2x_hook.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
|
#include "hooks/audio/mme.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "util/cpuutils.h"
|
#include "util/cpuutils.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
|
#include "hooks/setupapihook.h"
|
||||||
|
|
||||||
namespace games::gitadora {
|
namespace games::gitadora {
|
||||||
|
|
||||||
@@ -16,6 +18,32 @@ namespace games::gitadora {
|
|||||||
bool TWOCHANNEL = false;
|
bool TWOCHANNEL = false;
|
||||||
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevent GitaDora from creating folders on F drive
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef SPICE64
|
||||||
|
static DWORD WINAPI GetLogicalDrives_hook() {
|
||||||
|
return GetLogicalDrives() | 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT WINAPI GetDriveTypeA_hook(LPCSTR lpRootPathName) {
|
||||||
|
if (!strcmp(lpRootPathName, "F:\\")) {
|
||||||
|
return DRIVE_FIXED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetDriveTypeA(lpRootPathName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL WINAPI CreateDirectoryA_hook(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) {
|
||||||
|
if (!strncmp(lpPathName, "F:/", 3)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateDirectoryA(lpPathName, lpSecurityAttributes);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GitaDora checks if the IP address has changed, and if it has it throws 5-1506-0000 like jubeat.
|
* GitaDora checks if the IP address has changed, and if it has it throws 5-1506-0000 like jubeat.
|
||||||
* We don't want this so we patch it out.
|
* We don't want this so we patch it out.
|
||||||
@@ -214,6 +242,37 @@ namespace games::gitadora {
|
|||||||
system_module, "sys_debug_dip_set_param"));
|
system_module, "sys_debug_dip_set_param"));
|
||||||
|
|
||||||
#ifdef SPICE64
|
#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();
|
||||||
|
|
||||||
|
// f: drive hook
|
||||||
|
detour::iat_try("GetLogicalDrives", GetLogicalDrives_hook, avs::game::DLL_INSTANCE);
|
||||||
|
detour::iat_try("GetDriveTypeA", GetDriveTypeA_hook, avs::game::DLL_INSTANCE);
|
||||||
|
detour::iat_try("CreateDirectoryA", CreateDirectoryA_hook, avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// volume change prevention
|
||||||
|
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,20 @@ namespace games::gitadora {
|
|||||||
static inline bool is_drum() {
|
static inline bool is_drum() {
|
||||||
return (
|
return (
|
||||||
avs::game::is_model({ "J32", "K32", "L32" }) ||
|
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() {
|
static inline bool is_guitar() {
|
||||||
return (
|
return (
|
||||||
avs::game::is_model({ "J33", "K33", "L33" }) ||
|
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'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_arena_model() {
|
||||||
|
return (
|
||||||
|
avs::game::is_model("M32") &&
|
||||||
|
(avs::game::SPEC[0] == 'C' || avs::game::SPEC[0] == 'D')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
|||||||
"Service",
|
"Service",
|
||||||
"Test",
|
"Test",
|
||||||
"Coin",
|
"Coin",
|
||||||
|
"Headphone",
|
||||||
"Guitar P1 Start",
|
"Guitar P1 Start",
|
||||||
"Guitar P1 Up",
|
"Guitar P1 Up",
|
||||||
"Guitar P1 Down",
|
"Guitar P1 Down",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace games::gitadora {
|
|||||||
Service,
|
Service,
|
||||||
Test,
|
Test,
|
||||||
Coin,
|
Coin,
|
||||||
|
Headphone,
|
||||||
GuitarP1Start,
|
GuitarP1Start,
|
||||||
GuitarP1Up,
|
GuitarP1Up,
|
||||||
GuitarP1Down,
|
GuitarP1Down,
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
#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();
|
||||||
|
auto high_tom = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiTom]) * 1023);
|
||||||
|
auto low_tom = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLowTom]) * 1023);
|
||||||
|
auto snare = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumSnare]) * 1023);
|
||||||
|
auto floor_tom = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumFloorTom]) * 1023);
|
||||||
|
auto left_cymbal = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLeftCymbal]) * 1023);
|
||||||
|
auto right_cymbal = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumRightCymbal]) * 1023);
|
||||||
|
auto hihat = (uint16_t) ((GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHat]) ||
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHatClosed]) ||
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHatHalfOpen])) * 1023);
|
||||||
|
|
||||||
|
#define ENCODE_ANALOG(__val) (((__val & 0x3f) << 10) | ((__val & 0x3c0) >> 6))
|
||||||
|
payload.high_tom = ENCODE_ANALOG(high_tom);
|
||||||
|
payload.low_tom = ENCODE_ANALOG(low_tom);
|
||||||
|
payload.snare = ENCODE_ANALOG(snare);
|
||||||
|
payload.floor_tom = ENCODE_ANALOG(floor_tom);
|
||||||
|
payload.left_cymbal = ENCODE_ANALOG(left_cymbal);
|
||||||
|
payload.right_cymbal = ENCODE_ANALOG(right_cymbal);
|
||||||
|
payload.hihat = ENCODE_ANALOG(hihat);
|
||||||
|
#undef ENCODE_ANALOG
|
||||||
|
|
||||||
|
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,160 @@
|
|||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
// why this number?? no clue, but this is what the test menu shows as the max
|
||||||
|
// value in the green box with the red dot; the value display itself can go
|
||||||
|
// higher but the red dot will reach the edge at +-189
|
||||||
|
#define ARENA_MODEL_WAIL_MAX 189
|
||||||
|
|
||||||
|
static bool GFDM_GF_PICK_STATE_UP;
|
||||||
|
static bool GFDM_GF_PICK_STATE_DOWN;
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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])) {
|
||||||
|
if (!GFDM_GF_PICK_STATE_UP) {
|
||||||
|
GFDM_GF_PICK_STATE_UP = true;
|
||||||
|
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GFDM_GF_PICK_STATE_UP = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickDown])) {
|
||||||
|
if (!GFDM_GF_PICK_STATE_DOWN) {
|
||||||
|
GFDM_GF_PICK_STATE_DOWN = true;
|
||||||
|
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GFDM_GF_PICK_STATE_DOWN = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &analogs = get_analogs();
|
||||||
|
|
||||||
|
// get x,y,z analog values [-0.5f, 0.5f], centered at 0.f
|
||||||
|
float x = 0.f;
|
||||||
|
auto x_analog = analogs[Analogs::GuitarP1WailX];
|
||||||
|
if (x_analog.isSet()) {
|
||||||
|
x = GameAPI::Analogs::getState(RI_MGR, x_analog) - 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float y = 0.f;
|
||||||
|
auto y_analog = analogs[Analogs::GuitarP1WailY];
|
||||||
|
if (y_analog.isSet()) {
|
||||||
|
y = GameAPI::Analogs::getState(RI_MGR, y_analog) - 0.5f;
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailUp])) {
|
||||||
|
y = -0.5f;
|
||||||
|
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailDown])) {
|
||||||
|
y = 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float z = 0.f;
|
||||||
|
auto z_analog = analogs[Analogs::GuitarP1WailZ];
|
||||||
|
if (z_analog.isSet()) {
|
||||||
|
z = GameAPI::Analogs::getState(RI_MGR, z_analog) - 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert x,y,z to unsigned integer values that the game expects
|
||||||
|
const uint16_t x_int = x * ARENA_MODEL_WAIL_MAX * 2;
|
||||||
|
const uint16_t y_int = y * ARENA_MODEL_WAIL_MAX * 2;
|
||||||
|
const uint16_t z_int = z * ARENA_MODEL_WAIL_MAX * 2;
|
||||||
|
payload.gyroscope_x = static_cast<uint16_t>(((x_int >> 3) & (0xFF)) | ((x_int & 0x07) << 12));
|
||||||
|
payload.gyroscope_y = static_cast<uint16_t>(((y_int >> 3) & (0xFF)) | ((y_int & 0x07) << 12));
|
||||||
|
payload.gyroscope_z = static_cast<uint16_t>(((z_int >> 3) & (0xFF)) | ((z_int & 0x07) << 12));
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -195,11 +195,11 @@ bool games::iidx::IIDXFMSerialHandle::open(LPCWSTR lpFileName) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("iidx", "Opened COM2 (FM DEVICE)");
|
log_info("iidx", "Opened COM2 (FM DEVICE / bi2a)");
|
||||||
|
games::iidx::update_io_emulation_state(games::iidx::iidx_aio_emulation_state::bi2a_com2);
|
||||||
|
|
||||||
// ACIO device
|
// ACIO device
|
||||||
acio_emu.add_device(new FMSerialDevice());
|
acio_emu.add_device(new FMSerialDevice());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ int games::iidx::IIDXFMSerialHandle::device_io(DWORD dwIoControlCode, LPVOID lpI
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool games::iidx::IIDXFMSerialHandle::close() {
|
bool games::iidx::IIDXFMSerialHandle::close() {
|
||||||
log_info("iidx", "Closed COM2 (FM DEVICE)");
|
log_info("iidx", "Closed COM2 (FM DEVICE / bi2a)");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,11 @@ namespace games::iidx {
|
|||||||
|
|
||||||
static AIO_IOB2_BI2X_TDJ* __fastcall aioIob2Bi2xTDJ_Create(AIO_NMGR_IOB2 *nmgr, int a2) {
|
static AIO_IOB2_BI2X_TDJ* __fastcall aioIob2Bi2xTDJ_Create(AIO_NMGR_IOB2 *nmgr, int a2) {
|
||||||
if (!BI2X_PASSTHROUGH) {
|
if (!BI2X_PASSTHROUGH) {
|
||||||
|
|
||||||
|
log_info("bi2x_hook", "aioIob2Bi2xTDJ_Create called (TDJ I/O emulation active)");
|
||||||
|
games::iidx::update_io_emulation_state(
|
||||||
|
games::iidx::iidx_aio_emulation_state::bi2x_hook);
|
||||||
|
|
||||||
custom_node = new AIO_IOB2_BI2X_TDJ;
|
custom_node = new AIO_IOB2_BI2X_TDJ;
|
||||||
memset(&custom_node->data, 0, sizeof(custom_node->data));
|
memset(&custom_node->data, 0, sizeof(custom_node->data));
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,10 @@ namespace games::iidx {
|
|||||||
char IIDXIO_LED_TICKER[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\x00'};
|
char IIDXIO_LED_TICKER[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\x00'};
|
||||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||||
std::mutex IIDX_LED_TICKER_LOCK;
|
std::mutex IIDX_LED_TICKER_LOCK;
|
||||||
|
bool IIDX_TDJ_MONITOR_WARNING = false;
|
||||||
|
|
||||||
|
// io
|
||||||
|
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
||||||
|
|
||||||
tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL] = {
|
tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL] = {
|
||||||
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" },
|
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" },
|
||||||
@@ -311,13 +315,14 @@ namespace games::iidx {
|
|||||||
memcpy(settings2.interface_detail, interface_detail2, sizeof(interface_detail2));
|
memcpy(settings2.interface_detail, interface_detail2, sizeof(interface_detail2));
|
||||||
setupapihook_add(settings2);
|
setupapihook_add(settings2);
|
||||||
|
|
||||||
// IIDX 25-27 BIO2 BI2A input device
|
// IIDX 25+ BIO2 BI2A input device
|
||||||
devicehook_add(new IIDXFMSerialHandle());
|
devicehook_add(new IIDXFMSerialHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for new I/O DLL
|
// check for new I/O DLL
|
||||||
auto aio = libutils::try_library("libaio.dll");
|
auto aio = libutils::try_library("libaio.dll");
|
||||||
if (aio != nullptr) {
|
if (aio != nullptr) {
|
||||||
|
HAS_LIBAIO = true;
|
||||||
|
|
||||||
// check TDJ mode
|
// check TDJ mode
|
||||||
TDJ_MODE |= fileutils::text_read("C:\\000rom.txt") == "TDJ-JA";
|
TDJ_MODE |= fileutils::text_read("C:\\000rom.txt") == "TDJ-JA";
|
||||||
@@ -471,9 +476,8 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check -monitor + TDJ mode
|
// check -monitor + TDJ mode
|
||||||
if (!GRAPHICS_WINDOWED &&
|
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && TDJ_MODE) {
|
||||||
options->at(launcher::Options::DisplayAdapter).is_active() &&
|
IIDX_TDJ_MONITOR_WARNING = true;
|
||||||
TDJ_MODE) {
|
|
||||||
log_warning(
|
log_warning(
|
||||||
"iidx",
|
"iidx",
|
||||||
"\n\n!!! using -monitor option with TDJ is NOT recommended due to known !!!\n"
|
"\n\n!!! using -monitor option with TDJ is NOT recommended due to known !!!\n"
|
||||||
@@ -895,4 +899,52 @@ namespace games::iidx {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_io_emulation_state(iidx_aio_emulation_state state) {
|
||||||
|
// <iidx27, don't care, since tdj wasn't a thing before
|
||||||
|
// this is also how we detect that iidx module is attached
|
||||||
|
if (!HAS_LIBAIO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in iidx27-30, most DLL types can handle both, caching things in nvram and possibly end
|
||||||
|
// up with wrong i/o (temporarily until nvram is initialized again)
|
||||||
|
// to avoid false positives, only do this for iidx31+, since we know for sure that iidx31
|
||||||
|
// has a clean 010/012 split
|
||||||
|
if (avs::game::is_ext(0, 2023101000)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// log_warning below could be turned into log_fatal in the future once we gain confidence
|
||||||
|
// that this isn't triggering when it's not supposed to
|
||||||
|
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"IIDX I/O mode conflict",
|
||||||
|
" game is currently configured to use LDJ I/O;",
|
||||||
|
" however, you turned on IIDX TDJ Mode (-iidxtdj)",
|
||||||
|
" two options to fix this:",
|
||||||
|
" For TDJ mode (120Hz): import patches and enable Force TDJ Mode patch",
|
||||||
|
" For LDJ mode (60Hz): turn off IIDX TDJ Mode option",
|
||||||
|
});
|
||||||
|
log_warning(
|
||||||
|
"iidx",
|
||||||
|
"game is using LDJ I/O but spice TDJ mode is turned on! "
|
||||||
|
"enable Force TDJ I/O patch, OR turn off -iidxtdj");
|
||||||
|
} else if (state == iidx_aio_emulation_state::bi2x_hook && !TDJ_MODE) {
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"IIDX I/O mode conflict",
|
||||||
|
" game is currently configured to use TDJ I/O;",
|
||||||
|
" however, you turned off IIDX TDJ Mode (-iidxtdj)",
|
||||||
|
" two options to fix this:",
|
||||||
|
" For TDJ mode (120Hz): turn on IIDX TDJ Mode option",
|
||||||
|
" For LDJ mode (60Hz): import patches and enable Force LDJ Mode patch",
|
||||||
|
});
|
||||||
|
log_warning(
|
||||||
|
"iidx",
|
||||||
|
"game is using TDJ I/O but spice TDJ mode is turned off! "
|
||||||
|
"turn on -iidxtdj, OR enable Force LDJ I/O patch");
|
||||||
|
} else {
|
||||||
|
log_misc("iidx", "i/o mode validation passed");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace games::iidx {
|
|||||||
extern char IIDXIO_LED_TICKER[10];
|
extern char IIDXIO_LED_TICKER[10];
|
||||||
extern bool IIDXIO_LED_TICKER_READONLY;
|
extern bool IIDXIO_LED_TICKER_READONLY;
|
||||||
extern std::mutex IIDX_LED_TICKER_LOCK;
|
extern std::mutex IIDX_LED_TICKER_LOCK;
|
||||||
|
extern bool IIDX_TDJ_MONITOR_WARNING;
|
||||||
|
|
||||||
constexpr int IIDX_TAPELED_TOTAL = 17;
|
constexpr int IIDX_TAPELED_TOTAL = 17;
|
||||||
// data mapping
|
// data mapping
|
||||||
@@ -62,4 +63,10 @@ namespace games::iidx {
|
|||||||
const char* get_16seg();
|
const char* get_16seg();
|
||||||
bool is_tdj_fhd();
|
bool is_tdj_fhd();
|
||||||
void apply_audio_hacks();
|
void apply_audio_hacks();
|
||||||
|
|
||||||
|
enum class iidx_aio_emulation_state {
|
||||||
|
bi2a_com2,
|
||||||
|
bi2x_hook
|
||||||
|
};
|
||||||
|
void update_io_emulation_state(iidx_aio_emulation_state state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,21 +48,31 @@ namespace games::sdvx {
|
|||||||
SdvxOverlayPosition OVERLAY_POS = SDVX_OVERLAY_BOTTOM;
|
SdvxOverlayPosition OVERLAY_POS = SDVX_OVERLAY_BOTTOM;
|
||||||
bool ENABLE_COM_PORT_SCAN_HOOK = false;
|
bool ENABLE_COM_PORT_SCAN_HOOK = false;
|
||||||
|
|
||||||
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
bool USE_ASIO = false;
|
||||||
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
||||||
|
|
||||||
// states
|
// states
|
||||||
|
bool SHOW_VM_MONITOR_WARNING = false;
|
||||||
static HKEY real_asio_reg_handle = nullptr;
|
static HKEY real_asio_reg_handle = nullptr;
|
||||||
static HKEY real_asio_device_reg_handle = nullptr;
|
static HKEY real_asio_device_reg_handle = nullptr;
|
||||||
|
|
||||||
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
|
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
|
||||||
if (lpSubKey != nullptr && phkResult != nullptr) {
|
if (lpSubKey != nullptr &&
|
||||||
if (hKey == HKEY_LOCAL_MACHINE &&
|
phkResult != nullptr &&
|
||||||
ASIO_DRIVER.has_value() &&
|
hKey == HKEY_LOCAL_MACHINE &&
|
||||||
_stricmp(lpSubKey, "software\\asio") == 0)
|
_stricmp(lpSubKey, "software\\asio") == 0) {
|
||||||
{
|
|
||||||
*phkResult = PARENT_ASIO_REG_HANDLE;
|
|
||||||
|
|
||||||
|
// prevent the game from randomly picking an ASIO device
|
||||||
|
if (!USE_ASIO) {
|
||||||
|
log_info(
|
||||||
|
"sdvx::asio",
|
||||||
|
"game tried to access HKLM\\SOFTWARE\\ASIO but ASIO is disabled; "
|
||||||
|
"access prevented, ASIO init will fail");
|
||||||
|
return ERROR_FILE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
// convince the game to use our hook (so we can swap in the preferred device)
|
||||||
|
if (ASIO_DRIVER.has_value()) {
|
||||||
|
*phkResult = PARENT_ASIO_REG_HANDLE;
|
||||||
return RegOpenKeyA_orig(hKey, lpSubKey, &real_asio_reg_handle);
|
return RegOpenKeyA_orig(hKey, lpSubKey, &real_asio_reg_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +119,7 @@ namespace games::sdvx {
|
|||||||
lpSubKey != nullptr && phkResult != nullptr &&
|
lpSubKey != nullptr && phkResult != nullptr &&
|
||||||
_stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) {
|
_stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) {
|
||||||
log_info("sdvx::io", "hook access to HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA");
|
log_info("sdvx::io", "hook access to HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA");
|
||||||
return 2; // ERROR_FILE_NOT_FOUND
|
return ERROR_FILE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult);
|
return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult);
|
||||||
@@ -229,6 +239,32 @@ namespace games::sdvx {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDVXGame::detect_sound_output_device() {
|
||||||
|
USE_ASIO = false;
|
||||||
|
// if the user specified a value for -sdvxasio, use asio
|
||||||
|
if (ASIO_DRIVER.has_value()) {
|
||||||
|
log_misc(
|
||||||
|
"sdvx",
|
||||||
|
"-sdvxasio is set to \"{}\"", ASIO_DRIVER.value());
|
||||||
|
USE_ASIO = true;
|
||||||
|
} else {
|
||||||
|
// see if XONAR AE is present; if so, use that
|
||||||
|
HKEY subkey;
|
||||||
|
LSTATUS result;
|
||||||
|
result = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\ASIO\\XONAR SOUND CARD(64)", &subkey);
|
||||||
|
if (result == ERROR_SUCCESS) {
|
||||||
|
RegCloseKey(subkey);
|
||||||
|
USE_ASIO = true;
|
||||||
|
log_misc(
|
||||||
|
"sdvx",
|
||||||
|
"found HKLM\\SOFTWARE\\ASIO\\XONAR SOUND CARD(64)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!USE_ASIO) {
|
||||||
|
log_misc("sdvx", "asio not configured");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typedef void **(__fastcall *volume_set_t)(uint64_t, uint64_t, uint64_t);
|
typedef void **(__fastcall *volume_set_t)(uint64_t, uint64_t, uint64_t);
|
||||||
static volume_set_t volume_set_orig = nullptr;
|
static volume_set_t volume_set_orig = nullptr;
|
||||||
static void **__fastcall volume_set_hook(uint64_t vol_sound, uint64_t vol_woofer, uint64_t vol_headphone) {
|
static void **__fastcall volume_set_hook(uint64_t vol_sound, uint64_t vol_woofer, uint64_t vol_headphone) {
|
||||||
@@ -295,12 +331,10 @@ namespace games::sdvx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SPICE64 // SDVX5+ specific code
|
#ifdef SPICE64 // SDVX5+ specific code
|
||||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
this->VALKYRIE_MODEL = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
||||||
auto options = games::get_options(eamuse_get_game());
|
|
||||||
// check -monitor + UFC mode
|
// check -monitor + UFC mode
|
||||||
if (!GRAPHICS_WINDOWED &&
|
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && this->VALKYRIE_MODEL) {
|
||||||
options->at(launcher::Options::DisplayAdapter).is_active() &&
|
SHOW_VM_MONITOR_WARNING = true;
|
||||||
isValkyrieCabinetMode) {
|
|
||||||
log_warning(
|
log_warning(
|
||||||
"sdvx",
|
"sdvx",
|
||||||
"\n\n!!! using -monitor option with VM mode is NOT recommended due to !!!\n"
|
"\n\n!!! using -monitor option with VM mode is NOT recommended due to !!!\n"
|
||||||
@@ -324,16 +358,19 @@ namespace games::sdvx {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPICE64
|
||||||
|
this->detect_sound_output_device();
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDVXGame::attach() {
|
void SDVXGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
#ifdef SPICE64 // SDVX5+ specific code
|
#ifdef SPICE64 // SDVX5+ specific code
|
||||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
|
||||||
|
|
||||||
// LCD handle
|
// LCD handle
|
||||||
if (!isValkyrieCabinetMode) {
|
if (!this->VALKYRIE_MODEL) {
|
||||||
devicehook_init();
|
devicehook_init();
|
||||||
devicehook_add(new games::shared::LCDHandle());
|
devicehook_add(new games::shared::LCDHandle());
|
||||||
}
|
}
|
||||||
@@ -372,8 +409,7 @@ namespace games::sdvx {
|
|||||||
nvapi_hook::initialize(avs::game::DLL_INSTANCE);
|
nvapi_hook::initialize(avs::game::DLL_INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for Valkyrie cabinet mode
|
if (this->VALKYRIE_MODEL) {
|
||||||
if (isValkyrieCabinetMode) {
|
|
||||||
// hook touch window
|
// hook touch window
|
||||||
// in windowed mode, game can accept mouse input on the second screen
|
// in windowed mode, game can accept mouse input on the second screen
|
||||||
if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ namespace games::sdvx {
|
|||||||
extern bool BI2X_INITIALIZED;
|
extern bool BI2X_INITIALIZED;
|
||||||
extern SdvxOverlayPosition OVERLAY_POS;
|
extern SdvxOverlayPosition OVERLAY_POS;
|
||||||
|
|
||||||
|
// states
|
||||||
|
extern bool SHOW_VM_MONITOR_WARNING;
|
||||||
|
|
||||||
class SDVXGame : public games::Game {
|
class SDVXGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
SDVXGame();
|
SDVXGame();
|
||||||
@@ -30,5 +33,12 @@ namespace games::sdvx {
|
|||||||
virtual void attach() override;
|
virtual void attach() override;
|
||||||
virtual void post_attach() override;
|
virtual void post_attach() override;
|
||||||
virtual void detach() override;
|
virtual void detach() override;
|
||||||
|
private:
|
||||||
|
// don't be tempted to make this into a public or a global variable
|
||||||
|
// various modules need to check if VM mode is active and they should
|
||||||
|
// not depend on the fact that SDVX module is active (since it can be
|
||||||
|
// inactive on cabs or partial I/O setup)
|
||||||
|
bool VALKYRIE_MODEL = false;
|
||||||
|
void detect_sound_output_device();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "hooks/audio/audio_private.h"
|
#include "hooks/audio/audio_private.h"
|
||||||
#include "hooks/audio/backends/mmdevice/audio_endpoint_volume.h"
|
#include "hooks/audio/backends/mmdevice/audio_endpoint_volume.h"
|
||||||
#include "hooks/audio/backends/wasapi/audio_client.h"
|
#include "hooks/audio/backends/wasapi/audio_client.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
#define PRINT_FAILED_RESULT(name, ret) \
|
#define PRINT_FAILED_RESULT(name, ret) \
|
||||||
do { \
|
do { \
|
||||||
@@ -56,6 +57,7 @@ ULONG STDMETHODCALLTYPE WrappedIMMDevice::Release() {
|
|||||||
ULONG refs = pReal != nullptr ? pReal->Release() : 0;
|
ULONG refs = pReal != nullptr ? pReal->Release() : 0;
|
||||||
|
|
||||||
if (refs == 0) {
|
if (refs == 0) {
|
||||||
|
log_misc("audio::mmdevice", "WrappedIMMDevice::Release");
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
|||||||
PROPVARIANT *pActivationParams,
|
PROPVARIANT *pActivationParams,
|
||||||
void **ppInterface)
|
void **ppInterface)
|
||||||
{
|
{
|
||||||
log_misc("audio::mmdevice", "WrappedIMMDevice::Activate");
|
log_misc("audio::mmdevice", "WrappedIMMDevice::Activate {}", guid2s(iid));
|
||||||
|
|
||||||
// call original
|
// call original
|
||||||
HRESULT ret = pReal->Activate(iid, dwClsCtx, pActivationParams, ppInterface);
|
HRESULT ret = pReal->Activate(iid, dwClsCtx, pActivationParams, ppInterface);
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#include "device_collection.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceCollection::QueryInterface(REFIID riid, void **ppvObj) {
|
||||||
|
if (ppvObj == nullptr) {
|
||||||
|
return E_POINTER;
|
||||||
|
}
|
||||||
|
if (riid == __uuidof(IMMDeviceCollection)) {
|
||||||
|
this->AddRef();
|
||||||
|
*ppvObj = this;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
return pReal->QueryInterface(riid, ppvObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG STDMETHODCALLTYPE WrappedIMMDeviceCollection::AddRef() {
|
||||||
|
return pReal->AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG STDMETHODCALLTYPE WrappedIMMDeviceCollection::Release() {
|
||||||
|
// get reference count of underlying interface
|
||||||
|
ULONG refs = pReal != nullptr ? pReal->Release() : 0;
|
||||||
|
if (refs == 0) {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceCollection::GetCount(UINT *pcDevices) {
|
||||||
|
return pReal->GetCount(pcDevices);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceCollection::Item(UINT nDevice, IMMDevice **ppDevice) {
|
||||||
|
log_info("audio", "WrappedIMMDeviceCollection::Item[{}]", nDevice);
|
||||||
|
|
||||||
|
// call original
|
||||||
|
const auto hr = pReal->Item(nDevice, ppDevice);
|
||||||
|
|
||||||
|
// wrap interface
|
||||||
|
*ppDevice = new WrappedIMMDevice(*ppDevice);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
|
||||||
|
struct WrappedIMMDeviceCollection : IMMDeviceCollection {
|
||||||
|
explicit WrappedIMMDeviceCollection(IMMDeviceCollection *orig) : pReal(orig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
WrappedIMMDeviceCollection(const WrappedIMMDeviceCollection &) = delete;
|
||||||
|
WrappedIMMDeviceCollection &operator=(const WrappedIMMDeviceCollection &) = delete;
|
||||||
|
|
||||||
|
virtual ~WrappedIMMDeviceCollection() = default;
|
||||||
|
|
||||||
|
#pragma region IUnknown
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override;
|
||||||
|
virtual ULONG STDMETHODCALLTYPE AddRef() override;
|
||||||
|
virtual ULONG STDMETHODCALLTYPE Release() override;
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region IMMDeviceCollection
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE GetCount(UINT *pcDevices) override;
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE Item(UINT nDevice, IMMDevice **ppDevice) override;
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
private:
|
||||||
|
IMMDeviceCollection *const pReal;
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "device_enumerator.h"
|
#include "device_enumerator.h"
|
||||||
|
#include "device_collection.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
@@ -42,7 +43,11 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDeviceEnumerator::EnumAudioEndpoints(
|
|||||||
DWORD dwStateMask,
|
DWORD dwStateMask,
|
||||||
IMMDeviceCollection **ppDevices)
|
IMMDeviceCollection **ppDevices)
|
||||||
{
|
{
|
||||||
return pReal->EnumAudioEndpoints(dataFlow, dwStateMask, ppDevices);
|
const auto hr = pReal->EnumAudioEndpoints(dataFlow, dwStateMask, ppDevices);
|
||||||
|
if (SUCCEEDED(hr) && (ppDevices != nullptr) && (*ppDevices != nullptr)) {
|
||||||
|
*ppDevices = new WrappedIMMDeviceCollection(*ppDevices);
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceEnumerator::GetDefaultAudioEndpoint(
|
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceEnumerator::GetDefaultAudioEndpoint(
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "mme.h"
|
||||||
|
#include <mmeapi.h>
|
||||||
|
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace hooks::audio::mme {
|
||||||
|
|
||||||
|
static decltype(mixerSetControlDetails) *mixerSetControlDetails_orig = nullptr;
|
||||||
|
|
||||||
|
MMRESULT
|
||||||
|
WINAPI
|
||||||
|
mixerSetControlDetails_hook(
|
||||||
|
HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails) {
|
||||||
|
log_misc("audio::mme", "mixerSetControlDetails_hook called; ignoring volume change");
|
||||||
|
return MMSYSERR_NOERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(HINSTANCE module) {
|
||||||
|
mixerSetControlDetails_orig =
|
||||||
|
detour::iat_try("mixerSetControlDetails", mixerSetControlDetails_hook, module);
|
||||||
|
|
||||||
|
if (mixerSetControlDetails_orig != nullptr) {
|
||||||
|
log_misc("audio::mme", "mixerSetControlDetails hooked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace hooks::audio::mme {
|
||||||
|
void init(HINSTANCE module);
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
#include "avshook.h"
|
#include "avshook.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <mutex>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <external/robin_hood.h>
|
||||||
|
|
||||||
#include "avs/core.h"
|
#include "avs/core.h"
|
||||||
#include "avs/ea3.h"
|
#include "avs/ea3.h"
|
||||||
@@ -20,8 +23,12 @@ static bool FAKE_FILE_OPEN = false;
|
|||||||
static std::map<std::string, std::string> ROM_FILE_MAP;
|
static std::map<std::string, std::string> ROM_FILE_MAP;
|
||||||
static std::string *ROM_FILE_CONTENTS = nullptr;
|
static std::string *ROM_FILE_CONTENTS = nullptr;
|
||||||
|
|
||||||
|
static std::mutex lstat_cache_mutex;
|
||||||
|
static robin_hood::unordered_map<std::string, std::pair<int, struct avs::core::avs_stat>> lstat_cache;
|
||||||
|
|
||||||
namespace hooks::avs::config {
|
namespace hooks::avs::config {
|
||||||
bool DISABLE_VFS_DRIVE_REDIRECTION = false;
|
bool DISABLE_VFS_DRIVE_REDIRECTION = false;
|
||||||
|
bool DISABLE_CACHE = false;
|
||||||
bool LOG = false;
|
bool LOG = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,6 +86,74 @@ static bool is_spam_file(const char *file) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_ldj_cacheable_file(const char *file) {
|
||||||
|
static const char *spam_prefixes[] = {
|
||||||
|
// iidx33: profile background customization, spams on every frame during card entry
|
||||||
|
"/data/graphic/entry_card",
|
||||||
|
// iidx32+: movie thumbnails, spams on every frame during song select
|
||||||
|
"/data/graphic/thumbnail/",
|
||||||
|
"/data/graphic/movie_thumbnail/",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &spam : spam_prefixes) {
|
||||||
|
if (string_begins_with(file, spam)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cached_avs_fs_lstat(const char *name, struct avs::core::avs_stat *st) {
|
||||||
|
assert(name != nullptr);
|
||||||
|
assert(st != nullptr);
|
||||||
|
|
||||||
|
const std::string filename(name);
|
||||||
|
int value = 0;
|
||||||
|
std::lock_guard<std::mutex> lock(lstat_cache_mutex);
|
||||||
|
|
||||||
|
// if this was already seen, return cached result
|
||||||
|
if (lstat_cache.contains(filename)) {
|
||||||
|
const auto &result = lstat_cache.at(filename);
|
||||||
|
value = result.first;
|
||||||
|
memcpy(st, &result.second, sizeof(*st));
|
||||||
|
WRAP_DEBUG_FMT(
|
||||||
|
"(cached) file={}, a={}, m={}, c={}, size={}, u={}",
|
||||||
|
name,
|
||||||
|
st->st_atime,
|
||||||
|
st->st_mtime,
|
||||||
|
st->st_ctime,
|
||||||
|
st->filesize,
|
||||||
|
st->unk1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not, call the original avs function
|
||||||
|
// this returns 1 on success with valid values in st
|
||||||
|
// return 0 on failure, with filesize set to 0
|
||||||
|
// maybe this could be used for future optimization
|
||||||
|
value = avs::core::avs_fs_lstat(name, st);
|
||||||
|
WRAP_DEBUG_FMT(
|
||||||
|
"file={} a={}, m={}, c={}, size={}, u={}",
|
||||||
|
name,
|
||||||
|
st->st_atime,
|
||||||
|
st->st_mtime,
|
||||||
|
st->st_ctime,
|
||||||
|
st->filesize,
|
||||||
|
st->unk1);
|
||||||
|
|
||||||
|
// and cache it if there is room (prevent unconstrained growth)
|
||||||
|
if (lstat_cache.size() <= 4096) {
|
||||||
|
// c++ was a mistake
|
||||||
|
lstat_cache.emplace(
|
||||||
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(filename),
|
||||||
|
std::forward_as_tuple(value, *st));
|
||||||
|
}
|
||||||
|
|
||||||
|
// done
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
static int avs_fs_fstat(avs::core::avs_file_t fd, struct avs::core::avs_stat *st) {
|
static int avs_fs_fstat(avs::core::avs_file_t fd, struct avs::core::avs_stat *st) {
|
||||||
if (is_fake_fd(fd) && ROM_FILE_CONTENTS) {
|
if (is_fake_fd(fd) && ROM_FILE_CONTENTS) {
|
||||||
if (st) {
|
if (st) {
|
||||||
@@ -104,7 +179,12 @@ static int avs_fs_lstat(const char *name, struct avs::core::avs_stat *st) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = avs::core::avs_fs_lstat(name, st);
|
if (!hooks::avs::config::DISABLE_CACHE && (st != nullptr) &&
|
||||||
|
avs::game::is_model("LDJ") && is_ldj_cacheable_file(name)) {
|
||||||
|
return cached_avs_fs_lstat(name, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = avs::core::avs_fs_lstat(name, st);
|
||||||
if (!is_spam_file(name)) {
|
if (!is_spam_file(name)) {
|
||||||
WRAP_DEBUG_FMT("name: {}", name);
|
WRAP_DEBUG_FMT("name: {}", name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
namespace hooks::avs {
|
namespace hooks::avs {
|
||||||
namespace config {
|
namespace config {
|
||||||
extern bool DISABLE_VFS_DRIVE_REDIRECTION;
|
extern bool DISABLE_VFS_DRIVE_REDIRECTION;
|
||||||
|
extern bool DISABLE_CACHE;
|
||||||
extern bool LOG;
|
extern bool LOG;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
@@ -399,6 +400,12 @@ static BOOL WINAPI ClearCommError_hook(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT
|
|||||||
lpStat->cbInQue = custom_handle->bytes_available();
|
lpStat->cbInQue = custom_handle->bytes_available();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gitadora arena model needs this, or else
|
||||||
|
// the game will keep spamming 0xAA
|
||||||
|
if (games::gitadora::is_arena_model() && lpErrors) {
|
||||||
|
*lpErrors = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "cfg/icon.h"
|
#include "cfg/icon.h"
|
||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
#include "games/ddr/ddr.h"
|
#include "games/ddr/ddr.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||||
#include "launcher/shutdown.h"
|
#include "launcher/shutdown.h"
|
||||||
@@ -33,6 +34,7 @@ struct CaptureData {
|
|||||||
|
|
||||||
HWND TDJ_SUBSCREEN_WINDOW = nullptr;
|
HWND TDJ_SUBSCREEN_WINDOW = nullptr;
|
||||||
HWND SDVX_SUBSCREEN_WINDOW = nullptr;
|
HWND SDVX_SUBSCREEN_WINDOW = nullptr;
|
||||||
|
HWND GFDM_SUBSCREEN_WINDOW = nullptr;
|
||||||
|
|
||||||
// icon
|
// icon
|
||||||
static HICON WINDOW_ICON = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(MAINICON));
|
static HICON WINDOW_ICON = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(MAINICON));
|
||||||
@@ -369,12 +371,17 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
|
|
||||||
bool is_tdj_sub_window = avs::game::is_model("LDJ") && window_name.ends_with(" sub");
|
bool is_tdj_sub_window = avs::game::is_model("LDJ") && window_name.ends_with(" sub");
|
||||||
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
||||||
|
bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL");
|
||||||
|
|
||||||
// TDJ windowed mode with subscreen: hide maximize button
|
// TDJ windowed mode with subscreen: hide maximize button
|
||||||
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window) {
|
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) {
|
||||||
dwStyle &= ~(WS_MAXIMIZEBOX);
|
dwStyle &= ~(WS_MAXIMIZEBOX);
|
||||||
}
|
}
|
||||||
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
// mouse clicks become misaligned when resized
|
||||||
|
if (is_gfdm_sub_window) {
|
||||||
|
dwStyle &= ~(WS_SIZEBOX);
|
||||||
|
}
|
||||||
|
if ((is_tdj_sub_window || is_sdvx_sub_window || is_gfdm_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
||||||
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +424,11 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW);
|
graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_gfdm_sub_window) {
|
||||||
|
GFDM_SUBSCREEN_WINDOW = result;
|
||||||
|
graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW);
|
||||||
|
}
|
||||||
|
|
||||||
disable_touch_indicators(result);
|
disable_touch_indicators(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ extern bool GRAPHICS_WSUB_BORDERLESS;
|
|||||||
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
|
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
|
||||||
extern HWND TDJ_SUBSCREEN_WINDOW;
|
extern HWND TDJ_SUBSCREEN_WINDOW;
|
||||||
extern HWND SDVX_SUBSCREEN_WINDOW;
|
extern HWND SDVX_SUBSCREEN_WINDOW;
|
||||||
|
extern HWND GFDM_SUBSCREEN_WINDOW;
|
||||||
|
|
||||||
extern bool SUBSCREEN_FORCE_REDRAW;
|
extern bool SUBSCREEN_FORCE_REDRAW;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -24,6 +25,7 @@ static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr;
|
|||||||
#ifdef SPICE64
|
#ifdef SPICE64
|
||||||
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
|
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
|
||||||
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
|
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
|
||||||
|
static decltype(WideCharToMultiByte) *WideCharToMultiByte_orig = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook(
|
static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook(
|
||||||
@@ -173,6 +175,41 @@ static BOOL WINAPI IsDBCSLeadByte_hook (
|
|||||||
return IsDBCSLeadByteEx(CODEPAGE_SHIFT_JIS, TestChar);
|
return IsDBCSLeadByteEx(CODEPAGE_SHIFT_JIS, TestChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int
|
||||||
|
WINAPI
|
||||||
|
WideCharToMultiByte_hook(
|
||||||
|
UINT CodePage,
|
||||||
|
DWORD dwFlags,
|
||||||
|
LPCWCH lpWideCharStr,
|
||||||
|
int cchWideChar,
|
||||||
|
LPSTR lpMultiByteStr,
|
||||||
|
int cbMultiByte,
|
||||||
|
LPCCH lpDefaultChar,
|
||||||
|
LPBOOL lpUsedDefaultChar
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (CodePage) {
|
||||||
|
case CP_ACP:
|
||||||
|
case CP_THREAD_ACP:
|
||||||
|
SetThreadLocale(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN));
|
||||||
|
CodePage = CODEPAGE_SHIFT_JIS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return WideCharToMultiByte_orig(
|
||||||
|
CodePage,
|
||||||
|
dwFlags,
|
||||||
|
lpWideCharStr,
|
||||||
|
cchWideChar,
|
||||||
|
lpMultiByteStr,
|
||||||
|
cbMultiByte,
|
||||||
|
lpDefaultChar,
|
||||||
|
lpUsedDefaultChar);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void hooks::lang::early_init() {
|
void hooks::lang::early_init() {
|
||||||
@@ -213,6 +250,15 @@ void hooks::lang::early_init() {
|
|||||||
IsDBCSLeadByte_hook,
|
IsDBCSLeadByte_hook,
|
||||||
&IsDBCSLeadByte_orig);
|
&IsDBCSLeadByte_orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (games::gitadora::is_arena_model()) {
|
||||||
|
log_info("hooks::lang", "hooking WideCharToMultiByte");
|
||||||
|
detour::trampoline_try(
|
||||||
|
"kernel32.dll",
|
||||||
|
"WideCharToMultiByte",
|
||||||
|
WideCharToMultiByte_hook,
|
||||||
|
&WideCharToMultiByte_orig);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@
|
|||||||
#include "misc/device.h"
|
#include "misc/device.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "misc/extdev.h"
|
#include "misc/extdev.h"
|
||||||
|
#include "misc/ami2000.h"
|
||||||
#include "misc/sciunit.h"
|
#include "misc/sciunit.h"
|
||||||
#include "misc/sde.h"
|
#include "misc/sde.h"
|
||||||
#include "misc/wintouchemu.h"
|
#include "misc/wintouchemu.h"
|
||||||
@@ -195,6 +196,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bool attach_icca = false;
|
bool attach_icca = false;
|
||||||
bool attach_device = false;
|
bool attach_device = false;
|
||||||
bool attach_extdev = false;
|
bool attach_extdev = false;
|
||||||
|
bool attach_ami2000 = false;
|
||||||
bool attach_sciunit = false;
|
bool attach_sciunit = false;
|
||||||
bool attach_cpusbxpkm_printer = false;
|
bool attach_cpusbxpkm_printer = false;
|
||||||
bool attach_iidx = false;
|
bool attach_iidx = false;
|
||||||
@@ -344,8 +346,13 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::DisplayAdapter].is_active()) {
|
if (options[launcher::Options::DisplayAdapter].is_active() &&
|
||||||
|
options[launcher::Options::DisplayAdapter].value_uint32() != D3DADAPTER_DEFAULT) {
|
||||||
D3D9_ADAPTER = options[launcher::Options::DisplayAdapter].value_uint32();
|
D3D9_ADAPTER = options[launcher::Options::DisplayAdapter].value_uint32();
|
||||||
|
|
||||||
|
// when we fix up adapter numbers, we only fix the first adapter, and not any subsequent
|
||||||
|
// adapters, so we can't deal with multi-monitor games
|
||||||
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::CaptureCursor].value_bool()) {
|
if (options[launcher::Options::CaptureCursor].value_bool()) {
|
||||||
GRAPHICS_CAPTURE_CURSOR = true;
|
GRAPHICS_CAPTURE_CURSOR = true;
|
||||||
@@ -423,8 +430,8 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::EnableEXTDEVModule].value_bool()) {
|
if (options[launcher::Options::EnableEXTDEVModule].value_bool()) {
|
||||||
attach_extdev = true;
|
attach_extdev = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::EnableSCIUNITModule].value_bool()) {
|
if (options[launcher::Options::EnableAMI2000Module].value_bool()) {
|
||||||
attach_sciunit = true;
|
attach_ami2000 = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::EnableDevicePassthrough].value_bool()) {
|
if (options[launcher::Options::EnableDevicePassthrough].value_bool()) {
|
||||||
hooks::device::ENABLE = false;
|
hooks::device::ENABLE = false;
|
||||||
@@ -523,6 +530,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
overlay::windows::IIDX_SEGMENT_LOCATION =
|
overlay::windows::IIDX_SEGMENT_LOCATION =
|
||||||
options[launcher::Options::spice2x_IIDXLEDPos].value_text();
|
options[launcher::Options::spice2x_IIDXLEDPos].value_text();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::IIDXLEDBorderless].value_bool()) {
|
||||||
|
overlay::windows::IIDX_SEGMENT_BORDERLESS = true;
|
||||||
|
}
|
||||||
if (options[launcher::Options::spice2x_IIDXNoESpec].value_bool()) {
|
if (options[launcher::Options::spice2x_IIDXNoESpec].value_bool()) {
|
||||||
games::iidx::DISABLE_ESPEC_IO = true;
|
games::iidx::DISABLE_ESPEC_IO = true;
|
||||||
}
|
}
|
||||||
@@ -744,6 +754,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) {
|
if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) {
|
||||||
hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true;
|
hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::DisableAvsCache].is_active()) {
|
||||||
|
hooks::avs::config::DISABLE_CACHE = true;
|
||||||
|
}
|
||||||
if (options[launcher::Options::ScreenResizeConfigPath].is_active()) {
|
if (options[launcher::Options::ScreenResizeConfigPath].is_active()) {
|
||||||
cfg::SCREEN_RESIZE_CFG_PATH_OVERRIDE =
|
cfg::SCREEN_RESIZE_CFG_PATH_OVERRIDE =
|
||||||
options[launcher::Options::ScreenResizeConfigPath].value_text();
|
options[launcher::Options::ScreenResizeConfigPath].value_text();
|
||||||
@@ -1629,6 +1642,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
attach_io = true;
|
attach_io = true;
|
||||||
attach_device = true;
|
attach_device = true;
|
||||||
attach_extdev = true;
|
attach_extdev = true;
|
||||||
|
attach_ami2000 = true;
|
||||||
attach_gitadora = true;
|
attach_gitadora = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2191,6 +2205,11 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
extdev_attach();
|
extdev_attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ami 2000 card reader attach
|
||||||
|
if (attach_io || attach_ami2000) {
|
||||||
|
ami2000_attach();
|
||||||
|
}
|
||||||
|
|
||||||
// sci unit attach
|
// sci unit attach
|
||||||
if (attach_io || attach_sciunit) {
|
if (attach_io || attach_sciunit) {
|
||||||
sciunit_attach();
|
sciunit_attach();
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "monitor",
|
.name = "monitor",
|
||||||
.desc = "Sets the display that the game will be opened in, for multiple monitors.\n\n"
|
.desc = "Sets the display that the game will be opened in, for multiple monitors.\n\n"
|
||||||
"0 is the primary monitor, 1 is the second monitor, and so on.\n\n"
|
"0 is the primary monitor, 1 is the second monitor, and so on.\n\n"
|
||||||
"Not all games will respect this. Does not work at all with multi-monitor games like TDJ/UFC. "
|
"Not all games will respect this. Not recommended for multi-monitor games like Lightning Model / Valkyrie Model modes. "
|
||||||
"Disable Full Screen Optimizations for best results",
|
"Disable Full Screen Optimizations for best results",
|
||||||
.type = OptionType::Integer,
|
.type = OptionType::Integer,
|
||||||
.category = "Graphics (Full Screen)",
|
.category = "Graphics (Full Screen)",
|
||||||
@@ -590,7 +590,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "IIDX TDJ Mode",
|
.title = "IIDX TDJ Mode (Lightning Model)",
|
||||||
.name = "iidxtdj",
|
.name = "iidxtdj",
|
||||||
.desc = "Enables TDJ cabinet mode. Ensure you also set -iidxsounddevice to desired option",
|
.desc = "Enables TDJ cabinet mode. Ensure you also set -iidxsounddevice to desired option",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
@@ -679,6 +679,16 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{"bottomright", ""},
|
{"bottomright", ""},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// IIDXLEDBorderless
|
||||||
|
.title = "IIDX LED Ticker Borderless",
|
||||||
|
.name = "iidxledborderless",
|
||||||
|
.aliases= "iidxledborderless",
|
||||||
|
.desc = "Make LED ticker overlay window borderless (remove window decoration)",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.game_name = "Beatmania IIDX",
|
||||||
|
.category = "Overlay",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "Force Load Sound Voltex Module",
|
.title = "Force Load Sound Voltex Module",
|
||||||
.name = "sdvx",
|
.name = "sdvx",
|
||||||
@@ -1311,6 +1321,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "I/O Modules",
|
.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",
|
.title = "Enable SCIUNIT Module",
|
||||||
.name = "sciunit",
|
.name = "sciunit",
|
||||||
@@ -1756,6 +1773,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Development",
|
.category = "Development",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// DisableAvsCache
|
||||||
|
.title = "Disable AVS Cache",
|
||||||
|
.name = "avscachedisable",
|
||||||
|
.desc = "Disable optimization used for some games to cache data file info (only for IIDX32+ for now)",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.category = "Development",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "Output PEB",
|
.title = "Output PEB",
|
||||||
.name = "pebprint",
|
.name = "pebprint",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ namespace launcher {
|
|||||||
spice2x_IIDXLEDFontSize,
|
spice2x_IIDXLEDFontSize,
|
||||||
spice2x_IIDXLEDColor,
|
spice2x_IIDXLEDColor,
|
||||||
spice2x_IIDXLEDPos,
|
spice2x_IIDXLEDPos,
|
||||||
|
IIDXLEDBorderless,
|
||||||
LoadSoundVoltexModule,
|
LoadSoundVoltexModule,
|
||||||
SDVXForce720p,
|
SDVXForce720p,
|
||||||
SDVXPrinterEmulation,
|
SDVXPrinterEmulation,
|
||||||
@@ -146,6 +147,7 @@ namespace launcher {
|
|||||||
EnableICCAModule,
|
EnableICCAModule,
|
||||||
EnableDEVICEModule,
|
EnableDEVICEModule,
|
||||||
EnableEXTDEVModule,
|
EnableEXTDEVModule,
|
||||||
|
EnableAMI2000Module,
|
||||||
EnableSCIUNITModule,
|
EnableSCIUNITModule,
|
||||||
EnableDevicePassthrough,
|
EnableDevicePassthrough,
|
||||||
ForceWinTouch,
|
ForceWinTouch,
|
||||||
@@ -195,6 +197,7 @@ namespace launcher {
|
|||||||
DisableSignalHandling,
|
DisableSignalHandling,
|
||||||
DisableDebugHooks,
|
DisableDebugHooks,
|
||||||
DisableAvsVfsDriveMountRedirection,
|
DisableAvsVfsDriveMountRedirection,
|
||||||
|
DisableAvsCache,
|
||||||
OutputPEB,
|
OutputPEB,
|
||||||
DumpSystemInfo,
|
DumpSystemInfo,
|
||||||
QKSArgs,
|
QKSArgs,
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "exitprompt.h"
|
#include "exitprompt.h"
|
||||||
|
#include "avs/game.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "games/iidx/iidx.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
@@ -55,7 +57,21 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
build_button(this->overlay->window_config, "Show Config", size, NextItem::NEW_LINE, false);
|
build_button(this->overlay->window_config, "Show Config", size, NextItem::NEW_LINE, false);
|
||||||
build_button(this->overlay->window_sub, "Show Subscreen", size, NextItem::NEW_LINE, false);
|
|
||||||
|
std::string sub = "Show Subscreen";
|
||||||
|
if (avs::game::is_model("LDJ")) {
|
||||||
|
if (games::iidx::TDJ_MODE) {
|
||||||
|
sub = "Show TDJ Subscreen";
|
||||||
|
} else {
|
||||||
|
sub = "Show LDJ LED Ticker";
|
||||||
|
}
|
||||||
|
} else if (avs::game::is_model("KFC")) {
|
||||||
|
sub = "Show Valkyrie Subscreen";
|
||||||
|
} else if (avs::game::is_model("REC")) {
|
||||||
|
sub = "Show DRS Dance Floor";
|
||||||
|
}
|
||||||
|
|
||||||
|
build_button(this->overlay->window_sub, sub, size, NextItem::NEW_LINE, false);
|
||||||
|
|
||||||
ImGui::TextDisabled("Graphics");
|
ImGui::TextDisabled("Graphics");
|
||||||
build_button(this->overlay->window_camera, "Camera control", size, NextItem::NEW_LINE);
|
build_button(this->overlay->window_camera, "Camera control", size, NextItem::NEW_LINE);
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ namespace overlay::windows {
|
|||||||
uint32_t IIDX_SEGMENT_FONT_SIZE = 64;
|
uint32_t IIDX_SEGMENT_FONT_SIZE = 64;
|
||||||
std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR = std::nullopt;
|
std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR = std::nullopt;
|
||||||
std::string IIDX_SEGMENT_LOCATION = "bottom";
|
std::string IIDX_SEGMENT_LOCATION = "bottom";
|
||||||
|
bool IIDX_SEGMENT_BORDERLESS = false;
|
||||||
|
|
||||||
static const size_t TICKER_SIZE = 9;
|
static const size_t TICKER_SIZE = 9;
|
||||||
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
|
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
|
||||||
static const ImVec4 RED(1.f, 0.f, 0.f, 1.f);
|
static const ImVec4 RED(1.f, 0.f, 0.f, 1.f);
|
||||||
static const int PADDING_Y = 8;
|
static const float PADDING_Y = 8.f;
|
||||||
static const int PADDING_X = 4;
|
static const float PADDING_X = 4.f;
|
||||||
|
|
||||||
static const std::map<char, std::pair<char, char>> CHARMAP = {\
|
static const std::map<char, std::pair<char, char>> CHARMAP = {\
|
||||||
// period - add a space afterwards (game sends 'm' for period)
|
// period - add a space afterwards (game sends 'm' for period)
|
||||||
@@ -31,16 +32,18 @@ namespace overlay::windows {
|
|||||||
log_fatal("iidx_seg", "DSEG_FONT is null");
|
log_fatal("iidx_seg", "DSEG_FONT is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
this->title = "IIDX LED Segment Display";
|
this->title = "IIDX LDJ LED Ticker";
|
||||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||||
this->remove_window_padding = true;
|
this->remove_window_padding = true;
|
||||||
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
||||||
this->flags = ImGuiWindowFlags_NoTitleBar
|
this->flags = ImGuiWindowFlags_NoScrollbar
|
||||||
| ImGuiWindowFlags_NoScrollbar
|
|
||||||
| ImGuiWindowFlags_NoResize
|
| ImGuiWindowFlags_NoResize
|
||||||
| ImGuiWindowFlags_NoNavFocus
|
| ImGuiWindowFlags_NoNavFocus
|
||||||
| ImGuiWindowFlags_NoNavInputs
|
| ImGuiWindowFlags_NoNavInputs
|
||||||
| ImGuiWindowFlags_NoDocking;
|
| ImGuiWindowFlags_NoDocking;
|
||||||
|
if (IIDX_SEGMENT_BORDERLESS) {
|
||||||
|
this->flags |= ImGuiWindowFlags_NoTitleBar;
|
||||||
|
}
|
||||||
|
|
||||||
if (IIDX_SEGMENT_FONT_COLOR.has_value()) {
|
if (IIDX_SEGMENT_FONT_COLOR.has_value()) {
|
||||||
const auto rgb = IIDX_SEGMENT_FONT_COLOR.value();
|
const auto rgb = IIDX_SEGMENT_FONT_COLOR.value();
|
||||||
@@ -51,13 +54,22 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float IIDXSegmentDisplay::get_title_bar_height() {
|
||||||
|
// make sure you don't call this within PushFont/PopFont block
|
||||||
|
// since text line height calculation is dependent on current font size
|
||||||
|
return ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
void IIDXSegmentDisplay::calculate_initial_window() {
|
void IIDXSegmentDisplay::calculate_initial_window() {
|
||||||
// ImGui::CalcTextSize doesn't seem to work here, so manually calculate
|
// ImGui::CalcTextSize doesn't seem to work here, so manually calculate
|
||||||
ImGui::PushFont(DSEG_FONT);
|
ImGui::PushFont(DSEG_FONT);
|
||||||
this->init_size = ImGui::CalcTextSize("~.~.~.~.~.~.~.~.~.");
|
this->init_size = ImGui::CalcTextSize("~.~.~.~.~.~.~.~.~.");
|
||||||
|
ImGui::PopFont();
|
||||||
this->init_size.x += PADDING_X * 2;
|
this->init_size.x += PADDING_X * 2;
|
||||||
this->init_size.y += PADDING_Y * 2;
|
this->init_size.y += PADDING_Y * 2;
|
||||||
ImGui::PopFont();
|
if (!IIDX_SEGMENT_BORDERLESS) {
|
||||||
|
this->init_size.y += get_title_bar_height();
|
||||||
|
}
|
||||||
|
|
||||||
// initial horizontal position
|
// initial horizontal position
|
||||||
if (IIDX_SEGMENT_LOCATION.find("left") != std::string::npos) {
|
if (IIDX_SEGMENT_LOCATION.find("left") != std::string::npos) {
|
||||||
@@ -127,9 +139,13 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IIDXSegmentDisplay::draw_ticker(char *ticker_string) {
|
void IIDXSegmentDisplay::draw_ticker(char *ticker_string) {
|
||||||
ImGui::PushFont(DSEG_FONT);
|
float pad_y = PADDING_Y;
|
||||||
|
if (!IIDX_SEGMENT_BORDERLESS) {
|
||||||
|
pad_y += get_title_bar_height();
|
||||||
|
}
|
||||||
|
const auto pos = ImVec2(PADDING_X, pad_y);
|
||||||
|
|
||||||
const auto pos = ImVec2(PADDING_X, PADDING_Y);
|
ImGui::PushFont(DSEG_FONT);
|
||||||
|
|
||||||
// to imitate LED "off"
|
// to imitate LED "off"
|
||||||
ImGui::SetCursorPos(pos);
|
ImGui::SetCursorPos(pos);
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ namespace overlay::windows {
|
|||||||
extern uint32_t IIDX_SEGMENT_FONT_SIZE;
|
extern uint32_t IIDX_SEGMENT_FONT_SIZE;
|
||||||
extern std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR;
|
extern std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR;
|
||||||
extern std::string IIDX_SEGMENT_LOCATION;
|
extern std::string IIDX_SEGMENT_LOCATION;
|
||||||
|
extern bool IIDX_SEGMENT_BORDERLESS;
|
||||||
|
|
||||||
class IIDXSegmentDisplay : public Window {
|
class IIDXSegmentDisplay : public Window {
|
||||||
public:
|
public:
|
||||||
IIDXSegmentDisplay(SpiceOverlay *overlay);
|
IIDXSegmentDisplay(SpiceOverlay *overlay);
|
||||||
void calculate_initial_window() override;
|
void calculate_initial_window() override;
|
||||||
void build_content() override;
|
void build_content() override;
|
||||||
|
float get_title_bar_height();
|
||||||
private:
|
private:
|
||||||
ImVec4 color;
|
ImVec4 color;
|
||||||
void draw_ticker(char *ticker_string);
|
void draw_ticker(char *ticker_string);
|
||||||
|
|||||||
@@ -9,12 +9,14 @@
|
|||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||||
this->title = "IIDX Sub Screen";
|
this->title = "IIDX TDJ Subscreen";
|
||||||
|
|
||||||
if (GRAPHICS_IIDX_WSUB) {
|
if (GRAPHICS_IIDX_WSUB) {
|
||||||
this->disabled_message =
|
this->disabled_message =
|
||||||
"Close this overlay and use the second window.\n"
|
"Close this overlay and use the second window.\n"
|
||||||
"If you don't see the window, double check your DLL type and apply TDJ I/O patches as needed.";
|
"If you don't see the window, double check your DLL type and apply TDJ I/O patches as needed.";
|
||||||
|
} else if (games::iidx::IIDX_TDJ_MONITOR_WARNING) {
|
||||||
|
this->disabled_message = "TDJ mode subscreen overlay is not compatible with -monitor option";
|
||||||
}
|
}
|
||||||
|
|
||||||
float size = 0.5f;
|
float size = 0.5f;
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||||
this->title = "SDVX Sub Screen";
|
this->title = "SDVX Subscreen";
|
||||||
|
|
||||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
||||||
if (!isValkyrieCabinetMode) {
|
if (!isValkyrieCabinetMode) {
|
||||||
this->disabled_message = "Valkyrie Model mode is not enabled!";
|
this->disabled_message = "Valkyrie Model mode is not enabled!";
|
||||||
|
} else if (games::sdvx::SHOW_VM_MONITOR_WARNING) {
|
||||||
|
this->disabled_message = "VM mode subscreen overlay is not compatible with -monitor option";
|
||||||
} else if (GRAPHICS_WINDOWED) {
|
} else if (GRAPHICS_WINDOWED) {
|
||||||
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
|
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
|
||||||
|
|||||||
@@ -241,7 +241,9 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto is_windowed_sub =
|
const auto is_windowed_sub =
|
||||||
(GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) || (hWnd == SDVX_SUBSCREEN_WINDOW);
|
(GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) ||
|
||||||
|
(hWnd == SDVX_SUBSCREEN_WINDOW) ||
|
||||||
|
(hWnd == GFDM_SUBSCREEN_WINDOW);
|
||||||
|
|
||||||
if (msg == WM_CLOSE) {
|
if (msg == WM_CLOSE) {
|
||||||
if (is_windowed_sub) {
|
if (is_windowed_sub) {
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ namespace deferredlogs {
|
|||||||
" this crash is most likely related to audio init failure",
|
" this crash is most likely related to audio init failure",
|
||||||
" * check if the default audio device has changed",
|
" * check if the default audio device has changed",
|
||||||
" * fix your audio device settings (e.g., sample rate)",
|
" * fix your audio device settings (e.g., sample rate)",
|
||||||
" * double check your spice audio options and patches"
|
" * double check your spice audio options and patches",
|
||||||
|
" * ensure no other application is using the device in exclusive mode"
|
||||||
};
|
};
|
||||||
|
|
||||||
std::mutex deferred_errors_mutex;
|
std::mutex deferred_errors_mutex;
|
||||||
|
|||||||
@@ -350,4 +350,23 @@ static inline std::string wchar_to_u8(const wchar_t* wstr) {
|
|||||||
std::string out(size - 1, 0);
|
std::string out(size - 1, 0);
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, out.data(), size - 1, nullptr, nullptr);
|
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, out.data(), size - 1, nullptr, nullptr);
|
||||||
return out;
|
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