spicetools/games/qma/qma.cpp

136 lines
4.5 KiB
C++

#include "qma.h"
#include <cstring>
#include <d3d9.h>
#include "games/shared/lcdhandle.h"
#include "games/shared/twtouch.h"
#include "hooks/graphics/graphics.h"
#include "hooks/devicehook.h"
#include "launcher/launcher.h"
#include "util/detour.h"
#include "util/fileutils.h"
#include "util/utils.h"
#include "ezusb.h"
namespace games::qma {
/**
* Overridden touchscreen for attaching the touch hooks to the window.
*/
class QMATouchDevice : public games::shared::TwTouchDevice {
public:
bool open(LPCWSTR lpFileName) override {
// check if device was opened
auto result = TwTouchDevice::open(lpFileName);
if (result) {
if (GRAPHICS_WINDOWED) {
// get game window
HWND wnd = GetForegroundWindow();
if (!string_begins_with(GetActiveWindowTitle(), "QMA")) {
wnd = FindWindowBeginsWith("QMA");
}
if (wnd != nullptr) {
// get client area
RECT rect {};
GetClientRect(wnd, &rect);
// remove style borders
LONG lStyle = GetWindowLong(wnd, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
SetWindowLongPtr(wnd, GWL_STYLE, lStyle);
// remove ex style borders
LONG lExStyle = GetWindowLong(wnd, GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLongPtr(wnd, GWL_EXSTYLE, lExStyle);
// update window
AdjustWindowRect(&rect, lStyle, FALSE);
SetWindowPos(wnd, nullptr,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
}
// attach touch
touch_attach_dx_hook();
// cursor
if (!is_touch_available()) {
ShowCursor(true);
}
}
// return result
return result;
}
};
/*
* The game likes to crash if E:\LMA\quiz13 does not exist.
* Therefore we create it when the game tries to access E:\LMA the first time.
*/
namespace dirfix {
static decltype(CreateFileW) *CreateFileW_real = nullptr;
static HANDLE WINAPI CreateFileW_Hook(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
// create quiz directory
static bool dirs_created = false;
if (!dirs_created && _wcsnicmp(lpFileName, L"E:\\LMA", 6) == 0) {
fileutils::dir_create_recursive("E:\\LMA\\quiz11");
fileutils::dir_create_recursive("E:\\LMA\\quiz13");
fileutils::dir_create_recursive("E:\\LMA\\quiz15");
dirs_created = true;
}
// return result
return CreateFileW_real(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}
void apply() {
CreateFileW_real = detour::iat_try("CreateFileW", CreateFileW_Hook);
}
}
QMAGame::QMAGame() : Game("Quiz Magic Academy") {
}
void QMAGame::pre_attach() {
Game::pre_attach();
// load without resolving references
// makes game not trigger DLLMain since that does some EZUSB device stuff
LoadLibraryExW((MODULE_PATH / "ezusb.dll").c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES);
}
void QMAGame::attach() {
Game::attach();
// init device hooks
devicehook_init();
devicehook_add(new games::shared::LCDHandle());
devicehook_add(new games::qma::QMATouchDevice());
// EZUSB for button input
ezusb_init();
// disable multi-head mode
D3D9_BEHAVIOR_DISABLE = D3DCREATE_ADAPTERGROUP_DEVICE;
// directory fix
dirfix::apply();
}
}