mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 23:00:42 -07:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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.
|
||||
|
||||
## 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/lcd.cpp
|
||||
api/modules/ddr.cpp
|
||||
api/modules/resize.cpp
|
||||
|
||||
# avs
|
||||
avs/core.cpp
|
||||
|
||||
@@ -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
|
||||
- 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
|
||||
- read()
|
||||
- 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()
|
||||
- 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
|
||||
Unless otherwise noted, all files are licensed under the GPLv3.
|
||||
See the LICENSE file for the full license text.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "modules/lights.h"
|
||||
#include "modules/memory.h"
|
||||
#include "modules/touch.h"
|
||||
#include "modules/resize.h"
|
||||
#include "request.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::Memory());
|
||||
state->modules.push_back(new modules::Touch());
|
||||
state->modules.push_back(new modules::Resize());
|
||||
}
|
||||
|
||||
void Controller::free_state(api::ClientState *state) {
|
||||
|
||||
@@ -19,11 +19,16 @@ namespace api::modules {
|
||||
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Lights::write, 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(name: str, ...)
|
||||
*/
|
||||
void Lights::read(api::Request &req, Response &res) {
|
||||
|
||||
@@ -32,17 +37,39 @@ namespace api::modules {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each light
|
||||
for (auto &light : *this->lights) {
|
||||
Value state(kArrayType);
|
||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
// all lights for this game
|
||||
if (req.params.Size() == 0) {
|
||||
// add state for each light
|
||||
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 light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +117,7 @@ namespace api::modules {
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset(name: str, ...)
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Lights::write_reset(Request &req, Response &res) {
|
||||
@@ -119,26 +147,28 @@ namespace api::modules {
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
const char* light_name = nullptr;
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
if (param.IsArray()) {
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
// get params
|
||||
light_name = param[0].GetString();
|
||||
} else if (param.IsString()) {
|
||||
light_name = param.GetString();
|
||||
} else {
|
||||
error(res, "parameters must be arrays or strings");
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto light_name = param[0].GetString();
|
||||
|
||||
// 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);
|
||||
continue;
|
||||
}
|
||||
@@ -153,21 +183,20 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
if (this->lights_by_names.contains(name)) {
|
||||
auto &light = this->lights_by_names.at(name).get();
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, state);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, state);
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Lights::write_light_reset(std::string name) {
|
||||
@@ -178,14 +207,13 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
if (this->lights_by_names.contains(name)) {
|
||||
auto &light = this->lights_by_names.at(name).get();
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
} else {
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
@@ -15,6 +17,7 @@ namespace api::modules {
|
||||
|
||||
// state
|
||||
std::vector<Light> *lights;
|
||||
robin_hood::unordered_map<std::string, std::reference_wrapper<Light>> lights_by_names;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
@@ -22,6 +25,7 @@ namespace api::modules {
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
void get_light(Light &light, Response &res);
|
||||
bool write_light(std::string name, float state);
|
||||
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 .memory import *
|
||||
from .touch import *
|
||||
from .resize import *
|
||||
@@ -2,8 +2,14 @@ from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def lights_read(con: Connection):
|
||||
res = con.request(Request("lights", "read"))
|
||||
def lights_read(con: Connection, light_names=None):
|
||||
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()
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
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):
|
||||
"""The main application frame."""
|
||||
@@ -419,6 +478,8 @@ class MainApp(ttk.Frame):
|
||||
self.tabs.add(self.tab_analogs, text="Analogs")
|
||||
self.tab_lights = LightsTab(self, self.tabs)
|
||||
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.tabs.add(self.tab_manual, text="Manual")
|
||||
self.tabs.pack(expand=True, fill=tk.BOTH)
|
||||
|
||||
@@ -76,14 +76,22 @@ namespace cfg {
|
||||
eamuse_get_game(),
|
||||
use_game_setting,
|
||||
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_linear_filter", this->enable_linear_filter);
|
||||
load_bool_value(doc, root + "keep_aspect_ratio", this->keep_aspect_ratio);
|
||||
load_bool_value(doc, root + "centered", this->centered);
|
||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||
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
|
||||
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
||||
@@ -189,14 +197,21 @@ namespace cfg {
|
||||
root);
|
||||
|
||||
// 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_linear_filter").Set(doc, this->enable_linear_filter);
|
||||
rapidjson::Pointer(root + "keep_aspect_ratio").Set(doc, this->keep_aspect_ratio);
|
||||
rapidjson::Pointer(root + "centered").Set(doc, this->centered);
|
||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||
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
|
||||
rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top);
|
||||
|
||||
@@ -14,6 +14,15 @@ namespace cfg {
|
||||
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;
|
||||
|
||||
class ScreenResize {
|
||||
@@ -31,14 +40,10 @@ namespace cfg {
|
||||
~ScreenResize();
|
||||
|
||||
// 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;
|
||||
int8_t screen_resize_current_scene = 0;
|
||||
bool enable_linear_filter = true;
|
||||
bool keep_aspect_ratio = true;
|
||||
bool centered = true;
|
||||
fullscreen_setting scene_settings[4];
|
||||
|
||||
// windowed mode sizing
|
||||
// Windows terminology:
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
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]
|
||||
Fix clipboard copy function truncating last character of string
|
||||
|
||||
|
||||
@@ -100,9 +100,25 @@ namespace games::iidx {
|
||||
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||
|
||||
log_info("iidx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||
|
||||
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
||||
const auto result = RegOpenKeyExA_orig(
|
||||
real_asio_reg_handle,
|
||||
ASIO_DRIVER.value().c_str(),
|
||||
ulOptions,
|
||||
samDesired,
|
||||
&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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -437,8 +437,20 @@ namespace games {
|
||||
vkey_defaults.push_back(VK_F12);
|
||||
names.emplace_back("Toggle Camera Control");
|
||||
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");
|
||||
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");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Navigator Activate");
|
||||
|
||||
@@ -22,7 +22,13 @@ namespace games {
|
||||
ToggleScreenResize,
|
||||
ToggleOverlay,
|
||||
ToggleCameraControl,
|
||||
TriggerPinMacroP1,
|
||||
TriggerPinMacroP2,
|
||||
ScreenResize,
|
||||
ScreenResizeScene1,
|
||||
ScreenResizeScene2,
|
||||
ScreenResizeScene3,
|
||||
ScreenResizeScene4,
|
||||
SuperExit,
|
||||
NavigatorActivate,
|
||||
NavigatorCancel,
|
||||
|
||||
@@ -79,9 +79,25 @@ namespace games::sdvx {
|
||||
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||
|
||||
log_info("sdvx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||
|
||||
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
||||
const auto result = RegOpenKeyExA_orig(
|
||||
real_asio_reg_handle,
|
||||
ASIO_DRIVER.value().c_str(),
|
||||
ulOptions,
|
||||
samDesired,
|
||||
&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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -740,9 +740,10 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
topSurface->UnlockRect();
|
||||
|
||||
// do the actual zoom / offset math
|
||||
if (cfg::SCREENRESIZE->centered) {
|
||||
targetRect.right = (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
||||
targetRect.bottom = (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||
if (scene.centered) {
|
||||
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 deltaW = ((targetRect.right - targetRect.left) - w) / 2;
|
||||
targetRect.top -= deltaH;
|
||||
@@ -750,12 +751,12 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
targetRect.left -= deltaW;
|
||||
targetRect.right -= deltaW;
|
||||
} else {
|
||||
targetRect.left -= cfg::SCREENRESIZE->offset_x;
|
||||
targetRect.top += cfg::SCREENRESIZE->offset_y;
|
||||
targetRect.right = -cfg::SCREENRESIZE->offset_x;
|
||||
targetRect.right += (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
||||
targetRect.bottom = cfg::SCREENRESIZE->offset_y;
|
||||
targetRect.bottom += (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
||||
targetRect.left -= scene.offset_x;
|
||||
targetRect.top += scene.offset_y;
|
||||
targetRect.right = -scene.offset_x;
|
||||
targetRect.right += (w + rectLeft) / scene.scale_x;
|
||||
targetRect.bottom = scene.offset_y;
|
||||
targetRect.bottom += (h + rectTop) / scene.scale_y;
|
||||
}
|
||||
|
||||
// draw to back buffer
|
||||
|
||||
@@ -143,6 +143,8 @@ static bool check_dll(const std::string &model) {
|
||||
}
|
||||
}
|
||||
|
||||
void update_msvcrt_args(int argc, char *argv[]);
|
||||
|
||||
int main_implementation(int argc, char *argv[]) {
|
||||
|
||||
// remember argv, argv
|
||||
@@ -735,6 +737,15 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::Player2Card].is_active()) {
|
||||
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()) {
|
||||
static int reader_id = 0;
|
||||
if (reader_id < 2) {
|
||||
@@ -1923,6 +1934,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
|
||||
}
|
||||
|
||||
update_msvcrt_args(argc, argv);
|
||||
|
||||
// load hooks
|
||||
for (auto &hook : game_hooks) {
|
||||
log_info("launcher", "loading hook DLL {}", hook);
|
||||
@@ -1964,6 +1977,11 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// start coin input thread
|
||||
eamuse_coin_start_thread();
|
||||
|
||||
// pin macro
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && PIN_MACRO_ENABLED) {
|
||||
eamuse_pin_macro_start_thread();
|
||||
}
|
||||
|
||||
// print PEB
|
||||
if (peb_print) {
|
||||
peb::peb_print();
|
||||
@@ -2048,6 +2066,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// stop coin input thread
|
||||
eamuse_coin_stop_thread();
|
||||
|
||||
eamuse_pin_macro_stop_thread();
|
||||
|
||||
// BT5API
|
||||
if (BT5API_ENABLED) {
|
||||
bt5api_dispose();
|
||||
@@ -2101,6 +2121,54 @@ int main_implementation(int argc, char *argv[]) {
|
||||
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
|
||||
int main(int argc, char *argv[]) {
|
||||
return main_implementation(argc, argv);
|
||||
|
||||
@@ -113,6 +113,26 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Network",
|
||||
.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",
|
||||
.name = "w",
|
||||
@@ -1721,7 +1741,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.title = "Window Forced Render Scaling",
|
||||
.name = "windowscale",
|
||||
.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 on some games, but completely broken on others",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Graphics (Windowed)",
|
||||
},
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace launcher {
|
||||
PCBID,
|
||||
Player1Card,
|
||||
Player2Card,
|
||||
Player1PinMacro,
|
||||
Player2PinMacro,
|
||||
WindowedMode,
|
||||
InjectHook,
|
||||
EarlyInjectHook,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "overlay/overlay.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 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) {
|
||||
|
||||
// get unit index
|
||||
@@ -295,6 +306,93 @@ void eamuse_coin_stop_thread() {
|
||||
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) {
|
||||
|
||||
// check unit
|
||||
|
||||
@@ -34,6 +34,9 @@ extern std::string CARD_OVERRIDES[2];
|
||||
extern bool AUTO_INSERT_CARD[2];
|
||||
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(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);
|
||||
@@ -56,6 +59,9 @@ int eamuse_coin_add();
|
||||
void eamuse_coin_start_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_bt5(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->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
||||
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() {
|
||||
@@ -42,13 +46,17 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::reset_vars_to_default() {
|
||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
|
||||
cfg::SCREENRESIZE->enable_linear_filter = true;
|
||||
cfg::SCREENRESIZE->keep_aspect_ratio = true;
|
||||
cfg::SCREENRESIZE->centered = true;
|
||||
cfg::SCREENRESIZE->offset_x = 0;
|
||||
cfg::SCREENRESIZE->offset_y = 0;
|
||||
cfg::SCREENRESIZE->scale_x = 1.f;
|
||||
cfg::SCREENRESIZE->scale_y = 1.f;
|
||||
for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) {
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[i];
|
||||
scene.keep_aspect_ratio = true;
|
||||
scene.centered = true;
|
||||
scene.offset_x = 0;
|
||||
scene.offset_y = 0;
|
||||
scene.scale_x = 1.f;
|
||||
scene.scale_y = 1.f;
|
||||
}
|
||||
|
||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||
cfg::SCREENRESIZE->window_always_on_top = false;
|
||||
@@ -88,28 +96,51 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::build_fullscreen_config() {
|
||||
// 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::SameLine();
|
||||
ImGui::HelpMarker("Hint: bind a key to Screen Resize for a quick toggle.");
|
||||
|
||||
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
|
||||
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||
ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered);
|
||||
if (!cfg::SCREENRESIZE->centered) {
|
||||
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
||||
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
||||
ImGui::Checkbox("Centered", &scene.centered);
|
||||
if (!scene.centered) {
|
||||
ImGui::InputInt("X Offset", &scene.offset_x);
|
||||
ImGui::InputInt("Y Offset", &scene.offset_y);
|
||||
}
|
||||
|
||||
// aspect ratio
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
||||
if (cfg::SCREENRESIZE->keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) {
|
||||
cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x;
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
||||
if (scene.keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.65f, 2.0f)) {
|
||||
scene.scale_y = scene.scale_x;
|
||||
}
|
||||
} else {
|
||||
ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.65f, 2.0f);
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
@@ -139,8 +170,16 @@ namespace overlay::windows {
|
||||
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||
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::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::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
||||
|
||||
@@ -209,8 +248,10 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::update() {
|
||||
Window::update();
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
|
||||
// toggle
|
||||
if (this->toggle_screen_resize != ~0u) {
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
bool toggle_screen_resize_new = overlay_buttons
|
||||
&& this->overlay->hotkeys_triggered()
|
||||
&& 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
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_windowed_config();
|
||||
void build_footer();
|
||||
|
||||
Reference in New Issue
Block a user