114 lines
4.4 KiB
C++
114 lines
4.4 KiB
C++
|
#include "qks.h"
|
||
|
|
||
|
#include <format>
|
||
|
#include "util/libutils.h"
|
||
|
#include "util/fileutils.h"
|
||
|
#include "util/utils.h"
|
||
|
#include "util/detour.h"
|
||
|
#include "acioemu/handle.h"
|
||
|
#include "bi2x_hook.h"
|
||
|
|
||
|
namespace games::qks {
|
||
|
std::string QKS_INJECT_ARGS = "";
|
||
|
|
||
|
const std::wstring portName = L"\\\\.\\COM1";
|
||
|
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||
|
static std::string commandLine;
|
||
|
static bool isOpened = false;
|
||
|
|
||
|
static decltype(AddVectoredExceptionHandler) *AddVectoredExceptionHandler_orig = nullptr;
|
||
|
static decltype(ShowCursor) *ShowCursor_orig = nullptr;
|
||
|
static decltype(CreateFileA) *execexe_CreateFileA_orig = nullptr;
|
||
|
static decltype(CloseHandle) *execexe_CloseHandle_orig = nullptr;
|
||
|
static decltype(GetCommandLineA) *GetCommandLineA_orig = nullptr;
|
||
|
|
||
|
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 == portName) {
|
||
|
if (!isOpened) {
|
||
|
isOpened = acioHandle->open(portName.c_str());
|
||
|
} else {
|
||
|
// NOTE: For some reason, QKS repeatedly opens and closes the COM port.
|
||
|
// In the existing implementation of `icca.cpp`, the game side would specify an impossible
|
||
|
// number of units and call `log_fatal()`, causing the game to crash, so the handle is reused.
|
||
|
log_info("qks", "ignored handle open. ({})", ws2s(portName));
|
||
|
}
|
||
|
SetLastError(0);
|
||
|
return (HANDLE)acioHandle;
|
||
|
}
|
||
|
return execexe_CreateFileA_orig(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||
|
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||
|
}
|
||
|
|
||
|
static WINBOOL WINAPI execexe_CloseHandle_hook(HANDLE hObject) {
|
||
|
if (hObject == acioHandle && isOpened) {
|
||
|
log_info("qks", "ignored handle close. ({})", ws2s(portName));
|
||
|
return TRUE;
|
||
|
}
|
||
|
return execexe_CloseHandle_orig(hObject);
|
||
|
}
|
||
|
|
||
|
static int WINAPI ShowCursor_hook(BOOL bShow) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||
|
return INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
|
||
|
static LPSTR WINAPI GetCommandLineA_hook() {
|
||
|
return (LPSTR)commandLine.c_str();
|
||
|
}
|
||
|
|
||
|
void QKSGame::attach() {
|
||
|
Game::attach();
|
||
|
|
||
|
// create required files
|
||
|
fileutils::dir_create_recursive("dev/raw/log");
|
||
|
fileutils::bin_write("dev/raw/log/output_log.txt", nullptr, 0);
|
||
|
|
||
|
// create optional files (suppress warning)
|
||
|
fileutils::bin_write("dev/raw/error.txt", nullptr, 0);
|
||
|
|
||
|
// preload libraries
|
||
|
libutils::load_library("execexe.dll");
|
||
|
libutils::load_library("libaio.dll");
|
||
|
libutils::load_library("libaio-iob.dll");
|
||
|
libutils::load_library("libaio-iob2_video.dll");
|
||
|
libutils::load_library("virtualsurroundnative.dll");
|
||
|
|
||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(7),
|
||
|
execexe_CloseHandle_hook,&execexe_CloseHandle_orig);
|
||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
|
||
|
execexe_CreateFileA_hook,&execexe_CreateFileA_orig);
|
||
|
|
||
|
// insert BI2X hooks
|
||
|
bi2x_hook_init();
|
||
|
|
||
|
// add card reader
|
||
|
acioHandle = new acioemu::ACIOHandle(portName.c_str());
|
||
|
devicehook_init_trampoline();
|
||
|
devicehook_add(acioHandle);
|
||
|
}
|
||
|
|
||
|
void QKSGame::post_attach() {
|
||
|
Game::post_attach();
|
||
|
|
||
|
detour::trampoline_try("kernel32.dll", "AddVectoredExceptionHandler",
|
||
|
AddVectoredExceptionHandler_hook, &AddVectoredExceptionHandler_orig);
|
||
|
detour::trampoline_try("kernel32.dll", "GetCommandLineA",
|
||
|
GetCommandLineA_hook, &GetCommandLineA_orig);
|
||
|
detour::trampoline_try("user32.dll", "ShowCursor",
|
||
|
ShowCursor_hook, &ShowCursor_orig);
|
||
|
|
||
|
commandLine = std::format("{} {}", GetCommandLineA_orig(), QKS_INJECT_ARGS);
|
||
|
}
|
||
|
|
||
|
void QKSGame::detach() {
|
||
|
Game::detach();
|
||
|
|
||
|
devicehook_dispose();
|
||
|
}
|
||
|
}
|