325 lines
10 KiB
C++
325 lines
10 KiB
C++
|
#include "avs/game.h"
|
||
|
#include "popn.h"
|
||
|
#include <cstdint>
|
||
|
#include <cstring>
|
||
|
#include "rawinput/rawinput.h"
|
||
|
#include "util/detour.h"
|
||
|
#include "util/fileutils.h"
|
||
|
#include "util/libutils.h"
|
||
|
#include "util/utils.h"
|
||
|
#include "cfg/button.h"
|
||
|
#include "cfg/api.h"
|
||
|
#include "hooks/sleephook.h"
|
||
|
#include "launcher/launcher.h"
|
||
|
#include "launcher/logger.h"
|
||
|
#include "misc/eamuse.h"
|
||
|
#include "io.h"
|
||
|
|
||
|
namespace games::popn {
|
||
|
|
||
|
static int __cdecl usbCheckAlive() {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbCheckSecurityNew() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbCoinGet() {
|
||
|
return eamuse_coin_get_stock();
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbCoinMode() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbEnd() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbFirmResult() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbGetKEYID() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbGetSecurity() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbLamp(uint32_t lamp_bits) {
|
||
|
|
||
|
// get lights
|
||
|
auto &lights = get_lights();
|
||
|
|
||
|
// bit scan
|
||
|
static const size_t mapping[] = {
|
||
|
Lights::Button1,
|
||
|
Lights::Button2,
|
||
|
Lights::Button3,
|
||
|
Lights::Button4,
|
||
|
Lights::Button5,
|
||
|
Lights::Button6,
|
||
|
Lights::Button7,
|
||
|
Lights::Button8,
|
||
|
Lights::Button9,
|
||
|
Lights::HiLamp1,
|
||
|
Lights::HiLamp2,
|
||
|
Lights::HiLamp3,
|
||
|
Lights::HiLamp4,
|
||
|
Lights::HiLamp5,
|
||
|
Lights::LeftLamp1,
|
||
|
Lights::LeftLamp2,
|
||
|
Lights::RightLamp1,
|
||
|
Lights::RightLamp2,
|
||
|
};
|
||
|
|
||
|
// sw lamps
|
||
|
for (size_t i = 0; i < 9; i++) {
|
||
|
float value = (lamp_bits & (1 << (i + 23))) ? 1.f : 0.f;
|
||
|
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||
|
}
|
||
|
// neon lamps
|
||
|
for (size_t i = 0; i < 5; i++) {
|
||
|
float value = (lamp_bits & (1 << i)) ? 1.f : 0.f;
|
||
|
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[i + 9]), value);
|
||
|
}
|
||
|
// screen lamps
|
||
|
for (size_t i = 0; i < 4; i++) {
|
||
|
float value = (lamp_bits & (1 << (i + 8))) ? 1.f : 0.f;
|
||
|
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[i + 14]), value);
|
||
|
}
|
||
|
|
||
|
// flush output
|
||
|
RI_MGR->devices_flush_output();
|
||
|
|
||
|
// return no error
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbPadRead(unsigned int *pad_bits) {
|
||
|
|
||
|
// get buttons
|
||
|
auto &buttons = get_buttons();
|
||
|
|
||
|
// reset
|
||
|
*pad_bits = 0;
|
||
|
|
||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::Service))) {
|
||
|
*pad_bits |= 1 << 6;
|
||
|
}
|
||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::Test))) {
|
||
|
*pad_bits |= 1 << 7;
|
||
|
}
|
||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::CoinMech))) {
|
||
|
*pad_bits |= 1 << 22;
|
||
|
}
|
||
|
|
||
|
// buttons
|
||
|
static const size_t mapping[] = {
|
||
|
Buttons::Button1,
|
||
|
Buttons::Button2,
|
||
|
Buttons::Button3,
|
||
|
Buttons::Button4,
|
||
|
Buttons::Button5,
|
||
|
Buttons::Button6,
|
||
|
Buttons::Button7,
|
||
|
Buttons::Button8,
|
||
|
Buttons::Button9,
|
||
|
};
|
||
|
for (size_t i = 0; i < 9; i++) {
|
||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(mapping[i]))) {
|
||
|
*pad_bits |= 1 << (8 + i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// return no error
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbPadReadLast(uint8_t *a1) {
|
||
|
memset(a1, 0, 40);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbSecurityInit() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbSecurityInitDone() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbSecuritySearch() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbSecuritySearchDone() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbSecuritySelect() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbSecuritySelectDone() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbSetExtIo(uint32_t rgb_bits) {
|
||
|
|
||
|
// get lights
|
||
|
auto &lights = get_lights();
|
||
|
|
||
|
// bit scan
|
||
|
static const size_t mapping[] = {
|
||
|
Lights::WooferLED_R,
|
||
|
Lights::WooferLED_G,
|
||
|
Lights::WooferLED_B,
|
||
|
};
|
||
|
|
||
|
for (size_t i = 0; i < 3; i++) {
|
||
|
float value = (rgb_bits & (1 << (i + 1)));
|
||
|
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||
|
}
|
||
|
|
||
|
// flush output
|
||
|
RI_MGR->devices_flush_output();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbStart() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbWdtReset() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbWdtStart() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __cdecl usbWdtStartDone() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static decltype(DeleteFileA)* DeleteFileA_real = nullptr;
|
||
|
|
||
|
static BOOL WINAPI DeleteFileA_hook(LPCSTR lpFileName) {
|
||
|
|
||
|
// fantasia deletes a bunch of drmfs files in dll_entry_init
|
||
|
if (string_begins_with(lpFileName, "D:\\L39\\contents")) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return DeleteFileA_real(lpFileName);
|
||
|
}
|
||
|
|
||
|
static decltype(mixerSetControlDetails)* mixerSetControlDetails_real = nullptr;
|
||
|
|
||
|
static MMRESULT WINAPI mixerSetControlDetails_hook(HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails) {
|
||
|
|
||
|
mixerSetControlDetails_real(hmxobj, pmxcd, fdwDetails);
|
||
|
return MMSYSERR_NOERROR;
|
||
|
}
|
||
|
|
||
|
static bool log_hook(void *user, const std::string &data, logger::Style style, std::string &out) {
|
||
|
|
||
|
// get rid of the log spam
|
||
|
if (data.find(" I:CabinetSettingFile: label:") != std::string::npos) {
|
||
|
out.clear();
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
POPNGame::POPNGame() : Game("Pop'n Music") {
|
||
|
logger::hook_add(log_hook, this);
|
||
|
}
|
||
|
|
||
|
POPNGame::~POPNGame() {
|
||
|
logger::hook_remove(log_hook, this);
|
||
|
}
|
||
|
|
||
|
void POPNGame::pre_attach() {
|
||
|
Game::pre_attach();
|
||
|
|
||
|
// only needed for older versions (19-21, inclusive) but create them anyway since 21 and above are all M39
|
||
|
fileutils::dir_create_recursive_log("popn", "dev\\raw\\bookkeeping");
|
||
|
fileutils::dir_create_recursive_log("popn", "dev\\raw\\ranking");
|
||
|
fileutils::dir_create_recursive_log("popn", "dev\\raw\\settings");
|
||
|
|
||
|
// load without resolving references
|
||
|
// makes game not trigger DLLMain which results in an error due to missing EZUSB device
|
||
|
LoadLibraryExW((MODULE_PATH / "ezusb.dll").c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES);
|
||
|
}
|
||
|
|
||
|
void POPNGame::attach() {
|
||
|
Game::attach();
|
||
|
|
||
|
/*
|
||
|
* Fast Boot (TM) Patch
|
||
|
* Game tries to create some directories and if it fails it will sleep for 1 second
|
||
|
* We make it sleep 1ms each time instead
|
||
|
*/
|
||
|
hooks::sleep::init(999, 1);
|
||
|
|
||
|
DeleteFileA_real = detour::iat_try("DeleteFileA", DeleteFileA_hook, avs::game::DLL_INSTANCE);
|
||
|
mixerSetControlDetails_real = detour::iat_try("mixerSetControlDetails", mixerSetControlDetails_hook, avs::game::DLL_INSTANCE);
|
||
|
|
||
|
// EZUSB hooks
|
||
|
HINSTANCE ezusb = libutils::try_library("ezusb.dll");
|
||
|
if (ezusb) {
|
||
|
detour::inline_hook((void *) usbCheckAlive,
|
||
|
libutils::try_proc(ezusb, "?usbCheckAlive@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbCheckSecurityNew,
|
||
|
libutils::try_proc(ezusb, "?usbCheckSecurityNew@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbCoinGet,
|
||
|
libutils::try_proc(ezusb, "?usbCoinGet@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbCoinMode,
|
||
|
libutils::try_proc(ezusb, "?usbCoinMode@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbEnd,
|
||
|
libutils::try_proc(ezusb, "?usbEnd@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbFirmResult,
|
||
|
libutils::try_proc(ezusb, "?usbFirmResult@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbGetKEYID,
|
||
|
libutils::try_proc(ezusb, "?usbGetKEYID@@YAHPAEH@Z"));
|
||
|
detour::inline_hook((void *) usbGetSecurity,
|
||
|
libutils::try_proc(ezusb, "?usbGetSecurity@@YAHHPAE@Z"));
|
||
|
detour::inline_hook((void *) usbLamp,
|
||
|
libutils::try_proc(ezusb, "?usbLamp@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbPadRead,
|
||
|
libutils::try_proc(ezusb, "?usbPadRead@@YAHPAK@Z"));
|
||
|
detour::inline_hook((void *) usbPadReadLast,
|
||
|
libutils::try_proc(ezusb, "?usbPadReadLast@@YAHPAE@Z"));
|
||
|
detour::inline_hook((void *) usbSecurityInit,
|
||
|
libutils::try_proc(ezusb, "?usbSecurityInit@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbSecurityInitDone,
|
||
|
libutils::try_proc(ezusb, "?usbSecurityInitDone@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbSecuritySearch,
|
||
|
libutils::try_proc(ezusb, "?usbSecuritySearch@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbSecuritySearchDone,
|
||
|
libutils::try_proc(ezusb, "?usbSecuritySearchDone@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbSecuritySelect,
|
||
|
libutils::try_proc(ezusb, "?usbSecuritySelect@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbSecuritySelectDone,
|
||
|
libutils::try_proc(ezusb, "?usbSecuritySelectDone@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbSetExtIo,
|
||
|
libutils::try_proc(ezusb, "?usbSetExtIo@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbStart,
|
||
|
libutils::try_proc(ezusb, "?usbStart@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbWdtReset,
|
||
|
libutils::try_proc(ezusb, "?usbWdtReset@@YAHXZ"));
|
||
|
detour::inline_hook((void *) usbWdtStart,
|
||
|
libutils::try_proc(ezusb, "?usbWdtStart@@YAHH@Z"));
|
||
|
detour::inline_hook((void *) usbWdtStartDone,
|
||
|
libutils::try_proc(ezusb, "?usbWdtStartDone@@YAHXZ"));
|
||
|
}
|
||
|
}
|
||
|
}
|