Initial re-upload of spice2x-24-08-24
This commit is contained in:
158
games/mga/gunio.cpp
Normal file
158
games/mga/gunio.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "gunio.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
bool games::mga::SpiceGearGunHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"COM1") != 0) {
|
||||
return false;
|
||||
}
|
||||
log_info("metalgear", "Opened gun device on COM1");
|
||||
return true;
|
||||
}
|
||||
|
||||
int games::mga::SpiceGearGunHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
|
||||
// minimum buffer size
|
||||
if ((command == 0u) || nNumberOfBytesToRead < 2)
|
||||
return 0;
|
||||
|
||||
// execute command
|
||||
int bytes_read = 0;
|
||||
switch (command) {
|
||||
case 0x04: // get version
|
||||
|
||||
// write version
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = version;
|
||||
|
||||
break;
|
||||
case 0x08: // get input state
|
||||
{
|
||||
// check buffer size
|
||||
if (nNumberOfBytesToRead < 12)
|
||||
return 0;
|
||||
|
||||
// get cursor position
|
||||
POINT cursor_pos{};
|
||||
GetCursorPos(&cursor_pos);
|
||||
|
||||
// get screen size
|
||||
RECT screen_size{};
|
||||
GetWindowRect(GetDesktopWindow(), &screen_size);
|
||||
auto screen_width = (unsigned short) (screen_size.right - screen_size.left);
|
||||
auto screen_height = (unsigned short) (screen_size.bottom - screen_size.top);
|
||||
|
||||
// calculate cursor position
|
||||
p1_x = (unsigned short) (((cursor_pos.x * 1024) / screen_width) % 1024);
|
||||
p1_y = (unsigned short) (1024 - (((cursor_pos.y * 1024) / screen_height) % 1024));
|
||||
|
||||
// gun P1
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x00;
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = HIBYTE(p1_x);
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = LOBYTE(p1_x);
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = HIBYTE(p1_y);
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = LOBYTE(p1_y);
|
||||
bytes_read += 3;
|
||||
|
||||
// fill buffer
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x75;
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x75;
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x75;
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x0C: // not called in game - empty
|
||||
|
||||
// empty data
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x00;
|
||||
|
||||
break;
|
||||
case 0x10: // get DIP switches
|
||||
|
||||
// clear bits
|
||||
*((unsigned char *) lpBuffer + bytes_read) = 0x00;
|
||||
|
||||
// set bits
|
||||
if (frequency == 1)
|
||||
*((unsigned char *) lpBuffer + bytes_read) |= 0x80;
|
||||
if (frequency == 2)
|
||||
*((unsigned char *) lpBuffer + bytes_read) |= 0x40;
|
||||
if (frequency == 3)
|
||||
*((unsigned char *) lpBuffer + bytes_read) |= 0x20;
|
||||
if (frequency == 4)
|
||||
*((unsigned char *) lpBuffer + bytes_read) |= 0x10;
|
||||
|
||||
break;
|
||||
case 0x14: // frequency 1
|
||||
|
||||
// set frequency and return empty data
|
||||
frequency = 1;
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x00;
|
||||
|
||||
break;
|
||||
case 0x18: // frequency 2
|
||||
|
||||
// set frequency and return empty data
|
||||
frequency = 2;
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x00;
|
||||
|
||||
break;
|
||||
case 0x19: // frequency 3
|
||||
|
||||
// set frequency and return empty data
|
||||
frequency = 3;
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x00;
|
||||
|
||||
break;
|
||||
case 0x1A: // frequency 4
|
||||
|
||||
// set frequency and return empty data
|
||||
frequency = 4;
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x00;
|
||||
|
||||
break;
|
||||
case 0x1C: // led board check
|
||||
|
||||
// check buffer size
|
||||
if (nNumberOfBytesToRead < 11)
|
||||
return 0;
|
||||
|
||||
// set all bits
|
||||
memset(lpBuffer, 0xFF, 10);
|
||||
bytes_read += 10;
|
||||
|
||||
break;
|
||||
default:
|
||||
log_warning("metalgear", "unknown opcode: 0x{:02X}", command);
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = 0x01;
|
||||
}
|
||||
|
||||
// reset command
|
||||
command = 0;
|
||||
|
||||
// calculate checksum
|
||||
unsigned char checksum = 0xFF;
|
||||
for (int i = 0; i < bytes_read; i++)
|
||||
checksum -= *((unsigned char *) lpBuffer + i);
|
||||
*((unsigned char *) lpBuffer + bytes_read++) = checksum;
|
||||
|
||||
// return amount of bytes read
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
int games::mga::SpiceGearGunHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
|
||||
// save command
|
||||
if (nNumberOfBytesToWrite > 0)
|
||||
command = *((unsigned char *) lpBuffer);
|
||||
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int games::mga::SpiceGearGunHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer, DWORD nOutBufferSize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool games::mga::SpiceGearGunHandle::close() {
|
||||
log_info("metalgear", "Closed gun device on COM1");
|
||||
return true;
|
||||
}
|
||||
29
games/mga/gunio.h
Normal file
29
games/mga/gunio.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace games::mga {
|
||||
|
||||
// Team FrontLine GUN IO
|
||||
class SpiceGearGunHandle : public CustomHandle {
|
||||
private:
|
||||
|
||||
unsigned char version = 62;
|
||||
unsigned char command = 0x00;
|
||||
unsigned short p1_x = 0;
|
||||
unsigned short p1_y = 0;
|
||||
int frequency = 1;
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
73
games/mga/io.cpp
Normal file
73
games/mga/io.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "io.h"
|
||||
|
||||
std::vector<Button> &games::mga::get_buttons() {
|
||||
static std::vector<Button> buttons;
|
||||
|
||||
if (buttons.empty()) {
|
||||
buttons = GameAPI::Buttons::getButtons("Metal Gear");
|
||||
|
||||
GameAPI::Buttons::sortButtons(
|
||||
&buttons,
|
||||
"Service",
|
||||
"Test",
|
||||
"Coin Mech",
|
||||
"Start",
|
||||
"Top",
|
||||
"Front Top",
|
||||
"Front Bottom",
|
||||
"Side Left",
|
||||
"Side Right",
|
||||
"Side Lever",
|
||||
"Trigger Button",
|
||||
"Switch Button",
|
||||
"Joy Forwards",
|
||||
"Joy Backwards",
|
||||
"Joy Left",
|
||||
"Joy Right"
|
||||
);
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::vector<Analog> &games::mga::get_analogs() {
|
||||
static std::vector<Analog> analogs;
|
||||
|
||||
if (analogs.empty()) {
|
||||
analogs = GameAPI::Analogs::getAnalogs("Metal Gear");
|
||||
|
||||
GameAPI::Analogs::sortAnalogs(
|
||||
&analogs,
|
||||
"Joy X",
|
||||
"Joy Y"
|
||||
);
|
||||
}
|
||||
|
||||
return analogs;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Light> &games::mga::get_lights() {
|
||||
static std::vector<Light> lights;
|
||||
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Metal Gear");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
&lights,
|
||||
"Start",
|
||||
"Left R",
|
||||
"Left G",
|
||||
"Left B",
|
||||
"Right R",
|
||||
"Right G",
|
||||
"Right B",
|
||||
"Gun R",
|
||||
"Gun G",
|
||||
"Gun B",
|
||||
"Gun Vibration"
|
||||
);
|
||||
}
|
||||
|
||||
return lights;
|
||||
}
|
||||
59
games/mga/io.h
Normal file
59
games/mga/io.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace games::mga {
|
||||
|
||||
// all buttons in correct order
|
||||
namespace Buttons {
|
||||
enum {
|
||||
Service,
|
||||
Test,
|
||||
CoinMech,
|
||||
Start,
|
||||
Top,
|
||||
FrontTop,
|
||||
FrontBottom,
|
||||
SideLeft,
|
||||
SideRight,
|
||||
SideLever,
|
||||
TriggerButton,
|
||||
SwitchButton,
|
||||
JoyForwards,
|
||||
JoyBackwards,
|
||||
JoyLeft,
|
||||
JoyRight,
|
||||
};
|
||||
}
|
||||
|
||||
// all analogs in correct order
|
||||
namespace Analogs {
|
||||
enum {
|
||||
JoyX,
|
||||
JoyY,
|
||||
};
|
||||
}
|
||||
|
||||
// all lights in correct order
|
||||
namespace Lights {
|
||||
enum {
|
||||
Start,
|
||||
LeftR,
|
||||
LeftG,
|
||||
LeftB,
|
||||
RightR,
|
||||
RightG,
|
||||
RightB,
|
||||
GunR,
|
||||
GunG,
|
||||
GunB,
|
||||
GunVibration,
|
||||
};
|
||||
}
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::vector<Analog> &get_analogs();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
82
games/mga/mga.cpp
Normal file
82
games/mga/mga.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "mga.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "hooks/devicehook.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/sigscan.h"
|
||||
|
||||
#include "gunio.h"
|
||||
|
||||
namespace games::mga {
|
||||
void (__cdecl *ess_eventlog_request_error_orig)(const char *, unsigned int, int, int);
|
||||
|
||||
static int __cdecl log_change_level_hook(int level) {
|
||||
log_misc("mga", "ignoring log_change_level({})", level);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uintptr_t __cdecl log_change_output_hook(uintptr_t target, uintptr_t a2) {
|
||||
log_misc("mga", "ignoring log_change_output(0x{:x}, 0x{:x})", target, a2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool __cdecl setvolume_hook(uint8_t volume) {
|
||||
log_misc("mga", "ignoring setvolume {}", static_cast<int>(volume));
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __cdecl ess_eventlog_request_error_hook(const char *a1, unsigned int a2, int a3, int a4) {
|
||||
log_info("mga", "ess_eventlog_request_error({}, {}, {}, {})", a1, a2, a3, a4);
|
||||
|
||||
//ess_eventlog_request_error_orig(a1, a2, a3, a4);
|
||||
}
|
||||
|
||||
MGAGame::MGAGame() : Game("Metal Gear Arcade") {
|
||||
}
|
||||
|
||||
void MGAGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
// load system.dll (along with the serial functions)
|
||||
auto system = libutils::load_library("system.dll");
|
||||
|
||||
// add the gun
|
||||
devicehook_init();
|
||||
devicehook_add(new SpiceGearGunHandle());
|
||||
|
||||
// fix ICCA
|
||||
acio::ICCA_COMPAT_ACTIVE = true;
|
||||
|
||||
// ignore sound engine failure
|
||||
if (!replace_pattern(
|
||||
system,
|
||||
"E8????????84C0750A68F4010000E8????????E8????????E8????????3BF3750433C0EB0C",
|
||||
"??????????????????90909090909090909090????????????????????????????????????",
|
||||
0, 0))
|
||||
{
|
||||
log_warning("mga", "failed to patch sound engine");
|
||||
}
|
||||
|
||||
// hook setvolume
|
||||
if (!detour::iat_try("?setvolume@@YAHPAD@Z", setvolume_hook)) {
|
||||
log_warning("mga", "setvolume hook failed");
|
||||
}
|
||||
|
||||
// stop the game from redirecting AVS log calls
|
||||
detour::iat_try("log_change_level", log_change_level_hook);
|
||||
detour::iat_try("log_change_output", log_change_output_hook);
|
||||
|
||||
ess_eventlog_request_error_orig = detour::iat_try("ess_eventlog_request_error", ess_eventlog_request_error_hook);
|
||||
}
|
||||
|
||||
void MGAGame::detach() {
|
||||
Game::detach();
|
||||
|
||||
devicehook_dispose();
|
||||
}
|
||||
}
|
||||
13
games/mga/mga.h
Normal file
13
games/mga/mga.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "games/game.h"
|
||||
|
||||
namespace games::mga {
|
||||
|
||||
class MGAGame : public games::Game {
|
||||
public:
|
||||
MGAGame();
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user