mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
Add Mahjong Fight Girl Support (#275)
## Link to GitHub Issue, if one exists n/a ## Description of change Adds Mahjong Fight Girl Support ## Compiling + ## Testing Tested with VFG:J:E:A:2024100800 in HG mode with VFG IO. No modifications made to data, other than avs-config.xml and ea3-config.xml. B and C modes don't work because of serial touchscreen. UKS mode doesn't work because of IO.
This commit is contained in:
@@ -442,6 +442,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/qks/qks.cpp
|
games/qks/qks.cpp
|
||||||
games/qks/io.cpp
|
games/qks/io.cpp
|
||||||
games/qks/bi2x_hook.cpp
|
games/qks/bi2x_hook.cpp
|
||||||
|
games/mfg/mfg.cpp
|
||||||
|
games/mfg/io.cpp
|
||||||
|
games/mfg/bi2a_hook.cpp
|
||||||
|
|
||||||
# hooks
|
# hooks
|
||||||
hooks/audio/audio.cpp
|
hooks/audio/audio.cpp
|
||||||
@@ -587,6 +590,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
util/sysutils.cpp
|
util/sysutils.cpp
|
||||||
util/lz77.cpp
|
util/lz77.cpp
|
||||||
util/tapeled.cpp
|
util/tapeled.cpp
|
||||||
|
util/execexe.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) {
|
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount) {
|
||||||
this->com_port = lpCOMPort;
|
this->com_port = lpCOMPort;
|
||||||
|
this->icca_node_count = iccaNodeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
||||||
@@ -16,7 +17,7 @@ bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
|||||||
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
|
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
|
||||||
|
|
||||||
// ACIO device
|
// ACIO device
|
||||||
acio_emu.add_device(new acioemu::ICCADevice(false, true, 2));
|
acio_emu.add_device(new acioemu::ICCADevice(false, true, icca_node_count));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ namespace acioemu {
|
|||||||
private:
|
private:
|
||||||
LPCWSTR com_port;
|
LPCWSTR com_port;
|
||||||
|
|
||||||
|
uint8_t icca_node_count;
|
||||||
|
|
||||||
acioemu::ACIOEmu acio_emu;
|
acioemu::ACIOEmu acio_emu;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ACIOHandle(LPCWSTR lpCOMPort);
|
ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount = 2);
|
||||||
|
|
||||||
bool open(LPCWSTR lpFileName) override;
|
bool open(LPCWSTR lpFileName) override;
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
|||||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
||||||
) {
|
) {
|
||||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
||||||
|
} else if (avs::game::is_model({"VFG"})) {
|
||||||
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
||||||
} else {
|
} else {
|
||||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||||
}
|
}
|
||||||
@@ -333,6 +335,7 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
|||||||
case ACIO_CMD_CLEAR:
|
case ACIO_CMD_CLEAR:
|
||||||
case 0x30: // GetBoardProductNumber
|
case 0x30: // GetBoardProductNumber
|
||||||
case 0x31: // GetMicomInfo
|
case 0x31: // GetMicomInfo
|
||||||
|
case 0x3A: // ???
|
||||||
case 0x0116: // ???
|
case 0x0116: // ???
|
||||||
case 0x0120: // ???
|
case 0x0120: // ???
|
||||||
case 0xFF: // BROADCAST
|
case 0xFF: // BROADCAST
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "bc/io.h"
|
#include "bc/io.h"
|
||||||
#include "ccj/io.h"
|
#include "ccj/io.h"
|
||||||
#include "qks/io.h"
|
#include "qks/io.h"
|
||||||
|
#include "mfg/io.h"
|
||||||
|
|
||||||
namespace games {
|
namespace games {
|
||||||
|
|
||||||
@@ -305,6 +306,13 @@ namespace games {
|
|||||||
buttons.insert({ qks, qks::get_buttons() });
|
buttons.insert({ qks, qks::get_buttons() });
|
||||||
buttons_help.insert({ qks, qks::get_buttons_help() });
|
buttons_help.insert({ qks, qks::get_buttons_help() });
|
||||||
file_hints[qks].emplace_back("game/uks.exe");
|
file_hints[qks].emplace_back("game/uks.exe");
|
||||||
|
|
||||||
|
// Mahjong Fight Girl
|
||||||
|
const std::string mfg("Mahjong Fight Girl");
|
||||||
|
games.push_back(mfg);
|
||||||
|
buttons.insert({ mfg, mfg::get_buttons() });
|
||||||
|
buttons_help.insert({ mfg, mfg::get_buttons_help() });
|
||||||
|
file_hints[mfg].emplace_back("game/MFGClient_Data");
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> &get_games() {
|
const std::vector<std::string> &get_games() {
|
||||||
|
|||||||
@@ -0,0 +1,385 @@
|
|||||||
|
#include "bi2a_hook.h"
|
||||||
|
|
||||||
|
#include "util/execexe.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "util/tapeled.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* class definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct AIO_SCI_COMM {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR_IOB {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__INPUTDATA {
|
||||||
|
uint8_t data[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__OUTPUTDATA {
|
||||||
|
uint8_t data[40];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__INPUT
|
||||||
|
{
|
||||||
|
uint8_t bPcPowerCheck;
|
||||||
|
uint8_t CoinCount;
|
||||||
|
uint8_t bTest;
|
||||||
|
uint8_t bService;
|
||||||
|
uint8_t bCoinSw;
|
||||||
|
uint8_t bCoinJam;
|
||||||
|
uint8_t bCabinet;
|
||||||
|
uint8_t bHPDetect;
|
||||||
|
uint8_t bRecDetect;
|
||||||
|
uint8_t bStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__DEVSTATUS
|
||||||
|
{
|
||||||
|
uint8_t MechType;
|
||||||
|
uint8_t bIsBi2a;
|
||||||
|
uint8_t IoCounter;
|
||||||
|
AIO_IOB_BI2A_VFG__INPUT Input;
|
||||||
|
AIO_IOB_BI2A_VFG__INPUTDATA InputData;
|
||||||
|
AIO_IOB_BI2A_VFG__OUTPUTDATA OutputData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* typedefs
|
||||||
|
*/
|
||||||
|
|
||||||
|
// libaio-iob_video.dll
|
||||||
|
typedef AIO_SCI_COMM* (__fastcall *aioIobBi2a_OpenSciUsbCdc_t)(uint32_t i_SerialNumber);
|
||||||
|
typedef AIO_IOB_BI2A_VFG* (__fastcall *aioIobBi2aVFG_Create_t)(AIO_NMGR_IOB *i_pNodeMgr, uint32_t i_DevId);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetMechType_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, uint32_t i_MechType);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_GetDeviceStatus_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl,
|
||||||
|
AIO_IOB_BI2A_VFG__DEVSTATUS *o_pDevStatus,
|
||||||
|
uint32_t i_cbDevStatus);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetWatchDogTimer_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint16_t i_Count);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_ControlCoinBlocker_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Slot,
|
||||||
|
bool i_bOpen);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_AddCounter_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Counter,
|
||||||
|
uint32_t i_Count);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetAmpVolume_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Amp,
|
||||||
|
uint32_t i_Volume);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_EnableUsbCharger_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, bool i_bEnable);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetLamp_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Lamp, uint8_t i_Bright);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetIccrLed_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_RGB);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetLedData_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint8_t *i_pData,
|
||||||
|
uint32_t i_cbData);
|
||||||
|
|
||||||
|
// libaio-iob.dll
|
||||||
|
typedef AIO_NMGR_IOB* (__fastcall *aioNMgrIob_Create_t)(AIO_SCI_COMM* i_pSci, uint32_t i_bfMode);
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
typedef void (__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB *i_pNodeMgr);
|
||||||
|
typedef int32_t (__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB *i_pNodeMgr);
|
||||||
|
typedef bool (__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State);
|
||||||
|
typedef bool (__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State);
|
||||||
|
typedef void (__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl);
|
||||||
|
typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl);
|
||||||
|
typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State);
|
||||||
|
typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* function pointers
|
||||||
|
*/
|
||||||
|
|
||||||
|
// libaio-iob_video.dll
|
||||||
|
static aioIobBi2a_OpenSciUsbCdc_t aioIobBi2a_OpenSciUsbCdc_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_Create_t aioIobBi2aVFG_Create_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetMechType_t aioIobBi2aVFG_SetMechType_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_GetDeviceStatus_t aioIobBi2aVFG_GetDeviceStatus_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetWatchDogTimer_t aioIobBi2aVFG_SetWatchDogTimer_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_ControlCoinBlocker_t aioIobBi2aVFG_ControlCoinBlocker_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_AddCounter_t aioIobBi2aVFG_AddCounter_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetAmpVolume_t aioIobBi2aVFG_SetAmpVolume_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_EnableUsbCharger_t aioIobBi2aVFG_EnableUsbCharger_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetLamp_t aioIobBi2aVFG_SetLamp_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetIccrLed_t aioIobBi2aVFG_SetIccrLed_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetLedData_t aioIobBi2aVFG_SetLedData_orig = nullptr;
|
||||||
|
|
||||||
|
// libaio-iob.dll
|
||||||
|
static aioNMgrIob_Create_t aioNMgrIob_Create_orig = nullptr;
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
static aioNodeMgr_Destroy_t aioNodeMgr_Destroy_orig = nullptr;
|
||||||
|
static aioNodeMgr_GetState_t aioNodeMgr_GetState_orig = nullptr;
|
||||||
|
static aioNodeMgr_IsReady_t aioNodeMgr_IsReady_orig = nullptr;
|
||||||
|
static aioNodeMgr_IsError_t aioNodeMgr_IsError_orig = nullptr;
|
||||||
|
static aioNodeCtl_Destroy_t aioNodeCtl_Destroy_orig = nullptr;
|
||||||
|
static aioNodeCtl_GetState_t aioNodeCtl_GetState_orig = nullptr;
|
||||||
|
static aioNodeCtl_IsReady_t aioNodeCtl_IsReady_orig = nullptr;
|
||||||
|
static aioNodeCtl_IsError_t aioNodeCtl_IsError_orig = nullptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
static AIO_SCI_COMM *aioSciComm;
|
||||||
|
static AIO_NMGR_IOB *aioNmgrIob;
|
||||||
|
static AIO_IOB_BI2A_VFG *aioIobBi2aVfg;
|
||||||
|
static uint8_t mechType = 0;
|
||||||
|
static uint8_t count = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* implementations
|
||||||
|
*/
|
||||||
|
|
||||||
|
static AIO_SCI_COMM* __fastcall aioIob2Bi2a_OpenSciUsbCdc(uint32_t i_SerialNumber) {
|
||||||
|
log_info("bi2a_hook", "aioIob2Bi2a_OpenSciUsbCdc hook hit");
|
||||||
|
aioSciComm = new AIO_SCI_COMM;
|
||||||
|
return aioSciComm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AIO_IOB_BI2A_VFG* __fastcall aioIobBi2aVFG_Create(AIO_NMGR_IOB *i_pNodeMgr, uint32_t i_DevId) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
log_info("bi2a_hook", "node created");
|
||||||
|
aioIobBi2aVfg = new AIO_IOB_BI2A_VFG;
|
||||||
|
return aioIobBi2aVfg;
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_Create_orig(i_pNodeMgr, i_DevId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetMechType(AIO_IOB_BI2A_VFG *i_pNodeCtl, uint32_t i_MechType) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
mechType = i_MechType;
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetMechType_orig(i_pNodeCtl, i_MechType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_GetDeviceStatus(AIO_IOB_BI2A_VFG* i_pNodeCtl,
|
||||||
|
AIO_IOB_BI2A_VFG__DEVSTATUS *o_pDevStatus,
|
||||||
|
uint32_t i_cbDevStatus) {
|
||||||
|
|
||||||
|
RI_MGR->devices_flush_output();
|
||||||
|
|
||||||
|
if (i_pNodeCtl != aioIobBi2aVfg) {
|
||||||
|
return aioIobBi2aVFG_GetDeviceStatus_orig(i_pNodeCtl, o_pDevStatus, i_cbDevStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(o_pDevStatus, 0x00, sizeof(AIO_IOB_BI2A_VFG__DEVSTATUS));
|
||||||
|
|
||||||
|
o_pDevStatus->MechType = mechType;
|
||||||
|
o_pDevStatus->bIsBi2a = 1;
|
||||||
|
o_pDevStatus->IoCounter = count++;
|
||||||
|
|
||||||
|
auto &buttons = get_buttons();
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]))
|
||||||
|
o_pDevStatus->Input.bTest = 1;
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]))
|
||||||
|
o_pDevStatus->Input.bService = 1;
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]))
|
||||||
|
o_pDevStatus->Input.bCoinSw = 1;
|
||||||
|
|
||||||
|
o_pDevStatus->Input.CoinCount = eamuse_coin_get_stock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetWatchDogTimer(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint16_t i_Count) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_ControlCoinBlocker(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Slot, bool i_bOpen) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_ControlCoinBlocker_orig(i_pNodeCtl, i_Slot, i_bOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_AddCounter(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetAmpVolume(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Amp, uint32_t i_Volume) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetAmpVolume_orig(i_pNodeCtl, i_Amp, i_Volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_EnableUsbCharger(AIO_IOB_BI2A_VFG* i_pNodeCtl, bool i_bEnable) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_EnableUsbCharger_orig(i_pNodeCtl, i_bEnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetLamp(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Lamp, uint8_t i_Bright) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetLamp_orig(i_pNodeCtl, i_Lamp, i_Bright);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetIccrLed(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_RGB) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetIccrLed_orig(i_pNodeCtl, i_RGB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetLedData(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint8_t *i_pData, uint32_t i_cbData) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetLedData_orig(i_pNodeCtl, i_pData, i_cbData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AIO_NMGR_IOB* __fastcall aioNMgrIob_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||||
|
if (i_pSci == aioSciComm) {
|
||||||
|
aioNmgrIob = new AIO_NMGR_IOB;
|
||||||
|
return aioNmgrIob;
|
||||||
|
} else {
|
||||||
|
return aioNMgrIob_Create_orig(i_pSci, i_bfMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioNodeMgr_Destroy(AIO_NMGR_IOB *i_pNodeMgr) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
delete aioNmgrIob;
|
||||||
|
aioNmgrIob = nullptr;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB *i_pNodeMgr) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_GetState_orig(i_pNodeMgr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioNodeCtl_Destroy(AIO_IOB_BI2A_VFG *i_pNodeCtl) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
delete aioIobBi2aVfg;
|
||||||
|
aioIobBi2aVfg = nullptr;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB_BI2A_VFG *i_pNodeCtl) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_GetState_orig(i_pNodeCtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeCtl_IsError(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bi2a_hook_init() {
|
||||||
|
// avoid double init
|
||||||
|
static bool initialized = false;
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// announce
|
||||||
|
log_info("bi2a_hook", "init");
|
||||||
|
|
||||||
|
// libaio-iob_video.dll
|
||||||
|
const auto libaioIobVideoDll = "libaio-iob_video.dll";
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2a_OpenSciUsbCdc",
|
||||||
|
aioIob2Bi2a_OpenSciUsbCdc, &aioIobBi2a_OpenSciUsbCdc_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_Create",
|
||||||
|
aioIobBi2aVFG_Create, &aioIobBi2aVFG_Create_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetMechType",
|
||||||
|
aioIobBi2aVFG_SetMechType, &aioIobBi2aVFG_SetMechType_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_GetDeviceStatus",
|
||||||
|
aioIobBi2aVFG_GetDeviceStatus, &aioIobBi2aVFG_GetDeviceStatus_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetWatchDogTimer",
|
||||||
|
aioIobBi2aVFG_SetWatchDogTimer, &aioIobBi2aVFG_SetWatchDogTimer_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_ControlCoinBlocker",
|
||||||
|
aioIobBi2aVFG_ControlCoinBlocker, &aioIobBi2aVFG_ControlCoinBlocker_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_AddCounter",
|
||||||
|
aioIobBi2aVFG_AddCounter, &aioIobBi2aVFG_AddCounter_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetAmpVolume",
|
||||||
|
aioIobBi2aVFG_SetAmpVolume, &aioIobBi2aVFG_SetAmpVolume_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_EnableUsbCharger",
|
||||||
|
aioIobBi2aVFG_EnableUsbCharger, &aioIobBi2aVFG_EnableUsbCharger_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetLamp",
|
||||||
|
aioIobBi2aVFG_SetLamp, &aioIobBi2aVFG_SetLamp_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetIccrLed",
|
||||||
|
aioIobBi2aVFG_SetIccrLed, &aioIobBi2aVFG_SetIccrLed_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetLedData",
|
||||||
|
aioIobBi2aVFG_SetLedData, &aioIobBi2aVFG_SetLedData_orig);
|
||||||
|
|
||||||
|
// libaio-iob.dll
|
||||||
|
const auto libaioIobDll = "libaio-iob.dll";
|
||||||
|
execexe::trampoline_try(libaioIobDll, "aioNMgrIob_Create",
|
||||||
|
aioNMgrIob_Create, &aioNMgrIob_Create_orig);
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
const auto libaioDll = "libaio.dll";
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
|
||||||
|
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_GetState",
|
||||||
|
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
|
||||||
|
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsError",
|
||||||
|
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
|
||||||
|
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_GetState",
|
||||||
|
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
|
||||||
|
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||||
|
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
void bi2a_hook_init();
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
std::vector<Button> &games::mfg::get_buttons() {
|
||||||
|
static std::vector<Button> buttons;
|
||||||
|
|
||||||
|
if (buttons.empty()) {
|
||||||
|
buttons = GameAPI::Buttons::getButtons("Mahjong Fight Girl");
|
||||||
|
|
||||||
|
GameAPI::Buttons::sortButtons(
|
||||||
|
&buttons,
|
||||||
|
"Service",
|
||||||
|
"Test",
|
||||||
|
"Coin Mech"
|
||||||
|
//"Action Button"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string games::mfg::get_buttons_help() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "cfg/api.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
namespace Buttons {
|
||||||
|
enum {
|
||||||
|
Service,
|
||||||
|
Test,
|
||||||
|
CoinMech,
|
||||||
|
ActionButton
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// getters
|
||||||
|
std::vector<Button> &get_buttons();
|
||||||
|
std::string get_buttons_help();
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#include "mfg.h"
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include "util/fileutils.h"
|
||||||
|
#include "util/unity_player.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
#include "util/execexe.h"
|
||||||
|
#include "acioemu/handle.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "bi2a_hook.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
std::string MFG_INJECT_ARGS = "";
|
||||||
|
std::string MFG_CABINET_TYPE = "HG";
|
||||||
|
bool MFG_NO_IO = false;
|
||||||
|
|
||||||
|
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||||
|
static std::wstring portName;
|
||||||
|
|
||||||
|
void MFGGame::attach() {
|
||||||
|
Game::attach();
|
||||||
|
|
||||||
|
// create required files
|
||||||
|
fileutils::dir_create_recursive("dev/raw/log");
|
||||||
|
fileutils::bin_write("dev/raw/log/output_log.txt", nullptr, 0);
|
||||||
|
|
||||||
|
SetEnvironmentVariableA("VFG_CABINET_TYPE", MFG_CABINET_TYPE.c_str());
|
||||||
|
|
||||||
|
// add card reader
|
||||||
|
portName = MFG_CABINET_TYPE == "UKS" ? L"\\\\.\\COM1" : L"\\\\.\\COM3";
|
||||||
|
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
|
||||||
|
devicehook_init_trampoline();
|
||||||
|
devicehook_add(acioHandle);
|
||||||
|
|
||||||
|
execexe::init();
|
||||||
|
execexe::init_port_hook(portName, acioHandle);
|
||||||
|
|
||||||
|
if (GRAPHICS_SHOW_CURSOR) {
|
||||||
|
unity_utils::force_show_cursor(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
unity_utils::set_args(
|
||||||
|
std::format("{} {}{}",
|
||||||
|
GetCommandLineA(),
|
||||||
|
MFG_INJECT_ARGS,
|
||||||
|
unity_utils::get_unity_player_args()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MFGGame::post_attach() {
|
||||||
|
Game::post_attach();
|
||||||
|
|
||||||
|
execexe::load_library("libaio.dll");
|
||||||
|
execexe::load_library("libaio-iob.dll");
|
||||||
|
execexe::load_library("libaio-iob_video.dll");
|
||||||
|
execexe::load_library("libaio-iob2_video.dll");
|
||||||
|
execexe::load_library("win10actlog.dll");
|
||||||
|
|
||||||
|
if (!MFG_NO_IO) {
|
||||||
|
// insert BI2* hooks
|
||||||
|
if (MFG_CABINET_TYPE == "UKS") {
|
||||||
|
log_fatal("mfg", "UKS io is not supported");
|
||||||
|
} else {
|
||||||
|
bi2a_hook_init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MFGGame::detach() {
|
||||||
|
Game::detach();
|
||||||
|
|
||||||
|
devicehook_dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "games/game.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
extern std::string MFG_INJECT_ARGS;
|
||||||
|
extern std::string MFG_CABINET_TYPE;
|
||||||
|
extern bool MFG_NO_IO;
|
||||||
|
|
||||||
|
class MFGGame : public games::Game {
|
||||||
|
public:
|
||||||
|
MFGGame() : Game("Mahjong Fight Girl") {}
|
||||||
|
|
||||||
|
virtual void attach() override;
|
||||||
|
virtual void post_attach() override;
|
||||||
|
virtual void detach() override;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
#include "games/ccj/ccj.h"
|
#include "games/ccj/ccj.h"
|
||||||
#include "games/ccj/trackball.h"
|
#include "games/ccj/trackball.h"
|
||||||
#include "games/qks/qks.h"
|
#include "games/qks/qks.h"
|
||||||
|
#include "games/mfg/mfg.h"
|
||||||
#include "games/museca/museca.h"
|
#include "games/museca/museca.h"
|
||||||
#include "hooks/avshook.h"
|
#include "hooks/avshook.h"
|
||||||
#include "hooks/audio/audio.h"
|
#include "hooks/audio/audio.h"
|
||||||
@@ -213,6 +214,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bool attach_bc = false;
|
bool attach_bc = false;
|
||||||
bool attach_ccj = false;
|
bool attach_ccj = false;
|
||||||
bool attach_qks = false;
|
bool attach_qks = false;
|
||||||
|
bool attach_mfg = false;
|
||||||
bool attach_museca = false;
|
bool attach_museca = false;
|
||||||
|
|
||||||
// misc settings
|
// misc settings
|
||||||
@@ -602,6 +604,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::LoadQKSModule].value_bool()) {
|
if (options[launcher::Options::LoadQKSModule].value_bool()) {
|
||||||
attach_qks = true;
|
attach_qks = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::LoadMFGModule].value_bool()) {
|
||||||
|
attach_mfg = true;
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
|
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
|
||||||
attach_museca = true;
|
attach_museca = true;
|
||||||
}
|
}
|
||||||
@@ -1028,6 +1033,15 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::QKSArgs].is_active()) {
|
if (options[launcher::Options::QKSArgs].is_active()) {
|
||||||
games::qks::QKS_INJECT_ARGS = options[launcher::Options::QKSArgs].value_text();
|
games::qks::QKS_INJECT_ARGS = options[launcher::Options::QKSArgs].value_text();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::MFGArgs].is_active()) {
|
||||||
|
games::mfg::MFG_INJECT_ARGS = options[launcher::Options::MFGArgs].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::MFGCabType].is_active()) {
|
||||||
|
games::mfg::MFG_CABINET_TYPE = options[launcher::Options::MFGCabType].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::MFGNoIO].is_active()) {
|
||||||
|
games::mfg::MFG_NO_IO = options[launcher::Options::MFGNoIO].value_bool();
|
||||||
|
}
|
||||||
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
|
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
|
||||||
rawinput::ENABLE_SMX_STAGE = true;
|
rawinput::ENABLE_SMX_STAGE = true;
|
||||||
}
|
}
|
||||||
@@ -1589,6 +1603,19 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mahjong Fight Girl
|
||||||
|
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/MFGClient_Data")) {
|
||||||
|
avs::game::DLL_NAME = "kamunity.dll";
|
||||||
|
attach_io = true;
|
||||||
|
attach_mfg = true;
|
||||||
|
launcher::signal::USE_VEH_WORKAROUND = true;
|
||||||
|
// automatically show cursor when no touchscreen is available
|
||||||
|
if (!is_touch_available()) {
|
||||||
|
GRAPHICS_SHOW_CURSOR = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Busou Shinki: Armored Princess Battle Conductor
|
// Busou Shinki: Armored Princess Battle Conductor
|
||||||
if (check_dll("kamunity.dll") && fileutils::file_exists("game/bsac_app.exe")) {
|
if (check_dll("kamunity.dll") && fileutils::file_exists("game/bsac_app.exe")) {
|
||||||
avs::game::DLL_NAME = "kamunity.dll";
|
avs::game::DLL_NAME = "kamunity.dll";
|
||||||
@@ -1740,6 +1767,10 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
avs::core::set_default_heap_size("kamunity.dll");
|
avs::core::set_default_heap_size("kamunity.dll");
|
||||||
games.push_back(new games::qks::QKSGame());
|
games.push_back(new games::qks::QKSGame());
|
||||||
}
|
}
|
||||||
|
if (attach_mfg) {
|
||||||
|
avs::core::set_default_heap_size("kamunity.dll");
|
||||||
|
games.push_back(new games::mfg::MFGGame());
|
||||||
|
}
|
||||||
|
|
||||||
// apply user heap size, if defined
|
// apply user heap size, if defined
|
||||||
if (user_heap_size > 0) {
|
if (user_heap_size > 0) {
|
||||||
|
|||||||
@@ -1006,6 +1006,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.game_name = "QuizKnock STADIUM",
|
.game_name = "QuizKnock STADIUM",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// LoadMFGModule
|
||||||
|
.title = "Force Load Mahjong Fight Girl",
|
||||||
|
.name = "mfg",
|
||||||
|
.desc = "manually enable Mahjong Fight Girl module",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options (Advanced)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// LoadMusecaModule
|
// LoadMusecaModule
|
||||||
.title = "Force Load Museca",
|
.title = "Force Load Museca",
|
||||||
@@ -1671,6 +1680,39 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.game_name = "Chase Chase Jokers",
|
.game_name = "Chase Chase Jokers",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.title = "MFG Arguments Override",
|
||||||
|
.name = "mfgargs",
|
||||||
|
.desc = "Command line arguments passed to the game.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "",
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options (Advanced)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "MFG Cabinet Type",
|
||||||
|
.name = "mfgcabtype",
|
||||||
|
.desc = "MFG Cabinet Type. Default is HG.",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.setting_name = "",
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options (Advanced)",
|
||||||
|
.elements = {
|
||||||
|
{"HG", "HG"},
|
||||||
|
{"B", "B"},
|
||||||
|
{"C", "C"},
|
||||||
|
{"UKS", "UKS"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "MFG Disable IO Emulation",
|
||||||
|
.name = "mfgnoio",
|
||||||
|
.desc = "Disables BI2X hooks for MFG",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.setting_name = "",
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options (Advanced)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// spice2x_LightsOverallBrightness
|
// spice2x_LightsOverallBrightness
|
||||||
.title = "Lights Brightness Adjustment",
|
.title = "Lights Brightness Adjustment",
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ namespace launcher {
|
|||||||
LoadBusouShinkiModule,
|
LoadBusouShinkiModule,
|
||||||
LoadCCJModule,
|
LoadCCJModule,
|
||||||
LoadQKSModule,
|
LoadQKSModule,
|
||||||
|
LoadMFGModule,
|
||||||
LoadMusecaModule,
|
LoadMusecaModule,
|
||||||
PathToModules,
|
PathToModules,
|
||||||
ScreenshotFolder,
|
ScreenshotFolder,
|
||||||
@@ -193,6 +194,9 @@ namespace launcher {
|
|||||||
CCJMouseTrackball,
|
CCJMouseTrackball,
|
||||||
CCJMouseTrackballWithToggle,
|
CCJMouseTrackballWithToggle,
|
||||||
CCJTrackballSensitivity,
|
CCJTrackballSensitivity,
|
||||||
|
MFGArgs,
|
||||||
|
MFGCabType,
|
||||||
|
MFGNoIO,
|
||||||
spice2x_LightsOverallBrightness,
|
spice2x_LightsOverallBrightness,
|
||||||
spice2x_WindowBorder,
|
spice2x_WindowBorder,
|
||||||
spice2x_WindowSize,
|
spice2x_WindowSize,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace launcher::signal {
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
bool DISABLE = false;
|
bool DISABLE = false;
|
||||||
|
bool USE_VEH_WORKAROUND = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define V(variant) case variant: return #variant
|
#define V(variant) case variant: return #variant
|
||||||
@@ -167,7 +168,7 @@ static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
|||||||
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||||||
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
||||||
|
|
||||||
return nullptr;
|
return launcher::signal::USE_VEH_WORKAROUND ? INVALID_HANDLE_VALUE : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void launcher::signal::attach() {
|
void launcher::signal::attach() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace launcher::signal {
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern bool DISABLE;
|
extern bool DISABLE;
|
||||||
|
extern bool USE_VEH_WORKAROUND;
|
||||||
|
|
||||||
//void print_stacktrace();
|
//void print_stacktrace();
|
||||||
void attach();
|
void attach();
|
||||||
|
|||||||
@@ -653,6 +653,8 @@ void eamuse_autodetect_game() {
|
|||||||
eamuse_set_game("Chase Chase Jokers");
|
eamuse_set_game("Chase Chase Jokers");
|
||||||
else if (avs::game::is_model("UKS"))
|
else if (avs::game::is_model("UKS"))
|
||||||
eamuse_set_game("QuizKnock STADIUM");
|
eamuse_set_game("QuizKnock STADIUM");
|
||||||
|
else if (avs::game::is_model("VFG"))
|
||||||
|
eamuse_set_game("Mahjong Fight Girl");
|
||||||
else {
|
else {
|
||||||
log_warning("eamuse", "unknown game model: {}", avs::game::MODEL);
|
log_warning("eamuse", "unknown game model: {}", avs::game::MODEL);
|
||||||
eamuse_set_game("unknown");
|
eamuse_set_game("unknown");
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
#include "execexe.h"
|
||||||
|
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/libutils.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
|
||||||
|
namespace execexe {
|
||||||
|
|
||||||
|
static HMODULE execexe_module = nullptr;
|
||||||
|
static decltype(&LoadLibraryW) execexe_LoadLibraryW = nullptr;
|
||||||
|
static decltype(&GetModuleHandleW) execexe_GetModuleHandleW = nullptr;
|
||||||
|
static decltype(&GetProcAddress) execexe_GetProcAddress = nullptr;
|
||||||
|
static decltype(&CreateFileA) execexe_CreateFileA = nullptr;
|
||||||
|
static decltype(&CreateFileW) execexe_CreateFileW = nullptr;
|
||||||
|
static decltype(&CloseHandle) execexe_CloseHandle = nullptr;
|
||||||
|
|
||||||
|
static std::wstring plugins_dir;
|
||||||
|
static acioemu::ACIOHandle *acio = nullptr;
|
||||||
|
static std::wstring port_name;
|
||||||
|
static bool port_opened = false;
|
||||||
|
|
||||||
|
static HANDLE WINAPI execexe_CreateFileA_hook(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||||
|
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||||
|
const auto lpFileNameW = s2ws(lpFileName);
|
||||||
|
if (lpFileNameW == port_name) {
|
||||||
|
if (!port_opened) {
|
||||||
|
port_opened = acio->open(port_name.c_str());
|
||||||
|
} else {
|
||||||
|
log_info("execexe", "ignored handle open. ({})", ws2s(port_name));
|
||||||
|
}
|
||||||
|
SetLastError(0);
|
||||||
|
return (HANDLE) acio;
|
||||||
|
}
|
||||||
|
return execexe_CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||||
|
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HANDLE WINAPI execexe_CreateFileW_hook(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||||
|
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||||
|
if (lpFileName == port_name && acio->open(lpFileName)) {
|
||||||
|
if (!port_opened) {
|
||||||
|
port_opened = acio->open(port_name.c_str());
|
||||||
|
} else {
|
||||||
|
log_info("execexe", "ignored handle open. ({})", ws2s(port_name));
|
||||||
|
}
|
||||||
|
SetLastError(0);
|
||||||
|
return (HANDLE) acio;
|
||||||
|
} else {
|
||||||
|
return execexe_CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||||
|
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static WINBOOL WINAPI execexe_CloseHandle_hook(HANDLE hObject) {
|
||||||
|
if (hObject == acio && port_opened) {
|
||||||
|
log_info("execexe", "ignored handle close. ({})", ws2s(port_name));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return execexe_CloseHandle(hObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE init() {
|
||||||
|
execexe_module = libutils::load_library("execexe.dll");
|
||||||
|
execexe_LoadLibraryW = libutils::get_proc<decltype(&LoadLibraryW)>(execexe_module, MAKEINTRESOURCE(34));
|
||||||
|
execexe_GetModuleHandleW = libutils::get_proc<decltype(&GetModuleHandleW)>(execexe_module, MAKEINTRESOURCE(25));
|
||||||
|
execexe_GetProcAddress = libutils::get_proc<decltype(&GetProcAddress)>(execexe_module, MAKEINTRESOURCE(27));
|
||||||
|
execexe_CloseHandle = libutils::get_proc<decltype(&CloseHandle)>(execexe_module, MAKEINTRESOURCE(7));
|
||||||
|
execexe_CreateFileA = libutils::get_proc<decltype(&CreateFileA)>(execexe_module, MAKEINTRESOURCE(9));
|
||||||
|
execexe_CreateFileW = libutils::get_proc<decltype(&CreateFileW)>(execexe_module, MAKEINTRESOURCE(11));
|
||||||
|
|
||||||
|
auto module_path = libutils::module_file_name(nullptr);
|
||||||
|
module_path = module_path.replace_extension("");
|
||||||
|
module_path = module_path.replace_filename(module_path.filename().wstring() + L"_Data");
|
||||||
|
plugins_dir = (module_path / L"Plugins" / L"x86_64").wstring() + L"\\";
|
||||||
|
|
||||||
|
return execexe_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle) {
|
||||||
|
static bool init = false;
|
||||||
|
if (init)
|
||||||
|
return;
|
||||||
|
init = true;
|
||||||
|
|
||||||
|
port_name = portName;
|
||||||
|
acio = acioHandle;
|
||||||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(7),
|
||||||
|
execexe_CloseHandle_hook, &execexe_CloseHandle);
|
||||||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
|
||||||
|
execexe_CreateFileA_hook, &execexe_CreateFileA);
|
||||||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||||
|
execexe_CreateFileW_hook, &execexe_CreateFileW);
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE load_library(const char *module_name, bool fatal) {
|
||||||
|
std::wstring module_name_w = s2ws(module_name);
|
||||||
|
std::wstring plugin_path = plugins_dir + module_name_w;
|
||||||
|
HMODULE module = execexe_LoadLibraryW(plugin_path.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
module = execexe_LoadLibraryW(module_name_w.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
log_fatal("execexe", "failed to load library {}", module_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE get_module(const char *module_name, bool fatal) {
|
||||||
|
std::wstring module_name_w = s2ws(module_name);
|
||||||
|
std::wstring plugin_path = plugins_dir + module_name_w;
|
||||||
|
|
||||||
|
HMODULE module = execexe_GetModuleHandleW(plugin_path.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
module = execexe_GetModuleHandleW(module_name_w.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
log_fatal("execexe", "failed to get module {}", module_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC get_proc(HMODULE module, const char *proc_name, bool fatal) {
|
||||||
|
FARPROC proc = execexe_GetProcAddress(module, proc_name);
|
||||||
|
if (proc != nullptr) {
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
log_fatal("execexe", "proc {} not found", proc_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trampoline(const char *dll, const char *func, void *hook, void **orig) {
|
||||||
|
HMODULE module = get_module(dll);
|
||||||
|
FARPROC proc = get_proc(module, func);
|
||||||
|
|
||||||
|
return detour::trampoline(
|
||||||
|
reinterpret_cast<void *>(proc),
|
||||||
|
hook,
|
||||||
|
orig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trampoline_try(const char *dll, const char *func, void *hook, void **orig) {
|
||||||
|
HMODULE module = get_module(dll, false);
|
||||||
|
if (module == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC proc = get_proc(module, func, false);
|
||||||
|
if (proc == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return detour::trampoline(
|
||||||
|
reinterpret_cast<void *>(proc),
|
||||||
|
hook,
|
||||||
|
orig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "acioemu/handle.h"
|
||||||
|
|
||||||
|
namespace execexe {
|
||||||
|
HMODULE init();
|
||||||
|
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle);
|
||||||
|
HMODULE load_library(const char *module_name, bool fatal = true);
|
||||||
|
FARPROC get_proc(HMODULE module, const char *proc_name, bool fatal = true);
|
||||||
|
HMODULE get_module(const char *module_name, bool fatal = true);
|
||||||
|
bool trampoline(const char *dll, const char *func, void *hook, void **orig);
|
||||||
|
bool trampoline_try(const char *dll, const char *func, void *hook, void **orig);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T get_proc(HMODULE module, const char *proc_name, bool fatal = true) {
|
||||||
|
return reinterpret_cast<T>(get_proc(module, proc_name, fatal));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool trampoline(const char *dll, const char *func, T hook, T *orig) {
|
||||||
|
return trampoline(
|
||||||
|
dll,
|
||||||
|
func,
|
||||||
|
reinterpret_cast<void *>(hook),
|
||||||
|
reinterpret_cast<void **>(orig));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool trampoline_try(const char *dll, const char *func, T hook, T *orig) {
|
||||||
|
return trampoline_try(
|
||||||
|
dll,
|
||||||
|
func,
|
||||||
|
reinterpret_cast<void *>(hook),
|
||||||
|
reinterpret_cast<void **>(orig));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,44 +2,83 @@
|
|||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "external/fmt/include/fmt/format.h"
|
#include "external/fmt/include/fmt/format.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
namespace unity_utils {
|
namespace unity_utils {
|
||||||
|
|
||||||
std::string get_unity_player_args() {
|
std::string get_unity_player_args() {
|
||||||
std::string args = "";
|
std::string args = "";
|
||||||
// windowed
|
// windowed
|
||||||
if (GRAPHICS_WINDOWED) {
|
if (GRAPHICS_WINDOWED) {
|
||||||
args += " -screen-fullscreen 0";
|
args += " -screen-fullscreen 0";
|
||||||
|
|
||||||
// window size - by default unity player will attempt to create a window that fills the
|
// window size - by default unity player will attempt to create a window that fills the
|
||||||
// screen, so instead fall back to 1080p resolution
|
// screen, so instead fall back to 1080p resolution
|
||||||
uint32_t w = 1920;
|
uint32_t w = 1920;
|
||||||
uint32_t h = 1080;
|
uint32_t h = 1080;
|
||||||
if (GRAPHICS_WINDOW_SIZE.has_value()) {
|
if (GRAPHICS_WINDOW_SIZE.has_value()) {
|
||||||
w = GRAPHICS_WINDOW_SIZE.value().first;
|
w = GRAPHICS_WINDOW_SIZE.value().first;
|
||||||
h = GRAPHICS_WINDOW_SIZE.value().second;
|
h = GRAPHICS_WINDOW_SIZE.value().second;
|
||||||
}
|
|
||||||
args += fmt::format(" -screen-width {} -screen-height {}", w, h);
|
|
||||||
|
|
||||||
// window border
|
|
||||||
// eventually we should launch the player inside a parent window that we have full control
|
|
||||||
// over using -parentHWND so we can let the user resize it by dragging the border...
|
|
||||||
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
|
||||||
if (GRAPHICS_WINDOW_STYLE == cfg::WindowDecorationMode::Borderless) {
|
|
||||||
args += " -popupwindow";
|
|
||||||
}
|
}
|
||||||
|
args += fmt::format(" -screen-width {} -screen-height {}", w, h);
|
||||||
|
|
||||||
|
// window border
|
||||||
|
// eventually we should launch the player inside a parent window that we have full control
|
||||||
|
// over using -parentHWND so we can let the user resize it by dragging the border...
|
||||||
|
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
||||||
|
if (GRAPHICS_WINDOW_STYLE == cfg::WindowDecorationMode::Borderless) {
|
||||||
|
args += " -popupwindow";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// need to specify this, otherwise it gets cached and uses previous value
|
||||||
|
args += " -screen-fullscreen 1";
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// need to specify this, otherwise it gets cached and uses previous value
|
// monitor
|
||||||
args += " -screen-fullscreen 1";
|
if (D3D9_ADAPTER.has_value()) {
|
||||||
|
args += fmt::format(" -monitor {}", D3D9_ADAPTER.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// monitor
|
static std::string cmdLine;
|
||||||
if (D3D9_ADAPTER.has_value()) {
|
static decltype(GetCommandLineA) *GetCommandLineA_orig = nullptr;
|
||||||
args += fmt::format(" -monitor {}", D3D9_ADAPTER.value());
|
|
||||||
|
static LPSTR WINAPI GetCommandLineA_hook() {
|
||||||
|
return (LPSTR) cmdLine.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
void set_args(const std::string &args) {
|
||||||
}
|
static bool init = false;
|
||||||
|
if (!init) {
|
||||||
|
init = true;
|
||||||
|
detour::trampoline_try("kernel32.dll", "GetCommandLineA",
|
||||||
|
(void*)GetCommandLineA_hook, (void**)&GetCommandLineA_orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdLine = args;
|
||||||
|
log_info("unity", "unity player args: ```{}```", cmdLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool show = false;
|
||||||
|
static decltype(ShowCursor) *ShowCursor_orig = nullptr;
|
||||||
|
|
||||||
|
static int WINAPI ShowCursor_hook(BOOL bShow) {
|
||||||
|
return show;
|
||||||
|
}
|
||||||
|
|
||||||
|
void force_show_cursor(bool bShow) {
|
||||||
|
static bool init = false;
|
||||||
|
if (!init) {
|
||||||
|
init = true;
|
||||||
|
detour::trampoline_try("user32.dll", "ShowCursor",
|
||||||
|
(void*)ShowCursor_hook, (void**)&ShowCursor_orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
show = bShow;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,4 +4,6 @@
|
|||||||
|
|
||||||
namespace unity_utils {
|
namespace unity_utils {
|
||||||
std::string get_unity_player_args();
|
std::string get_unity_player_args();
|
||||||
|
void set_args(const std::string &args);
|
||||||
|
void force_show_cursor(bool bShow);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user