spicetools/games/shogikai/shogikai.cpp

478 lines
16 KiB
C++

#include "shogikai.h"
#include "acio/icca/icca.h"
#include "avs/game.h"
#include "hooks/graphics/graphics.h"
#include "misc/eamuse.h"
#include "rawinput/rawinput.h"
#include "touch/touch.h"
#include "util/detour.h"
#include "util/libutils.h"
#include "util/logging.h"
#include "util/utils.h"
#include "io.h"
namespace games::shogikai {
// touch stuff
static int TOUCH_MAX_X = 1360;
static int TOUCH_MAX_Y = 768;
static bool TOUCH_ATTACHED = false;
static bool TOUCH_PRESENT = false;
static bool TOUCH_LAST_PRESENT = false;
static TouchPoint TOUCH_CURRENT {};
static TouchPoint TOUCH_LAST {};
struct joystick_state {
bool up;
bool down;
bool start;
bool service;
bool test;
};
// general i/o stuff
static struct joystick_state JOY_STATE {};
static struct joystick_state JOY_PREVIOUS_STATE {};
// registry hook
static decltype(RegOpenKeyExA) *RegOpenKeyExA_orig = nullptr;
static decltype(RegQueryValueExA) *RegQueryValueExA_orig = nullptr;
// ini hook
typedef void (__cdecl *inifile_param_num_t)(const char *, int *);
static inifile_param_num_t inifile_param_num_orig = nullptr;
// ic card stuff
static bool CARD_IN = false;
static uint8_t CARD_TYPE = 0;
static uint8_t CARD_UID[8];
static void __cdecl system_error_set(uint8_t code1, uint16_t code2, uint16_t code3, const char *error_type) {
if (code1 == 5 && code2 == 1502) {
log_warning("shogikai", "UNABLE TO SET MONITOR MODE {}", GRAPHICS_WINDOWED ? "" : "(TRY WINDOWED MODE!)");
}
log_fatal("shogikai", "{}: {:d}-{:04d}-{:04d}",
error_type == nullptr ? "UNKNOWN ERROR" : error_type,
code1, code2, code3);
}
static void __cdecl inifile_param_num(const char *param, int *value) {
if (param && strcmp(param, "WINDOWED") == 0) {
*value = GRAPHICS_WINDOWED;
} else if (inifile_param_num_orig) {
inifile_param_num_orig(param, value);
if (value) {
log_info("shogikai", "{}={}", param, *value);
}
} else {
*value = 0;
}
}
static LSTATUS WINAPI RegOpenKeyExA_hook(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions,
REGSAM samDesired, PHKEY phkResult) {
if (lpSubKey) {
if (_stricmp(lpSubKey, "HARDWARE\\DESCRIPTION\\System") == 0) {
return ERROR_SUCCESS;
} else if (_stricmp(lpSubKey, "SYSTEM\\CurrentControlSet\\Services\\ialm\\Enum") == 0) {
return ERROR_SUCCESS;
}
}
return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult);
}
static LSTATUS WINAPI RegQueryValueExA_hook(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved,
LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) {
if (lpValueName) {
if (strcmp(lpValueName, "SystemBiosDate") == 0) {
static const char DATA[] = "04/20/69";
memcpy(lpData, DATA, sizeof(DATA));
return ERROR_SUCCESS;
} else if (strcmp(lpValueName, "0") == 0) {
static const char DATA[] = "0";
memcpy(lpData, DATA, sizeof(DATA));
return ERROR_SUCCESS;
}
}
return RegQueryValueExA_orig(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
}
static bool __cdecl nic_dhcp_maybe_exist() {
return false;
}
static void __cdecl mouse_utl_step() {
}
static void __cdecl touch_init(int width, int height) {
log_info("mfc", "call touch_init(width: {}, height: {})", width, height);
// attach touch module
if (!TOUCH_ATTACHED) {
/*
* Find the game window.
* We check the foreground window first, then fall back to searching for the window title
*/
HWND wnd = GetForegroundWindow();
if (!string_begins_with(GetActiveWindowTitle(), avs::game::MODEL)) {
wnd = FindWindowBeginsWith(avs::game::MODEL);
}
// attach touch hook
touch_create_wnd(wnd, true);
// store touch size specification
TOUCH_MAX_X = width;
TOUCH_MAX_Y = height;
// show cursor
if (GRAPHICS_SHOW_CURSOR) {
ShowCursor(TRUE);
}
// set attached
TOUCH_ATTACHED = true;
}
}
static void __cdecl touch_step() {
if (TOUCH_ATTACHED) {
// get touch points
std::vector<TouchPoint> touch_points;
touch_get_points(touch_points);
TOUCH_LAST = TOUCH_CURRENT;
TOUCH_LAST_PRESENT = TOUCH_PRESENT;
if (!touch_points.empty()) {
auto &tp = touch_points[0];
TOUCH_CURRENT = tp;
TOUCH_PRESENT = true;
} else {
TOUCH_PRESENT = false;
}
}
}
static int __cdecl h8io_touch_getpos(int *pX, int *pY) {
if (!TOUCH_PRESENT) {
// false value means no touch present
return 0;
}
*pX = TOUCH_CURRENT.x > TOUCH_MAX_X ? TOUCH_MAX_X : TOUCH_CURRENT.x;
*pY = TOUCH_CURRENT.y > TOUCH_MAX_Y ? TOUCH_MAX_Y : TOUCH_CURRENT.y;
// true value means touch present
return 1;
}
static int __cdecl h8io_touch_getpos_trig(int *pX, int *pY) {
// following the game logic, a trigger check bails early if there was a touch detected
// during the last frame
if (TOUCH_LAST_PRESENT) {
return 0;
}
return h8io_touch_getpos(pX, pY);
}
static void __cdecl touch_get_raw_data(int *pX, int *pY) {
h8io_touch_getpos(pX, pY);
}
static int __cdecl touch_get_raw_data_trig(int *pX, int *pY) {
return h8io_touch_getpos_trig(pX, pY);
}
static char __cdecl mfc5_begin_io_mng(int a1, int a2) {
log_info("mfc", "call mfc5_begin_io_mng");
return 1;
}
static int __cdecl mfc5_is_io_mng_ready() {
log_info("mfc", "call mfc5_is_io_mng_ready");
return 1;
}
// the joystick state functions return the current state that is updated
// in `joystick_step` while ensuring to track the previous state to detect
// held buttons and newly pressed buttons
static void __cdecl joystick_step() {
// set last state
JOY_PREVIOUS_STATE = JOY_STATE;
// get buttons
auto &buttons = get_buttons();
// set new state
JOY_STATE.service = GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::Service));
JOY_STATE.test = GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::Test));
JOY_STATE.start = GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::Select));
}
static bool __cdecl joy_up(int a1) {
return JOY_STATE.up;
}
static bool __cdecl joy_up_on(int a1) {
return JOY_STATE.up && !JOY_PREVIOUS_STATE.up;
}
static bool __cdecl joy_up_rep(int a1) {
return JOY_STATE.up && JOY_PREVIOUS_STATE.up;
}
static bool __cdecl joy_down(int a1) {
return JOY_STATE.down;
}
static bool __cdecl joy_down_on(int a1) {
return JOY_STATE.down && !JOY_PREVIOUS_STATE.down;
}
static bool __cdecl joy_down_rep(int a1) {
return JOY_STATE.down && JOY_PREVIOUS_STATE.down;
}
static bool __cdecl joy_start(int a1) {
static bool FIRST_CHECK = true;
// following the game logic, the first poll of the start button must
// be true for center mode
if (FIRST_CHECK) {
FIRST_CHECK = false;
return true;
}
return JOY_STATE.start;
}
static bool __cdecl joy_start_on(int a1) {
return JOY_STATE.start && !JOY_PREVIOUS_STATE.start;
}
static bool __cdecl joy_start_rep(int a1) {
return JOY_STATE.start && JOY_PREVIOUS_STATE.start;
}
static bool __cdecl joy_service() {
return JOY_STATE.service;
}
static bool __cdecl joy_service_on() {
return JOY_STATE.service && !JOY_PREVIOUS_STATE.service;
}
static bool __cdecl joy_test() {
return JOY_STATE.test;
}
static bool __cdecl joy_test_on() {
return JOY_STATE.test && !JOY_PREVIOUS_STATE.test;
}
static bool __cdecl mfc5_center_iob_get_coin_state() {
auto &buttons = get_buttons();
return GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::CoinMech));
}
static int __cdecl mfc5_center_iob_get_coin_cntr(int a1) {
return eamuse_coin_get_stock();
}
static int __cdecl mfc5_center_iob_current_coin_counter() {
return mfc5_center_iob_get_coin_cntr(0);
}
static void __cdecl mfc5_center_iob_consume_coin_counter(int a1) {
for (int i = 0; i < a1 && a1 >= 0; i++) {
eamuse_coin_consume_stock();
}
}
static int __cdecl mfc5_current_coin_counter() {
return eamuse_coin_get_stock();
}
static void __cdecl mfc5_consume_coin_counter(int a1) {
for (int i = 0; i < a1 && a1 >= 0; i++) {
eamuse_coin_consume_stock();
}
}
static void __cdecl mfc5_iob_lamp(uint8_t lamp) {
auto &lights = get_lights();
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::Left), (lamp & 0x08) ? 1.f : 0.f);
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::Right), (lamp & 0x04) ? 1.f : 0.f);
RI_MGR->devices_flush_output();
}
static void update_card() {
if (eamuse_card_insert_consume(1, 0)) {
if (!CARD_IN) {
CARD_IN = true;
eamuse_get_card(1, 0, CARD_UID);
CARD_TYPE = is_card_uid_felica(CARD_UID) ? 1 : 0;
}
} else {
CARD_IN = false;
}
}
static int __cdecl mfc5_ic_card_read_init() {
//log_misc("mfc", "mfc5_ic_card_read_init");
CARD_IN = false;
CARD_TYPE = 0;
memset(CARD_UID, 0, sizeof(CARD_UID));
return 0;
}
static int __cdecl mfc5_ic_card_read_step() {
//log_misc("mfc", "mfc5_ic_card_read_step");
update_card();
return CARD_IN ? 0 : 1;
}
static int __cdecl mfc5_ic_card_status() {
//log_misc("mfc", "mfc5_ic_card_status");
return CARD_IN ? 2 : 0;
}
static void __cdecl mfc5_get_ic_card_id_type(uint8_t *a1) {
//log_misc("mfc", "mfc5_get_ic_card_id_type(a1: {})", fmt::ptr(a1));
*a1 = CARD_TYPE;
}
static void __cdecl mfc5_get_ic_card_id(uint8_t *a1) {
//log_misc("mfc", "mfc5_get_ic_card_id(a1: {})", fmt::ptr(a1));
memcpy(a1, CARD_UID, 8);
}
ShogikaiGame::ShogikaiGame() : Game("Shogikai") {
}
void ShogikaiGame::attach() {
Game::attach();
// hook error call
detour::inline_hook((void *) system_error_set, libutils::try_proc(
libutils::try_module("error.dll"), "?system_error_set@@YAXEGGPAD@Z"));
detour::iat("?inifile_param_num@@YAHPBDPAH@Z", inifile_param_num);
// get rid of errors when trying to get BIOS information
RegOpenKeyExA_orig = detour::iat("RegOpenKeyExA", RegOpenKeyExA_hook);
RegQueryValueExA_orig = detour::iat("RegQueryValueExA", RegQueryValueExA_hook);
// network fix
detour::iat_try("?nic_dhcp_maybe_exist@@YAEXZ", nic_dhcp_maybe_exist);
// cursor fix
auto util_module = libutils::try_module("util.dll");
if (GRAPHICS_SHOW_CURSOR) {
detour::inline_hook((void *) mouse_utl_step, libutils::try_proc(
util_module, "?mouse_utl_step@@YAXXZ"));
}
// touch i/o
auto h8io_module = libutils::try_module("h8io.dll");
detour::inline_hook((void *) h8io_touch_getpos, libutils::try_proc(
h8io_module, "?h8io_touch_getpos@@YAHPAH0@Z"));
detour::inline_hook((void *) h8io_touch_getpos_trig, libutils::try_proc(
h8io_module, "?h8io_touch_getpos_trig@@YAHPAH0@Z"));
detour::inline_hook((void *) touch_get_raw_data, libutils::try_proc(
h8io_module, "?touch_get_raw_data@@YAXPAH0@Z"));
detour::inline_hook((void *) touch_get_raw_data_trig, libutils::try_proc(
h8io_module, "?touch_get_raw_data_trig@@YAHPAH0@Z"));
detour::inline_hook((void *) touch_init, libutils::try_proc(
h8io_module, "?touch_init@@YAXHH@Z"));
detour::inline_hook((void *) touch_step, libutils::try_proc(
h8io_module, "?touch_step@@YAXXZ"));
// general i/o
detour::inline_hook((void *) mfc5_begin_io_mng, libutils::try_proc(
h8io_module, "?mfc5_begin_io_mng@@YAEHH@Z"));
detour::inline_hook((void *) mfc5_is_io_mng_ready, libutils::try_proc(
h8io_module, "?mfc5_is_io_mng_ready@@YAHXZ"));
detour::inline_hook((void *) joystick_step, libutils::try_proc(
h8io_module, "?joystick_step@@YAXXZ"));
detour::inline_hook((void *) joy_up, libutils::try_proc(
h8io_module, "?joy_up@@YAHH@Z"));
detour::inline_hook((void *) joy_up_on, libutils::try_proc(
h8io_module, "?joy_up_on@@YAHH@Z"));
detour::inline_hook((void *) joy_up_rep, libutils::try_proc(
h8io_module, "?joy_up_rep@@YAHH@Z"));
detour::inline_hook((void *) joy_down, libutils::try_proc(
h8io_module, "?joy_down@@YAHH@Z"));
detour::inline_hook((void *) joy_down_on, libutils::try_proc(
h8io_module, "?joy_down_on@@YAHH@Z"));
detour::inline_hook((void *) joy_down_rep, libutils::try_proc(
h8io_module, "?joy_down_rep@@YAHH@Z"));
detour::inline_hook((void *) joy_start, libutils::try_proc(
h8io_module, "?joy_start@@YAHH@Z"));
detour::inline_hook((void *) joy_start_on, libutils::try_proc(
h8io_module, "?joy_start_on@@YAHH@Z"));
detour::inline_hook((void *) joy_start_rep, libutils::try_proc(
h8io_module, "?joy_start_rep@@YAHH@Z"));
detour::inline_hook((void *) joy_service, libutils::try_proc(
h8io_module, "?joy_service@@YAHXZ"));
detour::inline_hook((void *) joy_service_on, libutils::try_proc(
h8io_module, "?joy_service_on@@YAHXZ"));
detour::inline_hook((void *) joy_test, libutils::try_proc(
h8io_module, "?joy_test@@YAHXZ"));
detour::inline_hook((void *) joy_test_on, libutils::try_proc(
h8io_module, "?joy_test_on@@YAHXZ"));
detour::inline_hook((void *) mfc5_center_iob_get_coin_state, libutils::try_proc(
h8io_module, "?mfc5_iob_clt_get_coin_state@@YAEXZ"));
detour::inline_hook((void *) mfc5_center_iob_get_coin_cntr, libutils::try_proc(
h8io_module, "?mfc5_iob_clt_get_coin_cntr@@YAHW4E_COIN_CH@@@Z"));
detour::inline_hook((void *) mfc5_center_iob_current_coin_counter, libutils::try_proc(
h8io_module, "?mfc5_center_iob_current_coin_counter@@YAHXZ"));
detour::inline_hook((void *) mfc5_center_iob_consume_coin_counter, libutils::try_proc(
h8io_module, "?mfc5_center_iob_consume_coin_counter@@YAXH@Z"));
detour::inline_hook((void *) mfc5_current_coin_counter, libutils::try_proc(
h8io_module, "?mfc5_current_coin_counter@@YAHXZ"));
detour::inline_hook((void *) mfc5_consume_coin_counter, libutils::try_proc(
h8io_module, "?mfc5_consume_coin_counter@@YAXH@Z"));
detour::inline_hook((void *) mfc5_iob_lamp, libutils::try_proc(
h8io_module, "?mfc5_iob_lamp@@YAXE@Z"));
// ic card
detour::inline_hook((void *) mfc5_ic_card_read_init, libutils::try_proc(
h8io_module, "?mfc5_ic_card_read_init@@YAHXZ"));
detour::inline_hook((void *) mfc5_ic_card_read_step, libutils::try_proc(
h8io_module, "?mfc5_ic_card_read_step@@YAHXZ"));
detour::inline_hook((void *) mfc5_ic_card_status, libutils::try_proc(
h8io_module, "?mfc5_ic_card_status@@YAHXZ"));
detour::inline_hook((void *) mfc5_get_ic_card_id_type, libutils::try_proc(
h8io_module, "?mfc5_get_ic_card_id_type@@YAXPAE@Z"));
detour::inline_hook((void *) mfc5_get_ic_card_id, libutils::try_proc(
h8io_module, "?mfc5_get_ic_card_id@@YAXQAE@Z"));
}
void ShogikaiGame::detach() {
Game::detach();
}
}