mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 23:00:42 -07:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf79b5d2aa | |||
| e74c60f61d | |||
| 89921e5ec2 | |||
| 1b7ebd6fc2 | |||
| 165cc6028f | |||
| 8dc835bf52 | |||
| 8241641502 | |||
| 6de122c9f8 | |||
| b267ad09ac | |||
| e0dd371d61 | |||
| f8f45a0cbd | |||
| bf8ba050ce |
@@ -55,3 +55,6 @@ We explicitly do **NOT** have a Discord server for dicussions - we try to do eve
|
|||||||
|
|
||||||
Please see [CONTRIBUTING page](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md) for a full list of guidelines when submitting code.
|
Please see [CONTRIBUTING page](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md) for a full list of guidelines when submitting code.
|
||||||
|
|
||||||
|
## Additional information
|
||||||
|
|
||||||
|
Please read [README.md](https://github.com/spice2x/spice2x.github.io/blob/main/src/spice2x/README.md) inside `src/spice2x`.
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
api/modules/drs.cpp
|
api/modules/drs.cpp
|
||||||
api/modules/lcd.cpp
|
api/modules/lcd.cpp
|
||||||
api/modules/ddr.cpp
|
api/modules/ddr.cpp
|
||||||
|
api/modules/resize.cpp
|
||||||
|
|
||||||
# avs
|
# avs
|
||||||
avs/core.cpp
|
avs/core.cpp
|
||||||
@@ -441,6 +442,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/qks/qks.cpp
|
games/qks/qks.cpp
|
||||||
games/qks/io.cpp
|
games/qks/io.cpp
|
||||||
games/qks/bi2x_hook.cpp
|
games/qks/bi2x_hook.cpp
|
||||||
|
games/mfg/mfg.cpp
|
||||||
|
games/mfg/io.cpp
|
||||||
|
games/mfg/bi2a_hook.cpp
|
||||||
|
|
||||||
# hooks
|
# hooks
|
||||||
hooks/audio/audio.cpp
|
hooks/audio/audio.cpp
|
||||||
@@ -586,6 +590,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
util/sysutils.cpp
|
util/sysutils.cpp
|
||||||
util/lz77.cpp
|
util/lz77.cpp
|
||||||
util/tapeled.cpp
|
util/tapeled.cpp
|
||||||
|
util/execexe.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||||
|
|||||||
@@ -181,6 +181,12 @@ All of those three modules have equally named methods for you to call.
|
|||||||
- removes the override value from the objects specified by name
|
- removes the override value from the objects specified by name
|
||||||
- if no names were passed, all overrides will be removed
|
- if no names were passed, all overrides will be removed
|
||||||
|
|
||||||
|
##### Additional API for lights
|
||||||
|
- read(name: string, ...)
|
||||||
|
- same as read(), but you can specify light names
|
||||||
|
- write_reset(name: str, ...)
|
||||||
|
- same as write_reset(), but it accepts a flat list of strings
|
||||||
|
|
||||||
#### Touch
|
#### Touch
|
||||||
- read()
|
- read()
|
||||||
- returns an array of state objects containing id, x and y
|
- returns an array of state objects containing id, x and y
|
||||||
@@ -246,6 +252,18 @@ which also means that your hex edits are applicable directly.
|
|||||||
- info()
|
- info()
|
||||||
- returns information about the serial LCD controller some games use
|
- returns information about the serial LCD controller some games use
|
||||||
|
|
||||||
|
#### Resize
|
||||||
|
- image_resize_enable(enable: bool)
|
||||||
|
- enables or disables image resize state
|
||||||
|
- image_resize_set_scene(scene: int)
|
||||||
|
- sets the active scene for image resize state; set to 0 to disable resize
|
||||||
|
|
||||||
|
## Native wrapper libraries
|
||||||
|
Spicetools provides wrapper libraries in: Arduino, C++, Dart, and Python.
|
||||||
|
Python is the only one that is fully spec compliant.
|
||||||
|
Other libraries may be missing features and contain bugs; please feel free to
|
||||||
|
contribute code to fill the gaps if you work on a project using these libraries.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Unless otherwise noted, all files are licensed under the GPLv3.
|
Unless otherwise noted, all files are licensed under the GPLv3.
|
||||||
See the LICENSE file for the full license text.
|
See the LICENSE file for the full license text.
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) {
|
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount) {
|
||||||
this->com_port = lpCOMPort;
|
this->com_port = lpCOMPort;
|
||||||
|
this->icca_node_count = iccaNodeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
||||||
@@ -16,7 +17,7 @@ bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
|||||||
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
|
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
|
||||||
|
|
||||||
// ACIO device
|
// ACIO device
|
||||||
acio_emu.add_device(new acioemu::ICCADevice(false, true, 2));
|
acio_emu.add_device(new acioemu::ICCADevice(false, true, icca_node_count));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ namespace acioemu {
|
|||||||
private:
|
private:
|
||||||
LPCWSTR com_port;
|
LPCWSTR com_port;
|
||||||
|
|
||||||
|
uint8_t icca_node_count;
|
||||||
|
|
||||||
acioemu::ACIOEmu acio_emu;
|
acioemu::ACIOEmu acio_emu;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ACIOHandle(LPCWSTR lpCOMPort);
|
ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount = 2);
|
||||||
|
|
||||||
bool open(LPCWSTR lpFileName) override;
|
bool open(LPCWSTR lpFileName) override;
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
|||||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
||||||
) {
|
) {
|
||||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
||||||
|
} else if (avs::game::is_model({"VFG"})) {
|
||||||
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
||||||
} else {
|
} else {
|
||||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||||
}
|
}
|
||||||
@@ -333,6 +335,7 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
|||||||
case ACIO_CMD_CLEAR:
|
case ACIO_CMD_CLEAR:
|
||||||
case 0x30: // GetBoardProductNumber
|
case 0x30: // GetBoardProductNumber
|
||||||
case 0x31: // GetMicomInfo
|
case 0x31: // GetMicomInfo
|
||||||
|
case 0x3A: // ???
|
||||||
case 0x0116: // ???
|
case 0x0116: // ???
|
||||||
case 0x0120: // ???
|
case 0x0120: // ???
|
||||||
case 0xFF: // BROADCAST
|
case 0xFF: // BROADCAST
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "modules/lights.h"
|
#include "modules/lights.h"
|
||||||
#include "modules/memory.h"
|
#include "modules/memory.h"
|
||||||
#include "modules/touch.h"
|
#include "modules/touch.h"
|
||||||
|
#include "modules/resize.h"
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
#include "response.h"
|
#include "response.h"
|
||||||
|
|
||||||
@@ -403,6 +404,7 @@ void Controller::init_state(api::ClientState *state) {
|
|||||||
state->modules.push_back(new modules::Lights());
|
state->modules.push_back(new modules::Lights());
|
||||||
state->modules.push_back(new modules::Memory());
|
state->modules.push_back(new modules::Memory());
|
||||||
state->modules.push_back(new modules::Touch());
|
state->modules.push_back(new modules::Touch());
|
||||||
|
state->modules.push_back(new modules::Resize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::free_state(api::ClientState *state) {
|
void Controller::free_state(api::ClientState *state) {
|
||||||
|
|||||||
@@ -19,11 +19,16 @@ namespace api::modules {
|
|||||||
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
||||||
functions["write"] = std::bind(&Lights::write, this, _1, _2);
|
functions["write"] = std::bind(&Lights::write, this, _1, _2);
|
||||||
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
|
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
|
||||||
lights = games::get_lights(eamuse_get_game());
|
|
||||||
|
this->lights = games::get_lights(eamuse_get_game());
|
||||||
|
for (auto &light : *this->lights) {
|
||||||
|
this->lights_by_names.emplace(light.getName(), light);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read()
|
* read()
|
||||||
|
* read(name: str, ...)
|
||||||
*/
|
*/
|
||||||
void Lights::read(api::Request &req, Response &res) {
|
void Lights::read(api::Request &req, Response &res) {
|
||||||
|
|
||||||
@@ -32,8 +37,31 @@ namespace api::modules {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all lights for this game
|
||||||
|
if (req.params.Size() == 0) {
|
||||||
// add state for each light
|
// add state for each light
|
||||||
for (auto &light : *this->lights) {
|
for (auto &light : *this->lights) {
|
||||||
|
get_light(light, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// specified light names
|
||||||
|
for (Value ¶m : req.params.GetArray()) {
|
||||||
|
// check params
|
||||||
|
if (!param.IsString()) {
|
||||||
|
error_type(res, "name", "string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto name = param.GetString();
|
||||||
|
if (this->lights_by_names.contains(name)) {
|
||||||
|
get_light(this->lights_by_names.at(name).get(), res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lights::get_light(Light &light, Response &res) {
|
||||||
Value state(kArrayType);
|
Value state(kArrayType);
|
||||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||||
@@ -43,7 +71,6 @@ namespace api::modules {
|
|||||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||||
res.add_data(state);
|
res.add_data(state);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* write([name: str, state: float], ...)
|
* write([name: str, state: float], ...)
|
||||||
@@ -90,6 +117,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* write_reset()
|
* write_reset()
|
||||||
|
* write_reset(name: str, ...)
|
||||||
* write_reset([name: str], ...)
|
* write_reset([name: str], ...)
|
||||||
*/
|
*/
|
||||||
void Lights::write_reset(Request &req, Response &res) {
|
void Lights::write_reset(Request &req, Response &res) {
|
||||||
@@ -119,12 +147,10 @@ namespace api::modules {
|
|||||||
|
|
||||||
// loop parameters
|
// loop parameters
|
||||||
for (Value ¶m : req.params.GetArray()) {
|
for (Value ¶m : req.params.GetArray()) {
|
||||||
|
const char* light_name = nullptr;
|
||||||
|
|
||||||
// check params
|
// check params
|
||||||
if (!param.IsArray()) {
|
if (param.IsArray()) {
|
||||||
error(res, "parameters must be arrays");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (param.Size() < 1) {
|
if (param.Size() < 1) {
|
||||||
error_params_insufficient(res);
|
error_params_insufficient(res);
|
||||||
continue;
|
continue;
|
||||||
@@ -133,12 +159,16 @@ namespace api::modules {
|
|||||||
error_type(res, "name", "string");
|
error_type(res, "name", "string");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get params
|
// get params
|
||||||
auto light_name = param[0].GetString();
|
light_name = param[0].GetString();
|
||||||
|
} else if (param.IsString()) {
|
||||||
|
light_name = param.GetString();
|
||||||
|
} else {
|
||||||
|
error(res, "parameters must be arrays or strings");
|
||||||
|
}
|
||||||
|
|
||||||
// write analog state
|
// write analog state
|
||||||
if (!this->write_light_reset(light_name)) {
|
if (light_name && !this->write_light_reset(light_name)) {
|
||||||
error_unknown(res, "analog", light_name);
|
error_unknown(res, "analog", light_name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -153,8 +183,8 @@ namespace api::modules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find light
|
// find light
|
||||||
for (auto &light : *this->lights) {
|
if (this->lights_by_names.contains(name)) {
|
||||||
if (light.getName() == name) {
|
auto &light = this->lights_by_names.at(name).get();
|
||||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||||
light.override_enabled = true;
|
light.override_enabled = true;
|
||||||
|
|
||||||
@@ -163,12 +193,11 @@ namespace api::modules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
// unknown light
|
// unknown light
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Lights::write_light_reset(std::string name) {
|
bool Lights::write_light_reset(std::string name) {
|
||||||
|
|
||||||
@@ -178,14 +207,13 @@ namespace api::modules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find light
|
// find light
|
||||||
for (auto &light : *this->lights) {
|
if (this->lights_by_names.contains(name)) {
|
||||||
if (light.getName() == name) {
|
auto &light = this->lights_by_names.at(name).get();
|
||||||
light.override_enabled = false;
|
light.override_enabled = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
// unknown light
|
// unknown light
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <external/robin_hood.h>
|
||||||
|
|
||||||
#include "api/module.h"
|
#include "api/module.h"
|
||||||
#include "api/request.h"
|
#include "api/request.h"
|
||||||
#include "cfg/api.h"
|
#include "cfg/api.h"
|
||||||
@@ -15,6 +17,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
// state
|
// state
|
||||||
std::vector<Light> *lights;
|
std::vector<Light> *lights;
|
||||||
|
robin_hood::unordered_map<std::string, std::reference_wrapper<Light>> lights_by_names;
|
||||||
|
|
||||||
// function definitions
|
// function definitions
|
||||||
void read(Request &req, Response &res);
|
void read(Request &req, Response &res);
|
||||||
@@ -22,6 +25,7 @@ namespace api::modules {
|
|||||||
void write_reset(Request &req, Response &res);
|
void write_reset(Request &req, Response &res);
|
||||||
|
|
||||||
// helper
|
// helper
|
||||||
|
void get_light(Light &light, Response &res);
|
||||||
bool write_light(std::string name, float state);
|
bool write_light(std::string name, float state);
|
||||||
bool write_light_reset(std::string name);
|
bool write_light_reset(std::string name);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#include "resize.h"
|
||||||
|
#include "external/rapidjson/document.h"
|
||||||
|
#include "cfg/screen_resize.h"
|
||||||
|
|
||||||
|
using namespace std::placeholders;
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
namespace api::modules {
|
||||||
|
|
||||||
|
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||||
|
|
||||||
|
Resize::Resize() : Module("resize") {
|
||||||
|
functions["image_resize_enable"] = std::bind(&Resize::image_resize_enable, this, _1, _2);
|
||||||
|
functions["image_resize_set_scene"] = std::bind(&Resize::image_resize_set_scene, this, _1, _2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* image_resize_enable(enable: bool)
|
||||||
|
*/
|
||||||
|
void Resize::image_resize_enable(Request &req, Response &res) {
|
||||||
|
if (req.params.Size() < 1) {
|
||||||
|
return error_params_insufficient(res);
|
||||||
|
}
|
||||||
|
if (!req.params[0].IsBool()) {
|
||||||
|
return error_type(res, "enable", "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg::SCREENRESIZE->enable_screen_resize = req.params[0].GetBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* image_resize_set_scene(scene: int)
|
||||||
|
*/
|
||||||
|
void Resize::image_resize_set_scene(Request &req, Response &res) {
|
||||||
|
if (req.params.Size() < 1) {
|
||||||
|
return error_params_insufficient(res);
|
||||||
|
}
|
||||||
|
if (!req.params[0].IsInt()) {
|
||||||
|
return error_type(res, "scene", "int");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scene = req.params[0].GetInt();
|
||||||
|
if (scene < 0 || (int)std::size(cfg::SCREENRESIZE->scene_settings) < scene) {
|
||||||
|
return error(res, "invalid scene number");
|
||||||
|
}
|
||||||
|
if (scene == 0) {
|
||||||
|
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||||
|
} else {
|
||||||
|
cfg::SCREENRESIZE->enable_screen_resize = true;
|
||||||
|
cfg::SCREENRESIZE->screen_resize_current_scene = scene - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "api/module.h"
|
||||||
|
#include "api/request.h"
|
||||||
|
|
||||||
|
namespace api::modules {
|
||||||
|
|
||||||
|
class Resize : public Module {
|
||||||
|
public:
|
||||||
|
Resize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// function definitions
|
||||||
|
void image_resize_enable(Request &req, Response &res);
|
||||||
|
void image_resize_set_scene(Request &req, Response &res);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -12,3 +12,4 @@ from .keypads import *
|
|||||||
from .lights import *
|
from .lights import *
|
||||||
from .memory import *
|
from .memory import *
|
||||||
from .touch import *
|
from .touch import *
|
||||||
|
from .resize import *
|
||||||
@@ -2,8 +2,14 @@ from .connection import Connection
|
|||||||
from .request import Request
|
from .request import Request
|
||||||
|
|
||||||
|
|
||||||
def lights_read(con: Connection):
|
def lights_read(con: Connection, light_names=None):
|
||||||
res = con.request(Request("lights", "read"))
|
req = Request("lights", "read")
|
||||||
|
|
||||||
|
if light_names:
|
||||||
|
for light_name in light_names:
|
||||||
|
req.add_param(light_name)
|
||||||
|
|
||||||
|
res = con.request(req)
|
||||||
return res.get_data()
|
return res.get_data()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from .connection import Connection
|
||||||
|
from .request import Request
|
||||||
|
|
||||||
|
def image_resize_enable(con: Connection, enable: bool):
|
||||||
|
req = Request("resize", "image_resize_enable")
|
||||||
|
req.add_param(enable)
|
||||||
|
con.request(req)
|
||||||
|
|
||||||
|
def image_resize_set_scene(con: Connection, scene: int):
|
||||||
|
req = Request("resize", "image_resize_set_scene")
|
||||||
|
req.add_param(scene)
|
||||||
|
con.request(req)
|
||||||
@@ -397,6 +397,65 @@ class LightsTab(ttk.Frame):
|
|||||||
# set text
|
# set text
|
||||||
self.txt_lights.set_text(txt)
|
self.txt_lights.set_text(txt)
|
||||||
|
|
||||||
|
class ResizeTab(ttk.Frame):
|
||||||
|
"""Resize tab."""
|
||||||
|
|
||||||
|
def __init__(self, app, parent, **kwargs):
|
||||||
|
|
||||||
|
# init frame
|
||||||
|
ttk.Frame.__init__(self, parent, **kwargs)
|
||||||
|
self.app = app
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
# scale grid
|
||||||
|
self.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# image resize
|
||||||
|
self.resize = ttk.Frame(self, padding=(8, 8, 8, 8))
|
||||||
|
self.resize.grid(row=0, column=0, sticky=tk.E+tk.W)
|
||||||
|
self.resize.columnconfigure(0, weight=1)
|
||||||
|
self.resize.columnconfigure(1, weight=1)
|
||||||
|
self.resize_lbl = ttk.Label(self.resize, text="Image Resize")
|
||||||
|
self.resize_lbl.grid(row=0, columnspan=2)
|
||||||
|
|
||||||
|
self.resize_off = ttk.Button(self.resize, text="Disable", command=self.action_resize_false)
|
||||||
|
self.resize_off.grid(row=2, column=0, sticky=NSEW, padx=2, pady=2)
|
||||||
|
|
||||||
|
self.resize_on = ttk.Button(self.resize, text="Enable", command=self.action_resize_on)
|
||||||
|
self.resize_on.grid(row=2, column=1, sticky=NSEW, padx=2, pady=2)
|
||||||
|
|
||||||
|
self.resize_scene_1 = ttk.Button(self.resize, text="Scene 1", command=self.action_resize_scene_1)
|
||||||
|
self.resize_scene_1.grid(row=3, column=0, sticky=NSEW, padx=2, pady=2)
|
||||||
|
self.resize_scene_2 = ttk.Button(self.resize, text="Scene 2", command=self.action_resize_scene_2)
|
||||||
|
self.resize_scene_2.grid(row=3, column=1, sticky=NSEW, padx=2, pady=2)
|
||||||
|
self.resize_scene_3 = ttk.Button(self.resize, text="Scene 3", command=self.action_resize_scene_3)
|
||||||
|
self.resize_scene_3.grid(row=4, column=0, sticky=NSEW, padx=2, pady=2)
|
||||||
|
self.resize_scene_4 = ttk.Button(self.resize, text="Scene 4", command=self.action_resize_scene_4)
|
||||||
|
self.resize_scene_4.grid(row=4, column=1, sticky=NSEW, padx=2, pady=2)
|
||||||
|
|
||||||
|
@api_action
|
||||||
|
def action_resize_on(self):
|
||||||
|
spiceapi.image_resize_enable(self.app.connection, True)
|
||||||
|
|
||||||
|
@api_action
|
||||||
|
def action_resize_false(self):
|
||||||
|
spiceapi.image_resize_enable(self.app.connection, False)
|
||||||
|
|
||||||
|
@api_action
|
||||||
|
def action_resize_scene_1(self):
|
||||||
|
spiceapi.image_resize_set_scene(self.app.connection, 1)
|
||||||
|
|
||||||
|
@api_action
|
||||||
|
def action_resize_scene_2(self):
|
||||||
|
spiceapi.image_resize_set_scene(self.app.connection, 2)
|
||||||
|
|
||||||
|
@api_action
|
||||||
|
def action_resize_scene_3(self):
|
||||||
|
spiceapi.image_resize_set_scene(self.app.connection, 3)
|
||||||
|
|
||||||
|
@api_action
|
||||||
|
def action_resize_scene_4(self):
|
||||||
|
spiceapi.image_resize_set_scene(self.app.connection, 4)
|
||||||
|
|
||||||
class MainApp(ttk.Frame):
|
class MainApp(ttk.Frame):
|
||||||
"""The main application frame."""
|
"""The main application frame."""
|
||||||
@@ -419,6 +478,8 @@ class MainApp(ttk.Frame):
|
|||||||
self.tabs.add(self.tab_analogs, text="Analogs")
|
self.tabs.add(self.tab_analogs, text="Analogs")
|
||||||
self.tab_lights = LightsTab(self, self.tabs)
|
self.tab_lights = LightsTab(self, self.tabs)
|
||||||
self.tabs.add(self.tab_lights, text="Lights")
|
self.tabs.add(self.tab_lights, text="Lights")
|
||||||
|
self.tab_resize = ResizeTab(self, self.tabs)
|
||||||
|
self.tabs.add(self.tab_resize, text="Resize")
|
||||||
self.tab_manual = ManualTab(self, self.tabs)
|
self.tab_manual = ManualTab(self, self.tabs)
|
||||||
self.tabs.add(self.tab_manual, text="Manual")
|
self.tabs.add(self.tab_manual, text="Manual")
|
||||||
self.tabs.pack(expand=True, fill=tk.BOTH)
|
self.tabs.pack(expand=True, fill=tk.BOTH)
|
||||||
|
|||||||
@@ -76,14 +76,22 @@ namespace cfg {
|
|||||||
eamuse_get_game(),
|
eamuse_get_game(),
|
||||||
use_game_setting,
|
use_game_setting,
|
||||||
root);
|
root);
|
||||||
load_int_value(doc, root + "offset_x", this->offset_x);
|
|
||||||
load_int_value(doc, root + "offset_y", this->offset_y);
|
|
||||||
load_float_value(doc, root + "scale_x", this->scale_x);
|
|
||||||
load_float_value(doc, root + "scale_y", this->scale_y);
|
|
||||||
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
||||||
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||||
load_bool_value(doc, root + "keep_aspect_ratio", this->keep_aspect_ratio);
|
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||||
load_bool_value(doc, root + "centered", this->centered);
|
auto& scene = this->scene_settings[i];
|
||||||
|
std::string prefix = "";
|
||||||
|
if (0 < i) {
|
||||||
|
prefix += fmt::format("scenes/{}/", i-1);
|
||||||
|
}
|
||||||
|
load_int_value(doc, root + prefix + "offset_x", scene.offset_x);
|
||||||
|
load_int_value(doc, root + prefix + "offset_y", scene.offset_y);
|
||||||
|
load_float_value(doc, root + prefix + "scale_x", scene.scale_x);
|
||||||
|
load_float_value(doc, root + prefix + "scale_y", scene.scale_y);
|
||||||
|
load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio);
|
||||||
|
load_bool_value(doc, root + prefix + "centered", scene.centered);
|
||||||
|
}
|
||||||
|
|
||||||
// windowed settings are always under game settings
|
// windowed settings are always under game settings
|
||||||
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
||||||
@@ -189,14 +197,21 @@ namespace cfg {
|
|||||||
root);
|
root);
|
||||||
|
|
||||||
// full screen image settings
|
// full screen image settings
|
||||||
rapidjson::Pointer(root + "offset_x").Set(doc, this->offset_x);
|
|
||||||
rapidjson::Pointer(root + "offset_y").Set(doc, this->offset_y);
|
|
||||||
rapidjson::Pointer(root + "scale_x").Set(doc, this->scale_x);
|
|
||||||
rapidjson::Pointer(root + "scale_y").Set(doc, this->scale_y);
|
|
||||||
rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize);
|
rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize);
|
||||||
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
|
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
|
||||||
rapidjson::Pointer(root + "keep_aspect_ratio").Set(doc, this->keep_aspect_ratio);
|
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||||
rapidjson::Pointer(root + "centered").Set(doc, this->centered);
|
auto& scene = this->scene_settings[i];
|
||||||
|
std::string prefix = "";
|
||||||
|
if (0 < i) {
|
||||||
|
prefix += fmt::format("scenes/{}/", i-1);
|
||||||
|
}
|
||||||
|
rapidjson::Pointer(root + prefix + "offset_x").Set(doc, scene.offset_x);
|
||||||
|
rapidjson::Pointer(root + prefix + "offset_y").Set(doc, scene.offset_y);
|
||||||
|
rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x);
|
||||||
|
rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y);
|
||||||
|
rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio);
|
||||||
|
rapidjson::Pointer(root + prefix + "centered").Set(doc, scene.centered);
|
||||||
|
}
|
||||||
|
|
||||||
// windowed mode settings
|
// windowed mode settings
|
||||||
rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top);
|
rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top);
|
||||||
|
|||||||
@@ -14,6 +14,15 @@ namespace cfg {
|
|||||||
ResizableFrame = 2
|
ResizableFrame = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct fullscreen_setting {
|
||||||
|
int offset_x = 0;
|
||||||
|
int offset_y = 0;
|
||||||
|
float scale_x = 1.0;
|
||||||
|
float scale_y = 1.0;
|
||||||
|
bool keep_aspect_ratio = true;
|
||||||
|
bool centered = true;
|
||||||
|
};
|
||||||
|
|
||||||
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
||||||
|
|
||||||
class ScreenResize {
|
class ScreenResize {
|
||||||
@@ -31,14 +40,10 @@ namespace cfg {
|
|||||||
~ScreenResize();
|
~ScreenResize();
|
||||||
|
|
||||||
// full screen (directx) image settings
|
// full screen (directx) image settings
|
||||||
int offset_x = 0;
|
|
||||||
int offset_y = 0;
|
|
||||||
float scale_x = 1.0;
|
|
||||||
float scale_y = 1.0;
|
|
||||||
bool enable_screen_resize = false;
|
bool enable_screen_resize = false;
|
||||||
|
int8_t screen_resize_current_scene = 0;
|
||||||
bool enable_linear_filter = true;
|
bool enable_linear_filter = true;
|
||||||
bool keep_aspect_ratio = true;
|
fullscreen_setting scene_settings[4];
|
||||||
bool centered = true;
|
|
||||||
|
|
||||||
// windowed mode sizing
|
// windowed mode sizing
|
||||||
// Windows terminology:
|
// Windows terminology:
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
03/25/2025 [spice2x]
|
||||||
|
Add Mahjong Fight Girl support
|
||||||
|
Add option for custom full screen resolution (-forceres)
|
||||||
|
|
||||||
|
03/24/2025 [spice2x]
|
||||||
|
Auto PIN entry
|
||||||
|
Multiple scenes for image resize
|
||||||
|
API: add resize function, lights.read can now filter lights by name
|
||||||
|
Fix DLL hooks not receiving command-line arguments
|
||||||
|
|
||||||
03/16/2025 [spice2x]
|
03/16/2025 [spice2x]
|
||||||
Fix clipboard copy function truncating last character of string
|
Fix clipboard copy function truncating last character of string
|
||||||
|
|
||||||
|
|||||||
@@ -100,9 +100,25 @@ namespace games::iidx {
|
|||||||
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||||
|
|
||||||
log_info("iidx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
log_info("iidx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||||
|
const auto result = RegOpenKeyExA_orig(
|
||||||
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
real_asio_reg_handle,
|
||||||
|
ASIO_DRIVER.value().c_str(),
|
||||||
|
ulOptions,
|
||||||
|
samDesired,
|
||||||
&real_asio_device_reg_handle);
|
&real_asio_device_reg_handle);
|
||||||
|
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
log_warning(
|
||||||
|
"iidx::asio",
|
||||||
|
"failed to open registry subkey '{}', error=0x{:x}",
|
||||||
|
ASIO_DRIVER.value().c_str(), result);
|
||||||
|
log_warning(
|
||||||
|
"iidx::asio",
|
||||||
|
"due to improper ASIO setting, game will likely fail to launch; double check -iidxasio and the registry",
|
||||||
|
ASIO_DRIVER.value().c_str(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "bc/io.h"
|
#include "bc/io.h"
|
||||||
#include "ccj/io.h"
|
#include "ccj/io.h"
|
||||||
#include "qks/io.h"
|
#include "qks/io.h"
|
||||||
|
#include "mfg/io.h"
|
||||||
|
|
||||||
namespace games {
|
namespace games {
|
||||||
|
|
||||||
@@ -305,6 +306,13 @@ namespace games {
|
|||||||
buttons.insert({ qks, qks::get_buttons() });
|
buttons.insert({ qks, qks::get_buttons() });
|
||||||
buttons_help.insert({ qks, qks::get_buttons_help() });
|
buttons_help.insert({ qks, qks::get_buttons_help() });
|
||||||
file_hints[qks].emplace_back("game/uks.exe");
|
file_hints[qks].emplace_back("game/uks.exe");
|
||||||
|
|
||||||
|
// Mahjong Fight Girl
|
||||||
|
const std::string mfg("Mahjong Fight Girl");
|
||||||
|
games.push_back(mfg);
|
||||||
|
buttons.insert({ mfg, mfg::get_buttons() });
|
||||||
|
buttons_help.insert({ mfg, mfg::get_buttons_help() });
|
||||||
|
file_hints[mfg].emplace_back("game/MFGClient_Data");
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> &get_games() {
|
const std::vector<std::string> &get_games() {
|
||||||
@@ -437,8 +445,20 @@ namespace games {
|
|||||||
vkey_defaults.push_back(VK_F12);
|
vkey_defaults.push_back(VK_F12);
|
||||||
names.emplace_back("Toggle Camera Control");
|
names.emplace_back("Toggle Camera Control");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
|
names.emplace_back("Player 1 PIN Macro");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
|
names.emplace_back("Player 2 PIN Macro");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Screen Resize");
|
names.emplace_back("Screen Resize");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
|
names.emplace_back("Screen Resize Scene 1");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
|
names.emplace_back("Screen Resize Scene 2");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
|
names.emplace_back("Screen Resize Scene 3");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
|
names.emplace_back("Screen Resize Scene 4");
|
||||||
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Force Exit Game");
|
names.emplace_back("Force Exit Game");
|
||||||
vkey_defaults.push_back(0xFF);
|
vkey_defaults.push_back(0xFF);
|
||||||
names.emplace_back("Navigator Activate");
|
names.emplace_back("Navigator Activate");
|
||||||
|
|||||||
@@ -22,7 +22,13 @@ namespace games {
|
|||||||
ToggleScreenResize,
|
ToggleScreenResize,
|
||||||
ToggleOverlay,
|
ToggleOverlay,
|
||||||
ToggleCameraControl,
|
ToggleCameraControl,
|
||||||
|
TriggerPinMacroP1,
|
||||||
|
TriggerPinMacroP2,
|
||||||
ScreenResize,
|
ScreenResize,
|
||||||
|
ScreenResizeScene1,
|
||||||
|
ScreenResizeScene2,
|
||||||
|
ScreenResizeScene3,
|
||||||
|
ScreenResizeScene4,
|
||||||
SuperExit,
|
SuperExit,
|
||||||
NavigatorActivate,
|
NavigatorActivate,
|
||||||
NavigatorCancel,
|
NavigatorCancel,
|
||||||
|
|||||||
@@ -0,0 +1,385 @@
|
|||||||
|
#include "bi2a_hook.h"
|
||||||
|
|
||||||
|
#include "util/execexe.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "util/tapeled.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* class definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct AIO_SCI_COMM {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR_IOB {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__INPUTDATA {
|
||||||
|
uint8_t data[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__OUTPUTDATA {
|
||||||
|
uint8_t data[40];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__INPUT
|
||||||
|
{
|
||||||
|
uint8_t bPcPowerCheck;
|
||||||
|
uint8_t CoinCount;
|
||||||
|
uint8_t bTest;
|
||||||
|
uint8_t bService;
|
||||||
|
uint8_t bCoinSw;
|
||||||
|
uint8_t bCoinJam;
|
||||||
|
uint8_t bCabinet;
|
||||||
|
uint8_t bHPDetect;
|
||||||
|
uint8_t bRecDetect;
|
||||||
|
uint8_t bStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB_BI2A_VFG__DEVSTATUS
|
||||||
|
{
|
||||||
|
uint8_t MechType;
|
||||||
|
uint8_t bIsBi2a;
|
||||||
|
uint8_t IoCounter;
|
||||||
|
AIO_IOB_BI2A_VFG__INPUT Input;
|
||||||
|
AIO_IOB_BI2A_VFG__INPUTDATA InputData;
|
||||||
|
AIO_IOB_BI2A_VFG__OUTPUTDATA OutputData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* typedefs
|
||||||
|
*/
|
||||||
|
|
||||||
|
// libaio-iob_video.dll
|
||||||
|
typedef AIO_SCI_COMM* (__fastcall *aioIobBi2a_OpenSciUsbCdc_t)(uint32_t i_SerialNumber);
|
||||||
|
typedef AIO_IOB_BI2A_VFG* (__fastcall *aioIobBi2aVFG_Create_t)(AIO_NMGR_IOB *i_pNodeMgr, uint32_t i_DevId);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetMechType_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, uint32_t i_MechType);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_GetDeviceStatus_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl,
|
||||||
|
AIO_IOB_BI2A_VFG__DEVSTATUS *o_pDevStatus,
|
||||||
|
uint32_t i_cbDevStatus);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetWatchDogTimer_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint16_t i_Count);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_ControlCoinBlocker_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Slot,
|
||||||
|
bool i_bOpen);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_AddCounter_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Counter,
|
||||||
|
uint32_t i_Count);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetAmpVolume_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Amp,
|
||||||
|
uint32_t i_Volume);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_EnableUsbCharger_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, bool i_bEnable);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetLamp_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Lamp, uint8_t i_Bright);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetIccrLed_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_RGB);
|
||||||
|
typedef void (__fastcall *aioIobBi2aVFG_SetLedData_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint8_t *i_pData,
|
||||||
|
uint32_t i_cbData);
|
||||||
|
|
||||||
|
// libaio-iob.dll
|
||||||
|
typedef AIO_NMGR_IOB* (__fastcall *aioNMgrIob_Create_t)(AIO_SCI_COMM* i_pSci, uint32_t i_bfMode);
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
typedef void (__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB *i_pNodeMgr);
|
||||||
|
typedef int32_t (__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB *i_pNodeMgr);
|
||||||
|
typedef bool (__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State);
|
||||||
|
typedef bool (__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State);
|
||||||
|
typedef void (__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl);
|
||||||
|
typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl);
|
||||||
|
typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State);
|
||||||
|
typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* function pointers
|
||||||
|
*/
|
||||||
|
|
||||||
|
// libaio-iob_video.dll
|
||||||
|
static aioIobBi2a_OpenSciUsbCdc_t aioIobBi2a_OpenSciUsbCdc_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_Create_t aioIobBi2aVFG_Create_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetMechType_t aioIobBi2aVFG_SetMechType_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_GetDeviceStatus_t aioIobBi2aVFG_GetDeviceStatus_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetWatchDogTimer_t aioIobBi2aVFG_SetWatchDogTimer_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_ControlCoinBlocker_t aioIobBi2aVFG_ControlCoinBlocker_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_AddCounter_t aioIobBi2aVFG_AddCounter_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetAmpVolume_t aioIobBi2aVFG_SetAmpVolume_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_EnableUsbCharger_t aioIobBi2aVFG_EnableUsbCharger_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetLamp_t aioIobBi2aVFG_SetLamp_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetIccrLed_t aioIobBi2aVFG_SetIccrLed_orig = nullptr;
|
||||||
|
static aioIobBi2aVFG_SetLedData_t aioIobBi2aVFG_SetLedData_orig = nullptr;
|
||||||
|
|
||||||
|
// libaio-iob.dll
|
||||||
|
static aioNMgrIob_Create_t aioNMgrIob_Create_orig = nullptr;
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
static aioNodeMgr_Destroy_t aioNodeMgr_Destroy_orig = nullptr;
|
||||||
|
static aioNodeMgr_GetState_t aioNodeMgr_GetState_orig = nullptr;
|
||||||
|
static aioNodeMgr_IsReady_t aioNodeMgr_IsReady_orig = nullptr;
|
||||||
|
static aioNodeMgr_IsError_t aioNodeMgr_IsError_orig = nullptr;
|
||||||
|
static aioNodeCtl_Destroy_t aioNodeCtl_Destroy_orig = nullptr;
|
||||||
|
static aioNodeCtl_GetState_t aioNodeCtl_GetState_orig = nullptr;
|
||||||
|
static aioNodeCtl_IsReady_t aioNodeCtl_IsReady_orig = nullptr;
|
||||||
|
static aioNodeCtl_IsError_t aioNodeCtl_IsError_orig = nullptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
static AIO_SCI_COMM *aioSciComm;
|
||||||
|
static AIO_NMGR_IOB *aioNmgrIob;
|
||||||
|
static AIO_IOB_BI2A_VFG *aioIobBi2aVfg;
|
||||||
|
static uint8_t mechType = 0;
|
||||||
|
static uint8_t count = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* implementations
|
||||||
|
*/
|
||||||
|
|
||||||
|
static AIO_SCI_COMM* __fastcall aioIob2Bi2a_OpenSciUsbCdc(uint32_t i_SerialNumber) {
|
||||||
|
log_info("bi2a_hook", "aioIob2Bi2a_OpenSciUsbCdc hook hit");
|
||||||
|
aioSciComm = new AIO_SCI_COMM;
|
||||||
|
return aioSciComm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AIO_IOB_BI2A_VFG* __fastcall aioIobBi2aVFG_Create(AIO_NMGR_IOB *i_pNodeMgr, uint32_t i_DevId) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
log_info("bi2a_hook", "node created");
|
||||||
|
aioIobBi2aVfg = new AIO_IOB_BI2A_VFG;
|
||||||
|
return aioIobBi2aVfg;
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_Create_orig(i_pNodeMgr, i_DevId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetMechType(AIO_IOB_BI2A_VFG *i_pNodeCtl, uint32_t i_MechType) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
mechType = i_MechType;
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetMechType_orig(i_pNodeCtl, i_MechType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_GetDeviceStatus(AIO_IOB_BI2A_VFG* i_pNodeCtl,
|
||||||
|
AIO_IOB_BI2A_VFG__DEVSTATUS *o_pDevStatus,
|
||||||
|
uint32_t i_cbDevStatus) {
|
||||||
|
|
||||||
|
RI_MGR->devices_flush_output();
|
||||||
|
|
||||||
|
if (i_pNodeCtl != aioIobBi2aVfg) {
|
||||||
|
return aioIobBi2aVFG_GetDeviceStatus_orig(i_pNodeCtl, o_pDevStatus, i_cbDevStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(o_pDevStatus, 0x00, sizeof(AIO_IOB_BI2A_VFG__DEVSTATUS));
|
||||||
|
|
||||||
|
o_pDevStatus->MechType = mechType;
|
||||||
|
o_pDevStatus->bIsBi2a = 1;
|
||||||
|
o_pDevStatus->IoCounter = count++;
|
||||||
|
|
||||||
|
auto &buttons = get_buttons();
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]))
|
||||||
|
o_pDevStatus->Input.bTest = 1;
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]))
|
||||||
|
o_pDevStatus->Input.bService = 1;
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]))
|
||||||
|
o_pDevStatus->Input.bCoinSw = 1;
|
||||||
|
|
||||||
|
o_pDevStatus->Input.CoinCount = eamuse_coin_get_stock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetWatchDogTimer(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint16_t i_Count) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_ControlCoinBlocker(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Slot, bool i_bOpen) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_ControlCoinBlocker_orig(i_pNodeCtl, i_Slot, i_bOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_AddCounter(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetAmpVolume(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Amp, uint32_t i_Volume) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetAmpVolume_orig(i_pNodeCtl, i_Amp, i_Volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_EnableUsbCharger(AIO_IOB_BI2A_VFG* i_pNodeCtl, bool i_bEnable) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_EnableUsbCharger_orig(i_pNodeCtl, i_bEnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetLamp(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Lamp, uint8_t i_Bright) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetLamp_orig(i_pNodeCtl, i_Lamp, i_Bright);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetIccrLed(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_RGB) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetIccrLed_orig(i_pNodeCtl, i_RGB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall aioIobBi2aVFG_SetLedData(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint8_t *i_pData, uint32_t i_cbData) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
} else {
|
||||||
|
return aioIobBi2aVFG_SetLedData_orig(i_pNodeCtl, i_pData, i_cbData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AIO_NMGR_IOB* __fastcall aioNMgrIob_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||||
|
if (i_pSci == aioSciComm) {
|
||||||
|
aioNmgrIob = new AIO_NMGR_IOB;
|
||||||
|
return aioNmgrIob;
|
||||||
|
} else {
|
||||||
|
return aioNMgrIob_Create_orig(i_pSci, i_bfMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioNodeMgr_Destroy(AIO_NMGR_IOB *i_pNodeMgr) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
delete aioNmgrIob;
|
||||||
|
aioNmgrIob = nullptr;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB *i_pNodeMgr) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_GetState_orig(i_pNodeMgr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioNodeCtl_Destroy(AIO_IOB_BI2A_VFG *i_pNodeCtl) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
delete aioIobBi2aVfg;
|
||||||
|
aioIobBi2aVfg = nullptr;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB_BI2A_VFG *i_pNodeCtl) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_GetState_orig(i_pNodeCtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeCtl_IsError(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State) {
|
||||||
|
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bi2a_hook_init() {
|
||||||
|
// avoid double init
|
||||||
|
static bool initialized = false;
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// announce
|
||||||
|
log_info("bi2a_hook", "init");
|
||||||
|
|
||||||
|
// libaio-iob_video.dll
|
||||||
|
const auto libaioIobVideoDll = "libaio-iob_video.dll";
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2a_OpenSciUsbCdc",
|
||||||
|
aioIob2Bi2a_OpenSciUsbCdc, &aioIobBi2a_OpenSciUsbCdc_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_Create",
|
||||||
|
aioIobBi2aVFG_Create, &aioIobBi2aVFG_Create_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetMechType",
|
||||||
|
aioIobBi2aVFG_SetMechType, &aioIobBi2aVFG_SetMechType_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_GetDeviceStatus",
|
||||||
|
aioIobBi2aVFG_GetDeviceStatus, &aioIobBi2aVFG_GetDeviceStatus_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetWatchDogTimer",
|
||||||
|
aioIobBi2aVFG_SetWatchDogTimer, &aioIobBi2aVFG_SetWatchDogTimer_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_ControlCoinBlocker",
|
||||||
|
aioIobBi2aVFG_ControlCoinBlocker, &aioIobBi2aVFG_ControlCoinBlocker_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_AddCounter",
|
||||||
|
aioIobBi2aVFG_AddCounter, &aioIobBi2aVFG_AddCounter_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetAmpVolume",
|
||||||
|
aioIobBi2aVFG_SetAmpVolume, &aioIobBi2aVFG_SetAmpVolume_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_EnableUsbCharger",
|
||||||
|
aioIobBi2aVFG_EnableUsbCharger, &aioIobBi2aVFG_EnableUsbCharger_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetLamp",
|
||||||
|
aioIobBi2aVFG_SetLamp, &aioIobBi2aVFG_SetLamp_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetIccrLed",
|
||||||
|
aioIobBi2aVFG_SetIccrLed, &aioIobBi2aVFG_SetIccrLed_orig);
|
||||||
|
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetLedData",
|
||||||
|
aioIobBi2aVFG_SetLedData, &aioIobBi2aVFG_SetLedData_orig);
|
||||||
|
|
||||||
|
// libaio-iob.dll
|
||||||
|
const auto libaioIobDll = "libaio-iob.dll";
|
||||||
|
execexe::trampoline_try(libaioIobDll, "aioNMgrIob_Create",
|
||||||
|
aioNMgrIob_Create, &aioNMgrIob_Create_orig);
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
const auto libaioDll = "libaio.dll";
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
|
||||||
|
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_GetState",
|
||||||
|
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
|
||||||
|
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsError",
|
||||||
|
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
|
||||||
|
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_GetState",
|
||||||
|
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
|
||||||
|
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
|
||||||
|
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||||
|
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
void bi2a_hook_init();
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
std::vector<Button> &games::mfg::get_buttons() {
|
||||||
|
static std::vector<Button> buttons;
|
||||||
|
|
||||||
|
if (buttons.empty()) {
|
||||||
|
buttons = GameAPI::Buttons::getButtons("Mahjong Fight Girl");
|
||||||
|
|
||||||
|
GameAPI::Buttons::sortButtons(
|
||||||
|
&buttons,
|
||||||
|
"Service",
|
||||||
|
"Test",
|
||||||
|
"Coin Mech"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string games::mfg::get_buttons_help() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "cfg/api.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
namespace Buttons {
|
||||||
|
enum {
|
||||||
|
Service,
|
||||||
|
Test,
|
||||||
|
CoinMech
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// getters
|
||||||
|
std::vector<Button> &get_buttons();
|
||||||
|
std::string get_buttons_help();
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#include "mfg.h"
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include "util/fileutils.h"
|
||||||
|
#include "util/unity_player.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
#include "util/execexe.h"
|
||||||
|
#include "acioemu/handle.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "bi2a_hook.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
std::string MFG_INJECT_ARGS = "";
|
||||||
|
std::string MFG_CABINET_TYPE = "HG";
|
||||||
|
bool MFG_NO_IO = false;
|
||||||
|
|
||||||
|
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||||
|
static std::wstring portName;
|
||||||
|
|
||||||
|
void MFGGame::attach() {
|
||||||
|
Game::attach();
|
||||||
|
|
||||||
|
// create required files
|
||||||
|
fileutils::dir_create_recursive("dev/raw/log");
|
||||||
|
fileutils::bin_write("dev/raw/log/output_log.txt", nullptr, 0);
|
||||||
|
|
||||||
|
SetEnvironmentVariableA("VFG_CABINET_TYPE", MFG_CABINET_TYPE.c_str());
|
||||||
|
|
||||||
|
// add card reader
|
||||||
|
portName = MFG_CABINET_TYPE == "UKS" ? L"\\\\.\\COM1" : L"\\\\.\\COM3";
|
||||||
|
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
|
||||||
|
devicehook_init_trampoline();
|
||||||
|
devicehook_add(acioHandle);
|
||||||
|
|
||||||
|
execexe::init();
|
||||||
|
execexe::init_port_hook(portName, acioHandle);
|
||||||
|
|
||||||
|
if (GRAPHICS_SHOW_CURSOR) {
|
||||||
|
unity_utils::force_show_cursor(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
unity_utils::set_args(
|
||||||
|
std::format("{} {}{}",
|
||||||
|
GetCommandLineA(),
|
||||||
|
MFG_INJECT_ARGS,
|
||||||
|
unity_utils::get_unity_player_args()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MFGGame::post_attach() {
|
||||||
|
Game::post_attach();
|
||||||
|
|
||||||
|
execexe::load_library("libaio.dll");
|
||||||
|
execexe::load_library("libaio-iob.dll");
|
||||||
|
execexe::load_library("libaio-iob_video.dll");
|
||||||
|
execexe::load_library("libaio-iob2_video.dll");
|
||||||
|
execexe::load_library("win10actlog.dll");
|
||||||
|
|
||||||
|
if (!MFG_NO_IO) {
|
||||||
|
// insert BI2* hooks
|
||||||
|
if (MFG_CABINET_TYPE == "UKS") {
|
||||||
|
log_fatal("mfg", "UKS io is not supported");
|
||||||
|
} else {
|
||||||
|
bi2a_hook_init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MFGGame::detach() {
|
||||||
|
Game::detach();
|
||||||
|
|
||||||
|
devicehook_dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "games/game.h"
|
||||||
|
|
||||||
|
namespace games::mfg {
|
||||||
|
extern std::string MFG_INJECT_ARGS;
|
||||||
|
extern std::string MFG_CABINET_TYPE;
|
||||||
|
extern bool MFG_NO_IO;
|
||||||
|
|
||||||
|
class MFGGame : public games::Game {
|
||||||
|
public:
|
||||||
|
MFGGame() : Game("Mahjong Fight Girl") {}
|
||||||
|
|
||||||
|
virtual void attach() override;
|
||||||
|
virtual void post_attach() override;
|
||||||
|
virtual void detach() override;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -79,9 +79,25 @@ namespace games::sdvx {
|
|||||||
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||||
|
|
||||||
log_info("sdvx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
log_info("sdvx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||||
|
const auto result = RegOpenKeyExA_orig(
|
||||||
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
real_asio_reg_handle,
|
||||||
|
ASIO_DRIVER.value().c_str(),
|
||||||
|
ulOptions,
|
||||||
|
samDesired,
|
||||||
&real_asio_device_reg_handle);
|
&real_asio_device_reg_handle);
|
||||||
|
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
log_warning(
|
||||||
|
"sdvx::asio",
|
||||||
|
"failed to open registry subkey '{}', error=0x{:x}",
|
||||||
|
ASIO_DRIVER.value().c_str(), result);
|
||||||
|
log_warning(
|
||||||
|
"sdvx::asio",
|
||||||
|
"due to improper ASIO setting, game will likely fall back to WASAPI; double check -sdvxasio and the registry",
|
||||||
|
ASIO_DRIVER.value().c_str(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -680,7 +680,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
|||||||
|
|
||||||
// dump presentation parameters
|
// dump presentation parameters
|
||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
const auto *params = &pPresentationParameters[i];
|
auto *params = &pPresentationParameters[i];
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||||
|
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
|
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||||
|
}
|
||||||
|
|
||||||
log_info("graphics::d3d9",
|
log_info("graphics::d3d9",
|
||||||
"D3D9 presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
|
"D3D9 presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
|
||||||
@@ -846,7 +851,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
const auto *params = &pPresentationParameters[i];
|
auto *params = &pPresentationParameters[i];
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||||
|
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
|
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||||
|
}
|
||||||
|
|
||||||
log_info("graphics::d3d9",
|
log_info("graphics::d3d9",
|
||||||
"D3D9Ex presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
|
"D3D9Ex presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
|
||||||
@@ -870,7 +880,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
}
|
}
|
||||||
if (pFullscreenDisplayMode) {
|
if (pFullscreenDisplayMode) {
|
||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
const auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||||
|
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
|
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||||
|
}
|
||||||
|
|
||||||
log_info("graphics::d3d9",
|
log_info("graphics::d3d9",
|
||||||
"D3D9Ex fullscreen display mode for adapter {}: Width: {}, Height: {}, RefreshRate: {}, "
|
"D3D9Ex fullscreen display mode for adapter {}: Width: {}, Height: {}, RefreshRate: {}, "
|
||||||
|
|||||||
@@ -740,9 +740,10 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
|||||||
topSurface->UnlockRect();
|
topSurface->UnlockRect();
|
||||||
|
|
||||||
// do the actual zoom / offset math
|
// do the actual zoom / offset math
|
||||||
if (cfg::SCREENRESIZE->centered) {
|
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||||
targetRect.right = (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
if (scene.centered) {
|
||||||
targetRect.bottom = (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
targetRect.right = (w + rectLeft) / scene.scale_x;
|
||||||
|
targetRect.bottom = (h + rectTop) / scene.scale_y;
|
||||||
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
|
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
|
||||||
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
|
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
|
||||||
targetRect.top -= deltaH;
|
targetRect.top -= deltaH;
|
||||||
@@ -750,12 +751,12 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
|||||||
targetRect.left -= deltaW;
|
targetRect.left -= deltaW;
|
||||||
targetRect.right -= deltaW;
|
targetRect.right -= deltaW;
|
||||||
} else {
|
} else {
|
||||||
targetRect.left -= cfg::SCREENRESIZE->offset_x;
|
targetRect.left -= scene.offset_x;
|
||||||
targetRect.top += cfg::SCREENRESIZE->offset_y;
|
targetRect.top += scene.offset_y;
|
||||||
targetRect.right = -cfg::SCREENRESIZE->offset_x;
|
targetRect.right = -scene.offset_x;
|
||||||
targetRect.right += (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
targetRect.right += (w + rectLeft) / scene.scale_x;
|
||||||
targetRect.bottom = cfg::SCREENRESIZE->offset_y;
|
targetRect.bottom = scene.offset_y;
|
||||||
targetRect.bottom += (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
targetRect.bottom += (h + rectTop) / scene.scale_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw to back buffer
|
// draw to back buffer
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ graphics_dx9on12_state GRAPHICS_9_ON_12_STATE = DX9ON12_AUTO;
|
|||||||
bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
|
bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
|
||||||
bool SUBSCREEN_FORCE_REDRAW = false;
|
bool SUBSCREEN_FORCE_REDRAW = false;
|
||||||
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
|
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ extern UINT GRAPHICS_FORCE_REFRESH;
|
|||||||
extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
||||||
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
|
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
|
||||||
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
|
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
|
||||||
|
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||||
|
|
||||||
extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE;
|
extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE;
|
||||||
extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME;
|
extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME;
|
||||||
|
|||||||
@@ -123,10 +123,6 @@ void graphics_load_windowed_parameters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_debug("graphics-windowed", "graphics_load_windowed_parameters called");
|
log_debug("graphics-windowed", "graphics_load_windowed_parameters called");
|
||||||
const auto remove_spaces = [](const char& c) {
|
|
||||||
return c == ' ';
|
|
||||||
};
|
|
||||||
|
|
||||||
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
||||||
log_debug(
|
log_debug(
|
||||||
"graphics-windowed",
|
"graphics-windowed",
|
||||||
@@ -148,13 +144,11 @@ void graphics_load_windowed_parameters() {
|
|||||||
log_debug(
|
log_debug(
|
||||||
"graphics-windowed",
|
"graphics-windowed",
|
||||||
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_POS");
|
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_POS");
|
||||||
int32_t x, y;
|
std::pair<uint32_t, uint32_t> result;
|
||||||
auto s = GRAPHICS_WINDOW_POS.value();
|
if (parse_width_height(GRAPHICS_WINDOW_POS.value(), result)) {
|
||||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
|
||||||
if (sscanf(s.c_str(), "%d,%d", &x, &y) == 2) {
|
|
||||||
cfg::SCREENRESIZE->enable_window_resize = true;
|
cfg::SCREENRESIZE->enable_window_resize = true;
|
||||||
cfg::SCREENRESIZE->window_offset_x = x;
|
cfg::SCREENRESIZE->window_offset_x = result.first;
|
||||||
cfg::SCREENRESIZE->window_offset_y = y;
|
cfg::SCREENRESIZE->window_offset_y = result.second;
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -windowpos");
|
log_warning("graphics-windowed", "failed to parse -windowpos");
|
||||||
}
|
}
|
||||||
@@ -174,20 +168,16 @@ void graphics_load_windowed_subscreen_parameters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called");
|
log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called");
|
||||||
const auto remove_spaces = [](const char& c) {
|
|
||||||
return c == ' ';
|
|
||||||
};
|
|
||||||
|
|
||||||
if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) {
|
if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) {
|
||||||
log_debug(
|
log_debug(
|
||||||
"graphics-windowed",
|
"graphics-windowed",
|
||||||
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE");
|
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE");
|
||||||
uint32_t w, h;
|
|
||||||
auto s = GRAPHICS_IIDX_WSUB_SIZE.value();
|
std::pair<uint32_t, uint32_t> result;
|
||||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
if (parse_width_height(GRAPHICS_IIDX_WSUB_SIZE.value(), result)) {
|
||||||
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
|
GRAPHICS_IIDX_WSUB_WIDTH = result.first;
|
||||||
GRAPHICS_IIDX_WSUB_WIDTH = w;
|
GRAPHICS_IIDX_WSUB_HEIGHT = result.second;
|
||||||
GRAPHICS_IIDX_WSUB_HEIGHT = h;
|
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
||||||
}
|
}
|
||||||
@@ -197,12 +187,11 @@ void graphics_load_windowed_subscreen_parameters() {
|
|||||||
log_debug(
|
log_debug(
|
||||||
"graphics-windowed",
|
"graphics-windowed",
|
||||||
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS");
|
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS");
|
||||||
int32_t x, y;
|
|
||||||
auto s = GRAPHICS_IIDX_WSUB_POS.value();
|
std::pair<uint32_t, uint32_t> result;
|
||||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
if (parse_width_height(GRAPHICS_IIDX_WSUB_POS.value(), result)) {
|
||||||
if (sscanf(s.c_str(), "%d,%d", &x, &y) == 2) {
|
GRAPHICS_IIDX_WSUB_X = result.first;
|
||||||
GRAPHICS_IIDX_WSUB_X = x;
|
GRAPHICS_IIDX_WSUB_Y = result.second;
|
||||||
GRAPHICS_IIDX_WSUB_Y = y;
|
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@
|
|||||||
#include "games/ccj/ccj.h"
|
#include "games/ccj/ccj.h"
|
||||||
#include "games/ccj/trackball.h"
|
#include "games/ccj/trackball.h"
|
||||||
#include "games/qks/qks.h"
|
#include "games/qks/qks.h"
|
||||||
|
#include "games/mfg/mfg.h"
|
||||||
#include "games/museca/museca.h"
|
#include "games/museca/museca.h"
|
||||||
#include "hooks/avshook.h"
|
#include "hooks/avshook.h"
|
||||||
#include "hooks/audio/audio.h"
|
#include "hooks/audio/audio.h"
|
||||||
@@ -101,6 +102,7 @@
|
|||||||
#include "util/sysutils.h"
|
#include "util/sysutils.h"
|
||||||
#include "util/tapeled.h"
|
#include "util/tapeled.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
|
#include "util/utils.h"
|
||||||
#include "avs/ssl.h"
|
#include "avs/ssl.h"
|
||||||
#include "nvapi/nvapi.h"
|
#include "nvapi/nvapi.h"
|
||||||
#include "hooks/graphics/nvapi_hook.h"
|
#include "hooks/graphics/nvapi_hook.h"
|
||||||
@@ -143,6 +145,8 @@ static bool check_dll(const std::string &model) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_msvcrt_args(int argc, char *argv[]);
|
||||||
|
|
||||||
int main_implementation(int argc, char *argv[]) {
|
int main_implementation(int argc, char *argv[]) {
|
||||||
|
|
||||||
// remember argv, argv
|
// remember argv, argv
|
||||||
@@ -211,6 +215,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bool attach_bc = false;
|
bool attach_bc = false;
|
||||||
bool attach_ccj = false;
|
bool attach_ccj = false;
|
||||||
bool attach_qks = false;
|
bool attach_qks = false;
|
||||||
|
bool attach_mfg = false;
|
||||||
bool attach_museca = false;
|
bool attach_museca = false;
|
||||||
|
|
||||||
// misc settings
|
// misc settings
|
||||||
@@ -362,6 +367,15 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
GRAPHICS_FORCE_SINGLE_ADAPTER = false;
|
GRAPHICS_FORCE_SINGLE_ADAPTER = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options[launcher::Options::FullscreenResolution].is_active()) {
|
||||||
|
std::pair<uint32_t, uint32_t> result;
|
||||||
|
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
||||||
|
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
||||||
|
} else {
|
||||||
|
log_warning("launcher", "failed to parse -forceres");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::spice2x_NvapiProfile].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
|
if (options[launcher::Options::spice2x_NvapiProfile].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
nvapi::ADD_PROFILE = true;
|
nvapi::ADD_PROFILE = true;
|
||||||
}
|
}
|
||||||
@@ -600,6 +614,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::LoadQKSModule].value_bool()) {
|
if (options[launcher::Options::LoadQKSModule].value_bool()) {
|
||||||
attach_qks = true;
|
attach_qks = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::LoadMFGModule].value_bool()) {
|
||||||
|
attach_mfg = true;
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
|
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
|
||||||
attach_museca = true;
|
attach_museca = true;
|
||||||
}
|
}
|
||||||
@@ -735,6 +752,15 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::Player2Card].is_active()) {
|
if (options[launcher::Options::Player2Card].is_active()) {
|
||||||
CARD_OVERRIDES[1] = options[launcher::Options::Player2Card].value_text();
|
CARD_OVERRIDES[1] = options[launcher::Options::Player2Card].value_text();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::Player1PinMacro].is_active()) {
|
||||||
|
PIN_MACRO_ENABLED = true;
|
||||||
|
PIN_MACRO_VALUES[0] = options[launcher::Options::Player1PinMacro].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::Player2PinMacro].is_active()) {
|
||||||
|
PIN_MACRO_ENABLED = true;
|
||||||
|
PIN_MACRO_VALUES[1] = options[launcher::Options::Player2PinMacro].value_text();
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &reader : options[launcher::Options::ICCAReaderPort].values_text()) {
|
for (auto &reader : options[launcher::Options::ICCAReaderPort].values_text()) {
|
||||||
static int reader_id = 0;
|
static int reader_id = 0;
|
||||||
if (reader_id < 2) {
|
if (reader_id < 2) {
|
||||||
@@ -936,12 +962,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
GRAPHICS_WINDOW_STYLE = options[launcher::Options::spice2x_WindowBorder].value_uint32();
|
GRAPHICS_WINDOW_STYLE = options[launcher::Options::spice2x_WindowBorder].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::spice2x_WindowSize].is_active()) {
|
if (options[launcher::Options::spice2x_WindowSize].is_active()) {
|
||||||
auto s = options[launcher::Options::spice2x_WindowSize].value_text();
|
std::pair<uint32_t, uint32_t> result;
|
||||||
uint32_t w, h;
|
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
||||||
const auto remove_spaces = [](const char& c) { return c == ' '; };
|
GRAPHICS_WINDOW_SIZE = result;
|
||||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
|
||||||
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
|
|
||||||
GRAPHICS_WINDOW_SIZE = std::pair(w, h);
|
|
||||||
} else {
|
} else {
|
||||||
log_warning("launcher", "failed to parse -windowsize");
|
log_warning("launcher", "failed to parse -windowsize");
|
||||||
}
|
}
|
||||||
@@ -1017,6 +1040,15 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::QKSArgs].is_active()) {
|
if (options[launcher::Options::QKSArgs].is_active()) {
|
||||||
games::qks::QKS_INJECT_ARGS = options[launcher::Options::QKSArgs].value_text();
|
games::qks::QKS_INJECT_ARGS = options[launcher::Options::QKSArgs].value_text();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::MFGArgs].is_active()) {
|
||||||
|
games::mfg::MFG_INJECT_ARGS = options[launcher::Options::MFGArgs].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::MFGCabType].is_active()) {
|
||||||
|
games::mfg::MFG_CABINET_TYPE = options[launcher::Options::MFGCabType].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::MFGNoIO].is_active()) {
|
||||||
|
games::mfg::MFG_NO_IO = options[launcher::Options::MFGNoIO].value_bool();
|
||||||
|
}
|
||||||
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
|
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
|
||||||
rawinput::ENABLE_SMX_STAGE = true;
|
rawinput::ENABLE_SMX_STAGE = true;
|
||||||
}
|
}
|
||||||
@@ -1578,6 +1610,19 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mahjong Fight Girl
|
||||||
|
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/MFGClient_Data")) {
|
||||||
|
avs::game::DLL_NAME = "kamunity.dll";
|
||||||
|
attach_io = true;
|
||||||
|
attach_mfg = true;
|
||||||
|
launcher::signal::USE_VEH_WORKAROUND = true;
|
||||||
|
// automatically show cursor when no touchscreen is available
|
||||||
|
if (!is_touch_available()) {
|
||||||
|
GRAPHICS_SHOW_CURSOR = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Busou Shinki: Armored Princess Battle Conductor
|
// Busou Shinki: Armored Princess Battle Conductor
|
||||||
if (check_dll("kamunity.dll") && fileutils::file_exists("game/bsac_app.exe")) {
|
if (check_dll("kamunity.dll") && fileutils::file_exists("game/bsac_app.exe")) {
|
||||||
avs::game::DLL_NAME = "kamunity.dll";
|
avs::game::DLL_NAME = "kamunity.dll";
|
||||||
@@ -1729,6 +1774,10 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
avs::core::set_default_heap_size("kamunity.dll");
|
avs::core::set_default_heap_size("kamunity.dll");
|
||||||
games.push_back(new games::qks::QKSGame());
|
games.push_back(new games::qks::QKSGame());
|
||||||
}
|
}
|
||||||
|
if (attach_mfg) {
|
||||||
|
avs::core::set_default_heap_size("kamunity.dll");
|
||||||
|
games.push_back(new games::mfg::MFGGame());
|
||||||
|
}
|
||||||
|
|
||||||
// apply user heap size, if defined
|
// apply user heap size, if defined
|
||||||
if (user_heap_size > 0) {
|
if (user_heap_size > 0) {
|
||||||
@@ -1923,6 +1972,8 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
|
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_msvcrt_args(argc, argv);
|
||||||
|
|
||||||
// load hooks
|
// load hooks
|
||||||
for (auto &hook : game_hooks) {
|
for (auto &hook : game_hooks) {
|
||||||
log_info("launcher", "loading hook DLL {}", hook);
|
log_info("launcher", "loading hook DLL {}", hook);
|
||||||
@@ -1964,6 +2015,11 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
// start coin input thread
|
// start coin input thread
|
||||||
eamuse_coin_start_thread();
|
eamuse_coin_start_thread();
|
||||||
|
|
||||||
|
// pin macro
|
||||||
|
if (!cfg::CONFIGURATOR_STANDALONE && PIN_MACRO_ENABLED) {
|
||||||
|
eamuse_pin_macro_start_thread();
|
||||||
|
}
|
||||||
|
|
||||||
// print PEB
|
// print PEB
|
||||||
if (peb_print) {
|
if (peb_print) {
|
||||||
peb::peb_print();
|
peb::peb_print();
|
||||||
@@ -2048,6 +2104,8 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
// stop coin input thread
|
// stop coin input thread
|
||||||
eamuse_coin_stop_thread();
|
eamuse_coin_stop_thread();
|
||||||
|
|
||||||
|
eamuse_pin_macro_stop_thread();
|
||||||
|
|
||||||
// BT5API
|
// BT5API
|
||||||
if (BT5API_ENABLED) {
|
if (BT5API_ENABLED) {
|
||||||
bt5api_dispose();
|
bt5api_dispose();
|
||||||
@@ -2101,6 +2159,54 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/spice2x/spice2x.github.io/issues/264
|
||||||
|
// huge ugly hack to work around things that broke when MinGW switched from msvcrt to ucrt
|
||||||
|
// this is done to ensure that any DLL hooks that rely on msvcrt continue to work
|
||||||
|
void update_msvcrt_args(int argc, char *argv[]) {
|
||||||
|
#if defined(_UCRT)
|
||||||
|
auto msvc = LoadLibraryA("msvcrt.dll");
|
||||||
|
if (!msvc) {
|
||||||
|
log_warning("launcher", "failed to load msvcrt.dll");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get __argc
|
||||||
|
PINT32 argc_addr = (PINT32)GetProcAddress(msvc, "__argc");
|
||||||
|
if (!argc_addr) {
|
||||||
|
log_warning("launcher", "failed to find msvcrt!__argc");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (*argc_addr == argc) {
|
||||||
|
log_warning("launcher", "msvcrt!__argc is already set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
log_warning("launcher", "exception while reading msvcrt!_argc: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
// get __argv
|
||||||
|
PCHAR **argv_addr = (PCHAR **)GetProcAddress(msvc, "__argv");
|
||||||
|
if (!argv_addr) {
|
||||||
|
log_warning("launcher", "failed to find msvcrt!__argv");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update them
|
||||||
|
try {
|
||||||
|
log_info("launcher", "msvcrt!__argc value before: {}", *argc_addr);
|
||||||
|
*argc_addr = argc;
|
||||||
|
log_info("launcher", "msvcrt!__argc value after: {}", *argc_addr);
|
||||||
|
*argv_addr = argv;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
log_warning("launcher", "exception while messing with msvcrt!_argc and _argv: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
log_misc("launcher", "not UCRT, skipping msvcrt!_argc / _argv hacks");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
return main_implementation(argc, argv);
|
return main_implementation(argc, argv);
|
||||||
|
|||||||
@@ -113,6 +113,26 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Network",
|
.category = "Network",
|
||||||
.sensitive = true,
|
.sensitive = true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Player1PinMacro
|
||||||
|
.title = "Player 1 PIN Macro",
|
||||||
|
.name = "pinmacro0",
|
||||||
|
.desc = "Set a PIN for Player 1 that will cause the PIN to be automatically typed when Player 1 PIN Macro overlay key is pressed",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "1234",
|
||||||
|
.category = "Network (Advanced)",
|
||||||
|
.sensitive = true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Player2PinMacro
|
||||||
|
.title = "Player 2 PIN Macro",
|
||||||
|
.name = "pinmacro1",
|
||||||
|
.desc = "Set a PIN for Player 2 that will cause the PIN to be automatically typed when Player 2 PIN Macro overlay key is pressed",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "5678",
|
||||||
|
.category = "Network (Advanced)",
|
||||||
|
.sensitive = true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "Windowed Mode",
|
.title = "Windowed Mode",
|
||||||
.name = "w",
|
.name = "w",
|
||||||
@@ -169,6 +189,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Integer,
|
.type = OptionType::Integer,
|
||||||
.category = "Graphics (Common)",
|
.category = "Graphics (Common)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.title = "Only Use One Monitor",
|
||||||
|
.name = "graphics-force-single-adapter",
|
||||||
|
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
|
||||||
|
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.category = "Graphics (Common)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "Force Refresh Rate",
|
.title = "Force Refresh Rate",
|
||||||
.name = "graphics-force-refresh",
|
.name = "graphics-force-refresh",
|
||||||
@@ -177,12 +205,16 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Graphics (Common)",
|
.category = "Graphics (Common)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Only Use One Monitor",
|
// FullscreenResolution
|
||||||
.name = "graphics-force-single-adapter",
|
.title = "Force Full Screen Resolution",
|
||||||
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
|
.name = "forceres",
|
||||||
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
|
.desc =
|
||||||
.type = OptionType::Bool,
|
"For full screen mode, forcibly set a custom resolution.\n\n"
|
||||||
.category = "Graphics (Common)",
|
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
|
||||||
|
"This should only be used as last resort if your GPU/monitor can't display the resolution required by the game",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "1280,720",
|
||||||
|
.category = "Graphics (Common)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Graphics9On12
|
// Graphics9On12
|
||||||
@@ -986,6 +1018,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.game_name = "QuizKnock STADIUM",
|
.game_name = "QuizKnock STADIUM",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// LoadMFGModule
|
||||||
|
.title = "Force Load Mahjong Fight Girl",
|
||||||
|
.name = "mfg",
|
||||||
|
.desc = "manually enable Mahjong Fight Girl module",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options (Advanced)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// LoadMusecaModule
|
// LoadMusecaModule
|
||||||
.title = "Force Load Museca",
|
.title = "Force Load Museca",
|
||||||
@@ -1651,6 +1692,39 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.game_name = "Chase Chase Jokers",
|
.game_name = "Chase Chase Jokers",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.title = "MFG Arguments Override",
|
||||||
|
.name = "mfgargs",
|
||||||
|
.desc = "Command line arguments passed to the game.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "",
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options (Advanced)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "MFG Cabinet Type",
|
||||||
|
.name = "mfgcabtype",
|
||||||
|
.desc = "MFG Cabinet Type. Default is HG.",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.setting_name = "",
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options",
|
||||||
|
.elements = {
|
||||||
|
{"HG", "HG"},
|
||||||
|
{"B", "B"},
|
||||||
|
{"C", "C"},
|
||||||
|
{"UKS", "UKS"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "MFG Disable IO Emulation",
|
||||||
|
.name = "mfgnoio",
|
||||||
|
.desc = "Disables BI2X hooks for MFG",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.setting_name = "",
|
||||||
|
.game_name = "Mahjong Fight Girl",
|
||||||
|
.category = "Game Options (Advanced)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// spice2x_LightsOverallBrightness
|
// spice2x_LightsOverallBrightness
|
||||||
.title = "Lights Brightness Adjustment",
|
.title = "Lights Brightness Adjustment",
|
||||||
@@ -1721,7 +1795,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.title = "Window Forced Render Scaling",
|
.title = "Window Forced Render Scaling",
|
||||||
.name = "windowscale",
|
.name = "windowscale",
|
||||||
.desc = "For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
|
.desc = "For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
|
||||||
"Works great on some games, but completely broken on others.",
|
"Reduces pixelated scaling artifacts. Works great for some games, but can COMPLETELY BREAK other games - YMMV!",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Graphics (Windowed)",
|
.category = "Graphics (Windowed)",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace launcher {
|
|||||||
PCBID,
|
PCBID,
|
||||||
Player1Card,
|
Player1Card,
|
||||||
Player2Card,
|
Player2Card,
|
||||||
|
Player1PinMacro,
|
||||||
|
Player2PinMacro,
|
||||||
WindowedMode,
|
WindowedMode,
|
||||||
InjectHook,
|
InjectHook,
|
||||||
EarlyInjectHook,
|
EarlyInjectHook,
|
||||||
@@ -25,8 +27,9 @@ namespace launcher {
|
|||||||
CaptureCursor,
|
CaptureCursor,
|
||||||
ShowCursor,
|
ShowCursor,
|
||||||
DisplayAdapter,
|
DisplayAdapter,
|
||||||
GraphicsForceRefresh,
|
|
||||||
GraphicsForceSingleAdapter,
|
GraphicsForceSingleAdapter,
|
||||||
|
GraphicsForceRefresh,
|
||||||
|
FullscreenResolution,
|
||||||
Graphics9On12,
|
Graphics9On12,
|
||||||
spice2x_Dx9On12,
|
spice2x_Dx9On12,
|
||||||
NoLegacy,
|
NoLegacy,
|
||||||
@@ -112,6 +115,7 @@ namespace launcher {
|
|||||||
LoadBusouShinkiModule,
|
LoadBusouShinkiModule,
|
||||||
LoadCCJModule,
|
LoadCCJModule,
|
||||||
LoadQKSModule,
|
LoadQKSModule,
|
||||||
|
LoadMFGModule,
|
||||||
LoadMusecaModule,
|
LoadMusecaModule,
|
||||||
PathToModules,
|
PathToModules,
|
||||||
ScreenshotFolder,
|
ScreenshotFolder,
|
||||||
@@ -191,6 +195,9 @@ namespace launcher {
|
|||||||
CCJMouseTrackball,
|
CCJMouseTrackball,
|
||||||
CCJMouseTrackballWithToggle,
|
CCJMouseTrackballWithToggle,
|
||||||
CCJTrackballSensitivity,
|
CCJTrackballSensitivity,
|
||||||
|
MFGArgs,
|
||||||
|
MFGCabType,
|
||||||
|
MFGNoIO,
|
||||||
spice2x_LightsOverallBrightness,
|
spice2x_LightsOverallBrightness,
|
||||||
spice2x_WindowBorder,
|
spice2x_WindowBorder,
|
||||||
spice2x_WindowSize,
|
spice2x_WindowSize,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace launcher::signal {
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
bool DISABLE = false;
|
bool DISABLE = false;
|
||||||
|
bool USE_VEH_WORKAROUND = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define V(variant) case variant: return #variant
|
#define V(variant) case variant: return #variant
|
||||||
@@ -167,7 +168,7 @@ static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
|||||||
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||||||
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
||||||
|
|
||||||
return nullptr;
|
return launcher::signal::USE_VEH_WORKAROUND ? INVALID_HANDLE_VALUE : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void launcher::signal::attach() {
|
void launcher::signal::attach() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace launcher::signal {
|
|||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern bool DISABLE;
|
extern bool DISABLE;
|
||||||
|
extern bool USE_VEH_WORKAROUND;
|
||||||
|
|
||||||
//void print_stacktrace();
|
//void print_stacktrace();
|
||||||
void attach();
|
void attach();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "overlay/overlay.h"
|
||||||
|
|
||||||
#include "bt5api.h"
|
#include "bt5api.h"
|
||||||
|
|
||||||
@@ -39,6 +40,16 @@ static std::optional<double> AUTO_INSERT_CARD_FIRST_CONSUME_TIME;
|
|||||||
static bool AUTO_INSERT_CARD_CACHED[2];
|
static bool AUTO_INSERT_CARD_CACHED[2];
|
||||||
static uint8_t AUTO_INSERT_CARD_CACHED_DATA[2][8];
|
static uint8_t AUTO_INSERT_CARD_CACHED_DATA[2][8];
|
||||||
|
|
||||||
|
// pin macro
|
||||||
|
bool PIN_MACRO_ENABLED = false;
|
||||||
|
std::string PIN_MACRO_VALUES[2] = {"", ""};
|
||||||
|
static std::thread *PIN_MACRO_THREAD = nullptr;
|
||||||
|
static bool PIN_MACRO_THREAD_ACTIVE = false;
|
||||||
|
static uint16_t PIN_MACRO_TRIGGER_KEYS[2] = {
|
||||||
|
games::OverlayButtons::TriggerPinMacroP1,
|
||||||
|
games::OverlayButtons::TriggerPinMacroP2
|
||||||
|
};
|
||||||
|
|
||||||
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card) {
|
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card) {
|
||||||
|
|
||||||
// get unit index
|
// get unit index
|
||||||
@@ -295,6 +306,93 @@ void eamuse_coin_stop_thread() {
|
|||||||
COIN_INPUT_THREAD = nullptr;
|
COIN_INPUT_THREAD = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void eamuse_pin_macro_start_thread() {
|
||||||
|
|
||||||
|
// set active
|
||||||
|
PIN_MACRO_THREAD_ACTIVE = true;
|
||||||
|
|
||||||
|
// create thread
|
||||||
|
PIN_MACRO_THREAD = new std::thread([]() {
|
||||||
|
uint16_t keypad_overrides[] = {
|
||||||
|
1 << EAM_IO_KEYPAD_0,
|
||||||
|
1 << EAM_IO_KEYPAD_1,
|
||||||
|
1 << EAM_IO_KEYPAD_2,
|
||||||
|
1 << EAM_IO_KEYPAD_3,
|
||||||
|
1 << EAM_IO_KEYPAD_4,
|
||||||
|
1 << EAM_IO_KEYPAD_5,
|
||||||
|
1 << EAM_IO_KEYPAD_6,
|
||||||
|
1 << EAM_IO_KEYPAD_7,
|
||||||
|
1 << EAM_IO_KEYPAD_8,
|
||||||
|
1 << EAM_IO_KEYPAD_9,
|
||||||
|
};
|
||||||
|
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||||
|
size_t pin_index[2] = {PIN_MACRO_VALUES[0].length(), PIN_MACRO_VALUES[1].length()};
|
||||||
|
|
||||||
|
std::optional<uint8_t> active_unit = std::nullopt;
|
||||||
|
|
||||||
|
while (PIN_MACRO_THREAD_ACTIVE) {
|
||||||
|
// wait for key press
|
||||||
|
if (!active_unit.has_value()) {
|
||||||
|
for (int unit = 0; unit < 2; unit++) {
|
||||||
|
if (PIN_MACRO_VALUES[unit].empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (overlay_buttons &&
|
||||||
|
(!overlay::OVERLAY || overlay::OVERLAY->hotkeys_triggered()) &&
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(PIN_MACRO_TRIGGER_KEYS[unit]))) {
|
||||||
|
active_unit = unit;
|
||||||
|
// Reset key index
|
||||||
|
pin_index[unit] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!active_unit.has_value()) {
|
||||||
|
Sleep(20);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto unit = active_unit.value();
|
||||||
|
// get character from config
|
||||||
|
if (pin_index[unit] < PIN_MACRO_VALUES[unit].length()) {
|
||||||
|
|
||||||
|
// insert character
|
||||||
|
char pin_char = PIN_MACRO_VALUES[unit].at(pin_index[unit]);
|
||||||
|
if (pin_char >= '0' && pin_char <= '9') {
|
||||||
|
int char_index = pin_char - '0';
|
||||||
|
eamuse_set_keypad_overrides(unit, keypad_overrides[char_index]);
|
||||||
|
}
|
||||||
|
pin_index[unit]++;
|
||||||
|
Sleep(100);
|
||||||
|
|
||||||
|
// clear
|
||||||
|
eamuse_set_keypad_overrides(unit, 0);
|
||||||
|
Sleep(50);
|
||||||
|
|
||||||
|
// end of PIN
|
||||||
|
if (pin_index[unit] == PIN_MACRO_VALUES[unit].length()) {
|
||||||
|
active_unit = std::nullopt;
|
||||||
|
Sleep(120);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep(200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void eamuse_pin_macro_stop_thread() {
|
||||||
|
PIN_MACRO_THREAD_ACTIVE = false;
|
||||||
|
if (PIN_MACRO_THREAD != nullptr) {
|
||||||
|
PIN_MACRO_THREAD->join();
|
||||||
|
delete PIN_MACRO_THREAD;
|
||||||
|
PIN_MACRO_THREAD = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state) {
|
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state) {
|
||||||
|
|
||||||
// check unit
|
// check unit
|
||||||
@@ -555,6 +653,8 @@ void eamuse_autodetect_game() {
|
|||||||
eamuse_set_game("Chase Chase Jokers");
|
eamuse_set_game("Chase Chase Jokers");
|
||||||
else if (avs::game::is_model("UKS"))
|
else if (avs::game::is_model("UKS"))
|
||||||
eamuse_set_game("QuizKnock STADIUM");
|
eamuse_set_game("QuizKnock STADIUM");
|
||||||
|
else if (avs::game::is_model("VFG"))
|
||||||
|
eamuse_set_game("Mahjong Fight Girl");
|
||||||
else {
|
else {
|
||||||
log_warning("eamuse", "unknown game model: {}", avs::game::MODEL);
|
log_warning("eamuse", "unknown game model: {}", avs::game::MODEL);
|
||||||
eamuse_set_game("unknown");
|
eamuse_set_game("unknown");
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ extern std::string CARD_OVERRIDES[2];
|
|||||||
extern bool AUTO_INSERT_CARD[2];
|
extern bool AUTO_INSERT_CARD[2];
|
||||||
extern float AUTO_INSERT_CARD_COOLDOWN;
|
extern float AUTO_INSERT_CARD_COOLDOWN;
|
||||||
|
|
||||||
|
extern bool PIN_MACRO_ENABLED;
|
||||||
|
extern std::string PIN_MACRO_VALUES[2];
|
||||||
|
|
||||||
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card);
|
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card);
|
||||||
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card, int unit_id);
|
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card, int unit_id);
|
||||||
bool eamuse_get_card_from_file(const std::filesystem::path &path, uint8_t *card, int index);
|
bool eamuse_get_card_from_file(const std::filesystem::path &path, uint8_t *card, int index);
|
||||||
@@ -56,6 +59,9 @@ int eamuse_coin_add();
|
|||||||
void eamuse_coin_start_thread();
|
void eamuse_coin_start_thread();
|
||||||
void eamuse_coin_stop_thread();
|
void eamuse_coin_stop_thread();
|
||||||
|
|
||||||
|
void eamuse_pin_macro_start_thread();
|
||||||
|
void eamuse_pin_macro_stop_thread();
|
||||||
|
|
||||||
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state);
|
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state);
|
||||||
void eamuse_set_keypad_overrides_bt5(size_t unit, uint16_t keypad_state);
|
void eamuse_set_keypad_overrides_bt5(size_t unit, uint16_t keypad_state);
|
||||||
void eamuse_set_keypad_overrides_reader(size_t unit, uint16_t keypad_state);
|
void eamuse_set_keypad_overrides_reader(size_t unit, uint16_t keypad_state);
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ namespace overlay::windows {
|
|||||||
this->init_pos = ImVec2(10, 10);
|
this->init_pos = ImVec2(10, 10);
|
||||||
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
||||||
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
|
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
|
||||||
|
this->toggle_scene[0] = games::OverlayButtons::ScreenResizeScene1;
|
||||||
|
this->toggle_scene[1] = games::OverlayButtons::ScreenResizeScene2;
|
||||||
|
this->toggle_scene[2] = games::OverlayButtons::ScreenResizeScene3;
|
||||||
|
this->toggle_scene[3] = games::OverlayButtons::ScreenResizeScene4;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenResize::~ScreenResize() {
|
ScreenResize::~ScreenResize() {
|
||||||
@@ -42,13 +46,17 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
void ScreenResize::reset_vars_to_default() {
|
void ScreenResize::reset_vars_to_default() {
|
||||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||||
|
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
|
||||||
cfg::SCREENRESIZE->enable_linear_filter = true;
|
cfg::SCREENRESIZE->enable_linear_filter = true;
|
||||||
cfg::SCREENRESIZE->keep_aspect_ratio = true;
|
for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) {
|
||||||
cfg::SCREENRESIZE->centered = true;
|
auto& scene = cfg::SCREENRESIZE->scene_settings[i];
|
||||||
cfg::SCREENRESIZE->offset_x = 0;
|
scene.keep_aspect_ratio = true;
|
||||||
cfg::SCREENRESIZE->offset_y = 0;
|
scene.centered = true;
|
||||||
cfg::SCREENRESIZE->scale_x = 1.f;
|
scene.offset_x = 0;
|
||||||
cfg::SCREENRESIZE->scale_y = 1.f;
|
scene.offset_y = 0;
|
||||||
|
scene.scale_x = 1.f;
|
||||||
|
scene.scale_y = 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||||
cfg::SCREENRESIZE->window_always_on_top = false;
|
cfg::SCREENRESIZE->window_always_on_top = false;
|
||||||
@@ -88,28 +96,51 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
void ScreenResize::build_fullscreen_config() {
|
void ScreenResize::build_fullscreen_config() {
|
||||||
// enable checkbox
|
// enable checkbox
|
||||||
ImGui::TextWrapped("Hint: bind a key to Screen Resize for quickly toggling this on/off.");
|
|
||||||
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker("Hint: bind a key to Screen Resize for a quick toggle.");
|
||||||
|
|
||||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize);
|
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize);
|
||||||
|
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||||
|
|
||||||
|
if (ImGui::RadioButton("Scene 1", cfg::SCREENRESIZE->screen_resize_current_scene == 0)) {
|
||||||
|
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Scene 2", cfg::SCREENRESIZE->screen_resize_current_scene == 1)) {
|
||||||
|
cfg::SCREENRESIZE->screen_resize_current_scene = 1;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Scene 3", cfg::SCREENRESIZE->screen_resize_current_scene == 2)) {
|
||||||
|
cfg::SCREENRESIZE->screen_resize_current_scene = 2;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Scene 4", cfg::SCREENRESIZE->screen_resize_current_scene == 3)) {
|
||||||
|
cfg::SCREENRESIZE->screen_resize_current_scene = 3;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker(
|
||||||
|
"Hint: bind a key to Screen Resize 1/2/3/4 for quick scene switching. "
|
||||||
|
"Scene 1 is the default scene activated when starting the game.");
|
||||||
|
|
||||||
|
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||||
|
|
||||||
// general settings
|
// general settings
|
||||||
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
ImGui::Checkbox("Centered", &scene.centered);
|
||||||
ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered);
|
if (!scene.centered) {
|
||||||
if (!cfg::SCREENRESIZE->centered) {
|
ImGui::InputInt("X Offset", &scene.offset_x);
|
||||||
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
ImGui::InputInt("Y Offset", &scene.offset_y);
|
||||||
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// aspect ratio
|
// aspect ratio
|
||||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
||||||
if (cfg::SCREENRESIZE->keep_aspect_ratio) {
|
if (scene.keep_aspect_ratio) {
|
||||||
if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) {
|
if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.65f, 2.0f)) {
|
||||||
cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x;
|
scene.scale_y = scene.scale_x;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f);
|
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.65f, 2.0f);
|
||||||
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.65f, 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
@@ -139,8 +170,16 @@ namespace overlay::windows {
|
|||||||
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||||
graphics_update_z_order(window);
|
graphics_update_z_order(window);
|
||||||
}
|
}
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker(
|
||||||
|
"For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
|
||||||
|
"Reduces pixelated scaling artifacts. Works great on some games, but completely broken on others.\n\n"
|
||||||
|
"This can't be changed in-game; instead, set -windowscale option in spicecfg and restart.");
|
||||||
|
|
||||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||||
ImGui::TextWrapped("Hint: if game is blurry after resizing, try -windowscale option in spicecfg.");
|
|
||||||
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
||||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
||||||
|
|
||||||
@@ -209,8 +248,10 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
void ScreenResize::update() {
|
void ScreenResize::update() {
|
||||||
Window::update();
|
Window::update();
|
||||||
if (this->toggle_screen_resize != ~0u) {
|
|
||||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||||
|
|
||||||
|
// toggle
|
||||||
|
if (this->toggle_screen_resize != ~0u) {
|
||||||
bool toggle_screen_resize_new = overlay_buttons
|
bool toggle_screen_resize_new = overlay_buttons
|
||||||
&& this->overlay->hotkeys_triggered()
|
&& this->overlay->hotkeys_triggered()
|
||||||
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_screen_resize));
|
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_screen_resize));
|
||||||
@@ -220,5 +261,36 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
this->toggle_screen_resize_state = toggle_screen_resize_new;
|
this->toggle_screen_resize_state = toggle_screen_resize_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scene switch
|
||||||
|
auto toggle_scene_state_new = ~0u;
|
||||||
|
for (size_t i = 0; i < std::size(this->toggle_scene); i++) {
|
||||||
|
if (this->toggle_scene[i] == ~0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool scene_switched = overlay_buttons
|
||||||
|
&& this->overlay->hotkeys_triggered()
|
||||||
|
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_scene[i]));
|
||||||
|
|
||||||
|
if (scene_switched) {
|
||||||
|
toggle_scene_state_new = (uint32_t)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only detect rising edges of key presses
|
||||||
|
if (scene_switched && (this->toggle_scene_state != i)) {
|
||||||
|
if (cfg::SCREENRESIZE->screen_resize_current_scene == (int8_t)i &&
|
||||||
|
cfg::SCREENRESIZE->enable_screen_resize) {
|
||||||
|
// this scene is already active, turn scaling off
|
||||||
|
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||||
|
} else {
|
||||||
|
// switch to scene
|
||||||
|
cfg::SCREENRESIZE->enable_screen_resize = true;
|
||||||
|
cfg::SCREENRESIZE->screen_resize_current_scene = i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remember if a key was pressed (or nothing pressed) this frame
|
||||||
|
this->toggle_scene_state = toggle_scene_state_new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ namespace overlay::windows {
|
|||||||
size_t toggle_screen_resize = ~0u;
|
size_t toggle_screen_resize = ~0u;
|
||||||
bool toggle_screen_resize_state = false;
|
bool toggle_screen_resize_state = false;
|
||||||
|
|
||||||
|
size_t toggle_scene[4] = { ~0u, ~0u, ~0u, ~0u };
|
||||||
|
uint32_t toggle_scene_state = ~0u;
|
||||||
|
|
||||||
void build_fullscreen_config();
|
void build_fullscreen_config();
|
||||||
void build_windowed_config();
|
void build_windowed_config();
|
||||||
void build_footer();
|
void build_footer();
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
#include "execexe.h"
|
||||||
|
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/libutils.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
|
||||||
|
namespace execexe {
|
||||||
|
|
||||||
|
static HMODULE execexe_module = nullptr;
|
||||||
|
static decltype(&LoadLibraryW) execexe_LoadLibraryW = nullptr;
|
||||||
|
static decltype(&GetModuleHandleW) execexe_GetModuleHandleW = nullptr;
|
||||||
|
static decltype(&GetProcAddress) execexe_GetProcAddress = nullptr;
|
||||||
|
static decltype(&CreateFileA) execexe_CreateFileA = nullptr;
|
||||||
|
static decltype(&CreateFileW) execexe_CreateFileW = nullptr;
|
||||||
|
static decltype(&CloseHandle) execexe_CloseHandle = nullptr;
|
||||||
|
|
||||||
|
static std::wstring plugins_dir;
|
||||||
|
static acioemu::ACIOHandle *acio = nullptr;
|
||||||
|
static std::wstring port_name;
|
||||||
|
static bool port_opened = false;
|
||||||
|
|
||||||
|
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 == port_name) {
|
||||||
|
if (!port_opened) {
|
||||||
|
port_opened = acio->open(port_name.c_str());
|
||||||
|
} else {
|
||||||
|
log_info("execexe", "ignored handle open. ({})", ws2s(port_name));
|
||||||
|
}
|
||||||
|
SetLastError(0);
|
||||||
|
return (HANDLE) acio;
|
||||||
|
}
|
||||||
|
return execexe_CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||||
|
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HANDLE WINAPI execexe_CreateFileW_hook(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||||
|
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||||
|
if (lpFileName == port_name && acio->open(lpFileName)) {
|
||||||
|
if (!port_opened) {
|
||||||
|
port_opened = acio->open(port_name.c_str());
|
||||||
|
} else {
|
||||||
|
log_info("execexe", "ignored handle open. ({})", ws2s(port_name));
|
||||||
|
}
|
||||||
|
SetLastError(0);
|
||||||
|
return (HANDLE) acio;
|
||||||
|
} else {
|
||||||
|
return execexe_CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||||
|
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static WINBOOL WINAPI execexe_CloseHandle_hook(HANDLE hObject) {
|
||||||
|
if (hObject == acio && port_opened) {
|
||||||
|
log_info("execexe", "ignored handle close. ({})", ws2s(port_name));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return execexe_CloseHandle(hObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE init() {
|
||||||
|
execexe_module = libutils::load_library("execexe.dll");
|
||||||
|
execexe_LoadLibraryW = libutils::get_proc<decltype(&LoadLibraryW)>(execexe_module, MAKEINTRESOURCE(34));
|
||||||
|
execexe_GetModuleHandleW = libutils::get_proc<decltype(&GetModuleHandleW)>(execexe_module, MAKEINTRESOURCE(25));
|
||||||
|
execexe_GetProcAddress = libutils::get_proc<decltype(&GetProcAddress)>(execexe_module, MAKEINTRESOURCE(27));
|
||||||
|
execexe_CloseHandle = libutils::get_proc<decltype(&CloseHandle)>(execexe_module, MAKEINTRESOURCE(7));
|
||||||
|
execexe_CreateFileA = libutils::get_proc<decltype(&CreateFileA)>(execexe_module, MAKEINTRESOURCE(9));
|
||||||
|
execexe_CreateFileW = libutils::get_proc<decltype(&CreateFileW)>(execexe_module, MAKEINTRESOURCE(11));
|
||||||
|
|
||||||
|
auto module_path = libutils::module_file_name(nullptr);
|
||||||
|
module_path = module_path.replace_extension("");
|
||||||
|
module_path = module_path.replace_filename(module_path.filename().wstring() + L"_Data");
|
||||||
|
plugins_dir = (module_path / L"Plugins" / L"x86_64").wstring() + L"\\";
|
||||||
|
|
||||||
|
return execexe_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle) {
|
||||||
|
static bool init = false;
|
||||||
|
if (init)
|
||||||
|
return;
|
||||||
|
init = true;
|
||||||
|
|
||||||
|
port_name = portName;
|
||||||
|
acio = acioHandle;
|
||||||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(7),
|
||||||
|
execexe_CloseHandle_hook, &execexe_CloseHandle);
|
||||||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
|
||||||
|
execexe_CreateFileA_hook, &execexe_CreateFileA);
|
||||||
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||||
|
execexe_CreateFileW_hook, &execexe_CreateFileW);
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE load_library(const char *module_name, bool fatal) {
|
||||||
|
std::wstring module_name_w = s2ws(module_name);
|
||||||
|
std::wstring plugin_path = plugins_dir + module_name_w;
|
||||||
|
HMODULE module = execexe_LoadLibraryW(plugin_path.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
module = execexe_LoadLibraryW(module_name_w.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
log_fatal("execexe", "failed to load library {}", module_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE get_module(const char *module_name, bool fatal) {
|
||||||
|
std::wstring module_name_w = s2ws(module_name);
|
||||||
|
std::wstring plugin_path = plugins_dir + module_name_w;
|
||||||
|
|
||||||
|
HMODULE module = execexe_GetModuleHandleW(plugin_path.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
module = execexe_GetModuleHandleW(module_name_w.c_str());
|
||||||
|
if (module != nullptr) {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
log_fatal("execexe", "failed to get module {}", module_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC get_proc(HMODULE module, const char *proc_name, bool fatal) {
|
||||||
|
FARPROC proc = execexe_GetProcAddress(module, proc_name);
|
||||||
|
if (proc != nullptr) {
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
log_fatal("execexe", "proc {} not found", proc_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trampoline(const char *dll, const char *func, void *hook, void **orig) {
|
||||||
|
HMODULE module = get_module(dll);
|
||||||
|
FARPROC proc = get_proc(module, func);
|
||||||
|
|
||||||
|
return detour::trampoline(
|
||||||
|
reinterpret_cast<void *>(proc),
|
||||||
|
hook,
|
||||||
|
orig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trampoline_try(const char *dll, const char *func, void *hook, void **orig) {
|
||||||
|
HMODULE module = get_module(dll, false);
|
||||||
|
if (module == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC proc = get_proc(module, func, false);
|
||||||
|
if (proc == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return detour::trampoline(
|
||||||
|
reinterpret_cast<void *>(proc),
|
||||||
|
hook,
|
||||||
|
orig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "acioemu/handle.h"
|
||||||
|
|
||||||
|
namespace execexe {
|
||||||
|
HMODULE init();
|
||||||
|
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle);
|
||||||
|
HMODULE load_library(const char *module_name, bool fatal = true);
|
||||||
|
FARPROC get_proc(HMODULE module, const char *proc_name, bool fatal = true);
|
||||||
|
HMODULE get_module(const char *module_name, bool fatal = true);
|
||||||
|
bool trampoline(const char *dll, const char *func, void *hook, void **orig);
|
||||||
|
bool trampoline_try(const char *dll, const char *func, void *hook, void **orig);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T get_proc(HMODULE module, const char *proc_name, bool fatal = true) {
|
||||||
|
return reinterpret_cast<T>(get_proc(module, proc_name, fatal));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool trampoline(const char *dll, const char *func, T hook, T *orig) {
|
||||||
|
return trampoline(
|
||||||
|
dll,
|
||||||
|
func,
|
||||||
|
reinterpret_cast<void *>(hook),
|
||||||
|
reinterpret_cast<void **>(orig));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool trampoline_try(const char *dll, const char *func, T hook, T *orig) {
|
||||||
|
return trampoline_try(
|
||||||
|
dll,
|
||||||
|
func,
|
||||||
|
reinterpret_cast<void *>(hook),
|
||||||
|
reinterpret_cast<void **>(orig));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "external/fmt/include/fmt/format.h"
|
#include "external/fmt/include/fmt/format.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
namespace unity_utils {
|
namespace unity_utils {
|
||||||
|
|
||||||
@@ -42,4 +44,41 @@ std::string get_unity_player_args() {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string cmdLine;
|
||||||
|
static decltype(GetCommandLineA) *GetCommandLineA_orig = nullptr;
|
||||||
|
|
||||||
|
static LPSTR WINAPI GetCommandLineA_hook() {
|
||||||
|
return (LPSTR) cmdLine.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_args(const std::string &args) {
|
||||||
|
static bool init = false;
|
||||||
|
if (!init) {
|
||||||
|
init = true;
|
||||||
|
detour::trampoline_try("kernel32.dll", "GetCommandLineA",
|
||||||
|
(void*)GetCommandLineA_hook, (void**)&GetCommandLineA_orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdLine = args;
|
||||||
|
log_info("unity", "unity player args: ```{}```", cmdLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool show = false;
|
||||||
|
static decltype(ShowCursor) *ShowCursor_orig = nullptr;
|
||||||
|
|
||||||
|
static int WINAPI ShowCursor_hook(BOOL bShow) {
|
||||||
|
return show;
|
||||||
|
}
|
||||||
|
|
||||||
|
void force_show_cursor(bool bShow) {
|
||||||
|
static bool init = false;
|
||||||
|
if (!init) {
|
||||||
|
init = true;
|
||||||
|
detour::trampoline_try("user32.dll", "ShowCursor",
|
||||||
|
(void*)ShowCursor_hook, (void**)&ShowCursor_orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
show = bShow;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,4 +4,6 @@
|
|||||||
|
|
||||||
namespace unity_utils {
|
namespace unity_utils {
|
||||||
std::string get_unity_player_args();
|
std::string get_unity_player_args();
|
||||||
|
void set_args(const std::string &args);
|
||||||
|
void force_show_cursor(bool bShow);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,3 +298,16 @@ static inline int get_async_secondary_mouse() {
|
|||||||
int vk = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON;
|
int vk = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON;
|
||||||
return GetAsyncKeyState(vk);
|
return GetAsyncKeyState(vk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool parse_width_height(const std::string wh, std::pair<uint32_t, uint32_t> &result) {
|
||||||
|
std::string s = wh;
|
||||||
|
uint32_t w, h;
|
||||||
|
const auto remove_spaces = [](const char& c) { return c == ' '; };
|
||||||
|
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
||||||
|
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
|
||||||
|
result = std::pair(w, h);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user