mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66094de38a | |||
| c00cff2afd | |||
| b2cc8d404c | |||
| a0a04ab62f | |||
| a3a59e689e | |||
| a6f29d6a5a | |||
| 9be618d791 | |||
| 6bf7ae9074 | |||
| e3d63c65c1 | |||
| 5d2a9fa1cf | |||
| 0078b72b03 | |||
| b1ee37a066 | |||
| ebf6382e7f | |||
| 8e23ef224a |
+1
-1
@@ -29,7 +29,7 @@ Therefore, when making code changes, please be extremely careful about containin
|
|||||||
|
|
||||||
#### Config file compatibility
|
#### Config file compatibility
|
||||||
|
|
||||||
Do not change the names of options, buttons binds, analogs, etc - since they are used as unique identifiers in config files. If they are changed, you will introduce an incompatibility with previous versions of config file.
|
Do not change the names of buttons binds, analogs, strings for command line parameters, etc - since they are used as unique identifiers in config files. If they are changed, you will introduce an incompatibility with previous versions of config file.
|
||||||
|
|
||||||
### Code quality requirements
|
### Code quality requirements
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
|||||||
float pad3_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad3));
|
float pad3_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad3));
|
||||||
float pad4_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad4));
|
float pad4_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad4));
|
||||||
// FIXME(felix): this logic seems wrong for analog handling but correct for digital inputs
|
// FIXME(felix): this logic seems wrong for analog handling but correct for digital inputs
|
||||||
if (pad1_vel > 0.f) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad1))) {
|
||||||
STATUS_BUFFER[6] = (int) (51.f * pad1_vel + 0.5f);
|
STATUS_BUFFER[6] = (int) (51.f * pad1_vel + 0.5f);
|
||||||
}
|
}
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad2))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad2))) {
|
||||||
@@ -67,10 +67,10 @@ static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
|||||||
|
|
||||||
// process analogs
|
// process analogs
|
||||||
auto &analogs = games::ftt::get_analogs();
|
auto &analogs = games::ftt::get_analogs();
|
||||||
auto pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
auto &pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
||||||
auto pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
auto &pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
||||||
auto pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
auto &pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
||||||
auto pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
auto &pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
||||||
if (pad1_analog.isSet()) {
|
if (pad1_analog.isSet()) {
|
||||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad1_analog) + 0.5f);
|
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad1_analog) + 0.5f);
|
||||||
STATUS_BUFFER[6] = MAX(STATUS_BUFFER[6], val);
|
STATUS_BUFFER[6] = MAX(STATUS_BUFFER[6], val);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#include "screen_resize.h"
|
#include "screen_resize.h"
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
#include "external/rapidjson/pointer.h"
|
#include "external/rapidjson/pointer.h"
|
||||||
#include "external/rapidjson/prettywriter.h"
|
#include "external/rapidjson/prettywriter.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/deferlog.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
@@ -78,6 +80,10 @@ namespace cfg {
|
|||||||
root);
|
root);
|
||||||
|
|
||||||
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
||||||
|
if (this->enable_screen_resize) {
|
||||||
|
log_misc("ScreenResize", "enabled by config file");
|
||||||
|
}
|
||||||
|
|
||||||
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||||
auto& scene = this->scene_settings[i];
|
auto& scene = this->scene_settings[i];
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -51,6 +52,9 @@ namespace cfg {
|
|||||||
bool enable_linear_filter = true;
|
bool enable_linear_filter = true;
|
||||||
fullscreen_setting scene_settings[4];
|
fullscreen_setting scene_settings[4];
|
||||||
|
|
||||||
|
// state
|
||||||
|
std::atomic<bool> need_surface_clean = false;
|
||||||
|
|
||||||
// windowed mode sizing
|
// windowed mode sizing
|
||||||
// Windows terminology:
|
// Windows terminology:
|
||||||
// window = rectangle including the frame
|
// window = rectangle including the frame
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "hooks/setupapihook.h"
|
#include "hooks/setupapihook.h"
|
||||||
#include "hooks/sleephook.h"
|
#include "hooks/sleephook.h"
|
||||||
#include "hooks/input/dinput8/hook.h"
|
#include "hooks/input/dinput8/hook.h"
|
||||||
|
#include "util/deferlog.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
@@ -108,18 +109,32 @@ namespace games::ddr {
|
|||||||
log_info("ddr", "`{}` returned {}", cmd, result);
|
log_info("ddr", "`{}` returned {}", cmd, result);
|
||||||
} else {
|
} else {
|
||||||
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"DDR codec registration failure",
|
||||||
|
fmt::format(" {} failed to register with error {}", filename.string(), result)});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contains_only_ascii(file.path().string())) {
|
static std::once_flag printed;
|
||||||
log_warning(
|
std::call_once(printed, [file]() {
|
||||||
"ddr",
|
if (!contains_only_ascii(file.path().string())) {
|
||||||
"BAD PATH ERROR\n\n\n"
|
log_warning(
|
||||||
"!!! !!!\n"
|
"ddr",
|
||||||
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
|
"BAD PATH ERROR\n\n"
|
||||||
"!!! this may cause the game to crash! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
|
||||||
);
|
"!!! this may cause failures when loading codecs, and !!!\n"
|
||||||
}
|
"!!! potentially leading the game to crash! !!!\n"
|
||||||
|
"!!! !!!\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"path to DDR codecs contain non-Latin characters",
|
||||||
|
" this may lead to movies not playing, white screen issue,",
|
||||||
|
" game hanging, or potentially a crash",
|
||||||
|
" move the game to a directory that does not contain non-Latin characters",
|
||||||
|
" if you encountered any issues"});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
#include "hooks/setupapihook.h"
|
#include "hooks/setupapihook.h"
|
||||||
|
|
||||||
namespace games::gitadora {
|
namespace games::gitadora {
|
||||||
@@ -18,6 +19,8 @@ namespace games::gitadora {
|
|||||||
// settings
|
// settings
|
||||||
bool TWOCHANNEL = false;
|
bool TWOCHANNEL = false;
|
||||||
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
||||||
|
bool P1_LEFTY = false;
|
||||||
|
bool P2_LEFTY = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prevent GitaDora from creating folders on F drive
|
* Prevent GitaDora from creating folders on F drive
|
||||||
@@ -220,6 +223,9 @@ namespace games::gitadora {
|
|||||||
GRAPHICS_WINDOWED && !cfg::CONFIGURATOR_STANDALONE) {
|
GRAPHICS_WINDOWED && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
overlay::UI_SCALE_PERCENT = 250;
|
overlay::UI_SCALE_PERCENT = 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for guitar wail SOCD cleaning
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace games::gitadora {
|
|||||||
// settings
|
// settings
|
||||||
extern bool TWOCHANNEL;
|
extern bool TWOCHANNEL;
|
||||||
extern std::optional<unsigned int> CAB_TYPE;
|
extern std::optional<unsigned int> CAB_TYPE;
|
||||||
|
extern bool P1_LEFTY;
|
||||||
|
extern bool P2_LEFTY;
|
||||||
|
|
||||||
class GitaDoraGame : public games::Game {
|
class GitaDoraGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
@@ -38,4 +40,12 @@ namespace games::gitadora {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_player_lefty(size_t player) {
|
||||||
|
if (player == 0) {
|
||||||
|
return P1_LEFTY;
|
||||||
|
} else if (player == 1) {
|
||||||
|
return P2_LEFTY;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,9 +107,18 @@ std::string games::gitadora::get_buttons_help() {
|
|||||||
// keep to max 100 characters wide
|
// keep to max 100 characters wide
|
||||||
return
|
return
|
||||||
"guitar:\n"
|
"guitar:\n"
|
||||||
|
"\n"
|
||||||
" < R G B Y P --- Pick ] \n"
|
" < R G B Y P --- Pick ] \n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" If you hold your guitar left-handed (frets on the right hand):\n"
|
||||||
|
" * bind the buttons here as righty and use the in-game option\n"
|
||||||
|
" to turn on LEFT mode to flip the order of buttons.\n"
|
||||||
|
" * turn on GitaDora Lefty Guitar option in Options tab so that\n"
|
||||||
|
" wailing is recognized correctly.\n"
|
||||||
|
"\n"
|
||||||
|
"\n"
|
||||||
"drums:\n"
|
"drums:\n"
|
||||||
|
"\n"
|
||||||
" LeftCymbal RightCymbal\n"
|
" LeftCymbal RightCymbal\n"
|
||||||
" HiHat HiTom LowTom \n"
|
" HiHat HiTom LowTom \n"
|
||||||
" Snare FloorTom \n"
|
" Snare FloorTom \n"
|
||||||
@@ -117,10 +126,30 @@ std::string games::gitadora::get_buttons_help() {
|
|||||||
" Left Bass\n"
|
" Left Bass\n"
|
||||||
" Pedal Pedal\n"
|
" Pedal Pedal\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Drums are NOT velocity-sensitive!\n"
|
" Drums are NOT velocity-sensitive!\n"
|
||||||
"\n"
|
"\n"
|
||||||
"For MIDI drums with Open/Closed HiHat configurations, bind variations below. "
|
" For MIDI drums with Open/Closed HiHat configurations or pads with\n"
|
||||||
"v2_drum algorithm might work better for those drums."
|
" multiple hit zones, ensure you bind all variation using the Pages\n"
|
||||||
|
" button at the bottom."
|
||||||
|
;
|
||||||
|
}
|
||||||
|
std::string games::gitadora::get_analogs_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return
|
||||||
|
"guitar:\n"
|
||||||
|
"\n"
|
||||||
|
"X axis: 0% when body is held facing monitor, 50% when facing flat on a table\n"
|
||||||
|
"Y axis: 50% when held horizontal, 0% when neck is raised, 100% pointing down\n"
|
||||||
|
"Z axis: 50% at rest, 100% when swinging neck toward monitor (only used in XG series)\n"
|
||||||
|
"\n"
|
||||||
|
"You need both X and Y axis for up/down wail to work correctly in-game.\n"
|
||||||
|
"If you only have Y axis, consider using digital wailing instead.\n"
|
||||||
|
"\n"
|
||||||
|
"Ensure you clear all analog bindings if you want to use digital wailing.\n"
|
||||||
|
"\n"
|
||||||
|
"If you hold your guitar left-handed (frets on the right hand),\n"
|
||||||
|
"analog bindings will likely not work as intended.\n"
|
||||||
|
"Consider using digital wailing in Buttons tab."
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ namespace games::gitadora {
|
|||||||
// getters
|
// getters
|
||||||
std::vector<Button> &get_buttons();
|
std::vector<Button> &get_buttons();
|
||||||
std::string get_buttons_help();
|
std::string get_buttons_help();
|
||||||
|
std::string get_analogs_help();
|
||||||
std::vector<Analog> &get_analogs();
|
std::vector<Analog> &get_analogs();
|
||||||
std::vector<Light> &get_lights();
|
std::vector<Light> &get_lights();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/utils.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
|
#include "util/utils.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "j33i.h"
|
#include "j33i.h"
|
||||||
|
|
||||||
|
|
||||||
#define GUITAR_BTN_R 0x7
|
#define GUITAR_BTN_R 0x7
|
||||||
#define GUITAR_BTN_G 0x6
|
#define GUITAR_BTN_G 0x6
|
||||||
#define GUITAR_BTN_B 0x5
|
#define GUITAR_BTN_B 0x5
|
||||||
@@ -111,8 +112,8 @@ bool games::gitadora::J33ISerialDevice::parse_msg(
|
|||||||
|
|
||||||
auto &analogs = get_analogs();
|
auto &analogs = get_analogs();
|
||||||
|
|
||||||
// get x,y,z analog values [-0.5f, 0.5f], centered at 0.f
|
// get x,y,z analog values [-0.5f, 0.5f]
|
||||||
float x = 0.f;
|
float x = is_player_lefty(0) ? 0.3f : -0.5f;
|
||||||
auto x_analog = analogs[Analogs::GuitarP1WailX];
|
auto x_analog = analogs[Analogs::GuitarP1WailX];
|
||||||
if (x_analog.isSet()) {
|
if (x_analog.isSet()) {
|
||||||
x = GameAPI::Analogs::getState(RI_MGR, x_analog) - 0.5f;
|
x = GameAPI::Analogs::getState(RI_MGR, x_analog) - 0.5f;
|
||||||
@@ -123,11 +124,6 @@ bool games::gitadora::J33ISerialDevice::parse_msg(
|
|||||||
if (y_analog.isSet()) {
|
if (y_analog.isSet()) {
|
||||||
y = GameAPI::Analogs::getState(RI_MGR, y_analog) - 0.5f;
|
y = GameAPI::Analogs::getState(RI_MGR, y_analog) - 0.5f;
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailUp])) {
|
|
||||||
y = -0.5f;
|
|
||||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailDown])) {
|
|
||||||
y = 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float z = 0.f;
|
float z = 0.f;
|
||||||
auto z_analog = analogs[Analogs::GuitarP1WailZ];
|
auto z_analog = analogs[Analogs::GuitarP1WailZ];
|
||||||
@@ -135,6 +131,31 @@ bool games::gitadora::J33ISerialDevice::parse_msg(
|
|||||||
z = GameAPI::Analogs::getState(RI_MGR, z_analog) - 0.5f;
|
z = GameAPI::Analogs::getState(RI_MGR, z_analog) - 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle digital wail up/down
|
||||||
|
const auto wail_up = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailUp]);
|
||||||
|
const auto wail_down = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailDown]);
|
||||||
|
const auto wail_result =
|
||||||
|
socd::get_guitar_wail(0, wail_up, wail_down, get_performance_milliseconds());
|
||||||
|
if (!is_player_lefty(0)) {
|
||||||
|
// righty
|
||||||
|
if (wail_result == socd::TiltUp) {
|
||||||
|
x = 0;
|
||||||
|
y = -0.5f;
|
||||||
|
} else if (wail_result == socd::TiltDown) {
|
||||||
|
x = -0.5f;
|
||||||
|
y = 0.5f;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// lefty
|
||||||
|
if (wail_result == socd::TiltUp) {
|
||||||
|
x = 0.f;
|
||||||
|
y = 0.f;
|
||||||
|
} else if (wail_result == socd::TiltDown) {
|
||||||
|
x = 0.5f;
|
||||||
|
y = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convert x,y,z to unsigned integer values that the game expects
|
// convert x,y,z to unsigned integer values that the game expects
|
||||||
const uint16_t x_int = x * ARENA_MODEL_WAIL_MAX * 2;
|
const uint16_t x_int = x * ARENA_MODEL_WAIL_MAX * 2;
|
||||||
const uint16_t y_int = y * ARENA_MODEL_WAIL_MAX * 2;
|
const uint16_t y_int = y * ARENA_MODEL_WAIL_MAX * 2;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace games {
|
|||||||
static robin_hood::unordered_map<std::string, std::vector<Button>> buttons_keypads;
|
static robin_hood::unordered_map<std::string, std::vector<Button>> buttons_keypads;
|
||||||
static robin_hood::unordered_map<std::string, std::vector<Button>> buttons_overlay;
|
static robin_hood::unordered_map<std::string, std::vector<Button>> buttons_overlay;
|
||||||
static robin_hood::unordered_map<std::string, std::string> buttons_help;
|
static robin_hood::unordered_map<std::string, std::string> buttons_help;
|
||||||
|
static robin_hood::unordered_map<std::string, std::string> analogs_help;
|
||||||
static robin_hood::unordered_map<std::string, std::vector<Analog> &> analogs;
|
static robin_hood::unordered_map<std::string, std::vector<Analog> &> analogs;
|
||||||
static robin_hood::unordered_map<std::string, std::vector<Light> &> lights;
|
static robin_hood::unordered_map<std::string, std::vector<Light> &> lights;
|
||||||
static robin_hood::unordered_map<std::string, std::vector<Option>> options;
|
static robin_hood::unordered_map<std::string, std::vector<Option>> options;
|
||||||
@@ -107,6 +108,7 @@ namespace games {
|
|||||||
games.push_back(gitadora);
|
games.push_back(gitadora);
|
||||||
buttons.insert({ gitadora, gitadora::get_buttons() });
|
buttons.insert({ gitadora, gitadora::get_buttons() });
|
||||||
buttons_help.insert({ gitadora, gitadora::get_buttons_help() });
|
buttons_help.insert({ gitadora, gitadora::get_buttons_help() });
|
||||||
|
analogs_help.insert({ gitadora, gitadora::get_analogs_help() });
|
||||||
analogs.insert({ gitadora, gitadora::get_analogs() });
|
analogs.insert({ gitadora, gitadora::get_analogs() });
|
||||||
lights.insert({ gitadora, gitadora::get_lights() });
|
lights.insert({ gitadora, gitadora::get_lights() });
|
||||||
file_hints[gitadora].emplace_back("gdxg.dll");
|
file_hints[gitadora].emplace_back("gdxg.dll");
|
||||||
@@ -349,6 +351,15 @@ namespace games {
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_analogs_help(const std::string &game) {
|
||||||
|
initialize();
|
||||||
|
auto it = analogs_help.find(game);
|
||||||
|
if (it == analogs_help.end()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<Button> gen_buttons_keypads(const std::string &game) {
|
static std::vector<Button> gen_buttons_keypads(const std::string &game) {
|
||||||
auto buttons = GameAPI::Buttons::getButtons(game);
|
auto buttons = GameAPI::Buttons::getButtons(game);
|
||||||
std::vector<std::string> names;
|
std::vector<std::string> names;
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ namespace games {
|
|||||||
const std::vector<std::string> &get_games();
|
const std::vector<std::string> &get_games();
|
||||||
std::vector<Button> *get_buttons(const std::string &game);
|
std::vector<Button> *get_buttons(const std::string &game);
|
||||||
std::string get_buttons_help(const std::string &game);
|
std::string get_buttons_help(const std::string &game);
|
||||||
|
std::string get_analogs_help(const std::string &game);
|
||||||
std::vector<Button> *get_buttons_keypads(const std::string &game);
|
std::vector<Button> *get_buttons_keypads(const std::string &game);
|
||||||
std::vector<Button> *get_buttons_overlay(const std::string &game);
|
std::vector<Button> *get_buttons_overlay(const std::string &game);
|
||||||
std::vector<Analog> *get_analogs(const std::string &game);
|
std::vector<Analog> *get_analogs(const std::string &game);
|
||||||
|
|||||||
@@ -60,9 +60,32 @@ namespace games::sdvx {
|
|||||||
// confirmed in EG final in aioIob2Bi2xUFC_Create
|
// confirmed in EG final in aioIob2Bi2xUFC_Create
|
||||||
static_assert(sizeof(AIO_IOB2_BI2X_UFC) == 0x39D8);
|
static_assert(sizeof(AIO_IOB2_BI2X_UFC) == 0x39D8);
|
||||||
|
|
||||||
|
struct AIO_IOB2_BI2X_UFC__INPUT {
|
||||||
|
uint8_t DevIoCounter;
|
||||||
|
uint8_t bExIoAErr;
|
||||||
|
uint8_t bExIoBErr;
|
||||||
|
uint8_t bPcPowerOn;
|
||||||
|
uint8_t bPcPowerCheck;
|
||||||
|
uint8_t CoinCount;
|
||||||
|
uint8_t bTest;
|
||||||
|
uint8_t bService;
|
||||||
|
uint8_t bCoinSw;
|
||||||
|
uint8_t bCoinJam;
|
||||||
|
uint8_t bHPDetect;
|
||||||
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_UFC__DEVSTATUS {
|
struct AIO_IOB2_BI2X_UFC__DEVSTATUS {
|
||||||
// of course you could work with variables here
|
uint8_t InputCounter;
|
||||||
uint8_t buffer[0x19E];
|
uint8_t OutputCounter;
|
||||||
|
uint8_t IoResetCounter;
|
||||||
|
uint8_t TapeLedCounter;
|
||||||
|
uint8_t TapeLedRate[8];
|
||||||
|
AIO_IOB2_BI2X_UFC__INPUT Input;
|
||||||
|
uint8_t unk_1[289];
|
||||||
|
uint16_t AnalogLeft;
|
||||||
|
uint16_t AnalogRight;
|
||||||
|
uint8_t Buttons;
|
||||||
|
uint8_t unk_2[97];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -156,8 +179,8 @@ namespace games::sdvx {
|
|||||||
AIO_IOB2_BI2X_UFC__GetDeviceStatus_orig(This, status);
|
AIO_IOB2_BI2X_UFC__GetDeviceStatus_orig(This, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
status->buffer[0] = count;
|
status->InputCounter = count;
|
||||||
status->buffer[12] = count;
|
status->Input.DevIoCounter = count;
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
// get buttons
|
// get buttons
|
||||||
@@ -165,27 +188,29 @@ namespace games::sdvx {
|
|||||||
|
|
||||||
// control buttons
|
// control buttons
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]))
|
||||||
status->buffer[18] = 0x01;
|
status->Input.bTest = 0x01;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]))
|
||||||
status->buffer[19] = 0x01;
|
status->Input.bService = 0x01;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]))
|
||||||
status->buffer[20] = 0x01;
|
status->Input.bCoinSw = 0x01;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Start]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Start]))
|
||||||
status->buffer[316] |= 0x01;
|
status->Buttons |= 0x01;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_A]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_A]))
|
||||||
status->buffer[316] |= 0x02;
|
status->Buttons |= 0x02;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_B]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_B]))
|
||||||
status->buffer[316] |= 0x04;
|
status->Buttons |= 0x04;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_C]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_C]))
|
||||||
status->buffer[316] |= 0x08;
|
status->Buttons |= 0x08;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_D]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_D]))
|
||||||
status->buffer[316] |= 0x10;
|
status->Buttons |= 0x10;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FX_L]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FX_L]))
|
||||||
status->buffer[316] |= 0x20;
|
status->Buttons |= 0x20;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FX_R]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FX_R]))
|
||||||
status->buffer[316] |= 0x40;
|
status->Buttons |= 0x40;
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]))
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]))
|
||||||
status->buffer[22] = 0x01;
|
status->Input.bHPDetect = 0x01;
|
||||||
|
|
||||||
|
status->Input.CoinCount += eamuse_coin_get_stock();
|
||||||
|
|
||||||
const auto now = get_performance_milliseconds();
|
const auto now = get_performance_milliseconds();
|
||||||
|
|
||||||
@@ -220,14 +245,14 @@ namespace games::sdvx {
|
|||||||
vol_right += (uint16_t) (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::VOL_R]) * 65535);
|
vol_right += (uint16_t) (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::VOL_R]) * 65535);
|
||||||
}
|
}
|
||||||
|
|
||||||
*((uint16_t*) &status->buffer[312]) = vol_left;
|
status->AnalogLeft = vol_left;
|
||||||
*((uint16_t*) &status->buffer[314]) = vol_right;
|
status->AnalogRight = vol_right;
|
||||||
|
|
||||||
log_debug(
|
log_debug(
|
||||||
"bi2x_hook",
|
"bi2x_hook",
|
||||||
"knobs = {} {}",
|
"knobs = {} {}",
|
||||||
*((uint16_t*) &status->buffer[312]),
|
status->AnalogLeft,
|
||||||
*((uint16_t*) &status->buffer[314]));
|
status->AnalogRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __fastcall AIO_IOB2_BI2X_UFC__IoReset(AIO_IOB2_BI2X_UFC *This,
|
static void __fastcall AIO_IOB2_BI2X_UFC__IoReset(AIO_IOB2_BI2X_UFC *This,
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iid == IID_IAudioClient) {
|
// almost all games request IAudioClient
|
||||||
|
// so far we have not seen any games request IAudioClient2
|
||||||
|
// SDVX EG Final uses IID_IAudioClient3, but only when shared mode patch is on
|
||||||
|
if (iid == IID_IAudioClient || iid == IID_IAudioClient3) {
|
||||||
|
|
||||||
// prevent initialization recursion when using some ASIO backends that proxy to DirectSound, WASAPI, or WDM
|
// prevent initialization recursion when using some ASIO backends that proxy to DirectSound, WASAPI, or WDM
|
||||||
// like ASIO4All or FlexASIO
|
// like ASIO4All or FlexASIO
|
||||||
@@ -92,23 +95,18 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
|||||||
}
|
}
|
||||||
std::lock_guard initialize_guard(hooks::audio::INITIALIZE_LOCK, std::adopt_lock);
|
std::lock_guard initialize_guard(hooks::audio::INITIALIZE_LOCK, std::adopt_lock);
|
||||||
|
|
||||||
auto client = reinterpret_cast<IAudioClient *>(*ppInterface);
|
|
||||||
|
|
||||||
// release old audio client if initialized
|
// release old audio client if initialized
|
||||||
if (hooks::audio::CLIENT) {
|
if (hooks::audio::CLIENT) {
|
||||||
hooks::audio::CLIENT->Release();
|
hooks::audio::CLIENT->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
IAudioClient *client = nullptr;
|
||||||
ret = wrap_audio_client(pReal, dwClsCtx, pActivationParams, &client);
|
if (iid == IID_IAudioClient) {
|
||||||
if (FAILED(ret)) {
|
client = wrap_audio_client(reinterpret_cast<IAudioClient *>(*ppInterface));
|
||||||
return ret;
|
} else { // IID_IAudioClient3
|
||||||
|
client = wrap_audio_client3(reinterpret_cast<IAudioClient3 *>(*ppInterface));
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
client = wrap_audio_client(client);
|
|
||||||
|
|
||||||
*ppInterface = client;
|
*ppInterface = client;
|
||||||
|
|
||||||
// persist the audio client
|
// persist the audio client
|
||||||
hooks::audio::CLIENT = client;
|
hooks::audio::CLIENT = client;
|
||||||
hooks::audio::CLIENT->AddRef();
|
hooks::audio::CLIENT->AddRef();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "hooks/audio/implementations/asio.h"
|
#include "hooks/audio/implementations/asio.h"
|
||||||
#include "hooks/audio/implementations/wave_out.h"
|
#include "hooks/audio/implementations/wave_out.h"
|
||||||
//#include "util/co_task_mem_ptr.h"
|
//#include "util/co_task_mem_ptr.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "dummy_audio_client.h"
|
#include "dummy_audio_client.h"
|
||||||
@@ -40,114 +41,9 @@ static void fix_rec_format(WAVEFORMATEX *pFormat) {
|
|||||||
pFormat->nAvgBytesPerSec = pFormat->nSamplesPerSec * pFormat->nBlockAlign;
|
pFormat->nAvgBytesPerSec = pFormat->nSamplesPerSec * pFormat->nBlockAlign;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(felix): is it appropriate to automatically switch to shared mode? should we do a
|
|
||||||
// `MessageBox` to notify the user?
|
|
||||||
/*
|
|
||||||
static bool check_for_exclusive_access(IAudioClient *client) {
|
|
||||||
static bool checked_once = false;
|
|
||||||
static bool previous_check_result = false;
|
|
||||||
|
|
||||||
CoTaskMemPtr<WAVEFORMATEX> mix_format;
|
|
||||||
REFERENCE_TIME requested_duration = 0;
|
|
||||||
|
|
||||||
if (checked_once) {
|
|
||||||
return previous_check_result;
|
|
||||||
}
|
|
||||||
if (audio::BACKEND.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scope function so it has access to the local static variables
|
|
||||||
auto set_result = [](bool result) {
|
|
||||||
checked_once = true;
|
|
||||||
previous_check_result = result;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
HRESULT ret = client->GetMixFormat(mix_format.ppv());
|
|
||||||
if (FAILED(ret)) {
|
|
||||||
PRINT_FAILED_RESULT("IAudioClient::GetMixFormat", ret);
|
|
||||||
return set_result(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_info("audio::wasapi", "Mix Format:");
|
|
||||||
print_format(mix_format.data());
|
|
||||||
|
|
||||||
ret = client->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, mix_format.data(), nullptr);
|
|
||||||
if (ret == AUDCLNT_E_UNSUPPORTED_FORMAT) {
|
|
||||||
auto mix_format_ex = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.data());
|
|
||||||
|
|
||||||
log_warning("audio::wasapi", "device does not natively support the mix format, converting to PCM");
|
|
||||||
|
|
||||||
if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
|
|
||||||
IsEqualGUID(GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, mix_format_ex->SubFormat))
|
|
||||||
{
|
|
||||||
mix_format_ex->Format.wBitsPerSample = 16;
|
|
||||||
mix_format_ex->Format.nBlockAlign = mix_format_ex->Format.nChannels * (mix_format_ex->Format.wBitsPerSample / 8);
|
|
||||||
mix_format_ex->Format.nAvgBytesPerSec = mix_format_ex->Format.nSamplesPerSec * mix_format_ex->Format.nBlockAlign;
|
|
||||||
mix_format_ex->Samples.wValidBitsPerSample = 16;
|
|
||||||
mix_format_ex->SubFormat = GUID_KSDATAFORMAT_SUBTYPE_PCM;
|
|
||||||
} else if (mix_format->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
|
||||||
mix_format->wBitsPerSample = 16;
|
|
||||||
mix_format->nBlockAlign = mix_format->nChannels * (mix_format->wBitsPerSample / 8);
|
|
||||||
mix_format->nAvgBytesPerSec = mix_format->nSamplesPerSec * mix_format->nBlockAlign;
|
|
||||||
mix_format->wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
} else {
|
|
||||||
log_warning("audio::wasapi", "mix format is not a floating point format");
|
|
||||||
return set_result(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = client->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, mix_format.data(), nullptr);
|
|
||||||
if (FAILED(ret)) {
|
|
||||||
log_warning("audio::wasapi", "mix format is not supported");
|
|
||||||
return set_result(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = client->GetDevicePeriod(nullptr, &requested_duration);
|
|
||||||
if (FAILED(ret)) {
|
|
||||||
PRINT_FAILED_RESULT("IAudioClient::GetDevicePeriod", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = client->Initialize(
|
|
||||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
|
||||||
requested_duration,
|
|
||||||
requested_duration,
|
|
||||||
mix_format.data(),
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
if (ret == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED || SUCCEEDED(ret)) {
|
|
||||||
log_info("audio::wasapi", "exclusive mode is available, disabling backend");
|
|
||||||
return set_result(true);
|
|
||||||
} else {
|
|
||||||
log_warning("audio::wasapi", "exclusive mode is not available, enabling backend, hr={}", FMT_HRESULT(ret));
|
|
||||||
}
|
|
||||||
|
|
||||||
return set_result(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT wrap_audio_client(
|
|
||||||
IMMDevice *device,
|
|
||||||
DWORD cls_ctx,
|
|
||||||
PROPVARIANT *activation_params,
|
|
||||||
IAudioClient **audio_client)
|
|
||||||
{
|
|
||||||
auto exclusive_available = check_for_exclusive_access(*audio_client);
|
|
||||||
(*audio_client)->Stop();
|
|
||||||
(*audio_client)->Reset();
|
|
||||||
(*audio_client)->Release();
|
|
||||||
*audio_client = nullptr;
|
|
||||||
|
|
||||||
SAFE_CALL("IMMDevice", "Activate", device->Activate(
|
|
||||||
IID_IAudioClient,
|
|
||||||
cls_ctx,
|
|
||||||
activation_params,
|
|
||||||
reinterpret_cast<void **>(audio_client)));
|
|
||||||
*/
|
|
||||||
IAudioClient *wrap_audio_client(IAudioClient *audio_client) {
|
IAudioClient *wrap_audio_client(IAudioClient *audio_client) {
|
||||||
|
log_misc("audio::wasapi", "wrapping IAudioClient");
|
||||||
|
|
||||||
AudioBackend *backend = nullptr;
|
AudioBackend *backend = nullptr;
|
||||||
bool requires_dummy = false;
|
bool requires_dummy = false;
|
||||||
|
|
||||||
@@ -164,9 +60,6 @@ IAudioClient *wrap_audio_client(IAudioClient *audio_client) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//} else if (!exclusive_available) {
|
|
||||||
// backend = new WaveOutBackend();
|
|
||||||
//}
|
|
||||||
|
|
||||||
IAudioClient *new_client;
|
IAudioClient *new_client;
|
||||||
|
|
||||||
@@ -182,6 +75,23 @@ IAudioClient *wrap_audio_client(IAudioClient *audio_client) {
|
|||||||
|
|
||||||
return new_client;
|
return new_client;
|
||||||
}
|
}
|
||||||
|
IAudioClient3 *wrap_audio_client3(IAudioClient3 *audio_client) {
|
||||||
|
// TODO: ASIO backend for IAudioClient3, if there is a game that needs it
|
||||||
|
log_misc("audio::wasapi", "wrapping IAudioClient3");
|
||||||
|
|
||||||
|
if (hooks::audio::BACKEND.has_value()) {
|
||||||
|
log_fatal(
|
||||||
|
"audio::wasapi",
|
||||||
|
"IAudioClient3 does not currently support backends! clear -audiobackend and try again");
|
||||||
|
}
|
||||||
|
if (hooks::audio::USE_DUMMY) {
|
||||||
|
log_fatal(
|
||||||
|
"audio::wasapi",
|
||||||
|
"IAudioClient3 does not currently support dummy context, clear -audiodummy and try again");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WrappedIAudioClient(audio_client, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// IUnknown
|
// IUnknown
|
||||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::QueryInterface(REFIID riid, void **ppvObj) {
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::QueryInterface(REFIID riid, void **ppvObj) {
|
||||||
@@ -190,8 +100,8 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::QueryInterface(REFIID riid, void
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (riid == IID_WrappedIAudioClient ||
|
if (riid == IID_WrappedIAudioClient ||
|
||||||
riid == IID_IAudioClient)
|
riid == IID_IAudioClient ||
|
||||||
{
|
riid == IID_IAudioClient3) {
|
||||||
this->AddRef();
|
this->AddRef();
|
||||||
*ppvObj = this;
|
*ppvObj = this;
|
||||||
|
|
||||||
@@ -282,20 +192,6 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::Initialize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_info("audio::wasapi", "IAudioClient::Initialize success, hr={}", FMT_HRESULT(ret));
|
log_info("audio::wasapi", "IAudioClient::Initialize success, hr={}", FMT_HRESULT(ret));
|
||||||
|
|
||||||
/*
|
|
||||||
if (ShareMode == AUDCLNT_SHAREMODE_SHARED) {
|
|
||||||
IAudioClockAdjustment *clock = nullptr;
|
|
||||||
|
|
||||||
SAFE_CALL("IAudioClient", "GetService", pReal->GetService(
|
|
||||||
IID_IAudioClockAdjustment,
|
|
||||||
reinterpret_cast<void **>(&clock)));
|
|
||||||
|
|
||||||
SAFE_CALL("IAudioClockAdjustment", "SetSampleRate", clock->SetSampleRate(
|
|
||||||
static_cast<float>(pFormat->nSamplesPerSec)));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
copy_wave_format(&hooks::audio::FORMAT, pFormat);
|
copy_wave_format(&hooks::audio::FORMAT, pFormat);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -482,3 +378,93 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetService(REFIID riid, void **pp
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::IsOffloadCapable(
|
||||||
|
AUDIO_STREAM_CATEGORY Category,
|
||||||
|
BOOL *pbOffloadCapable) {
|
||||||
|
WRAP_VERBOSE;
|
||||||
|
|
||||||
|
CHECK_RESULT(pReal3->IsOffloadCapable(Category, pbOffloadCapable));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::SetClientProperties(
|
||||||
|
const AudioClientProperties *pProperties) {
|
||||||
|
|
||||||
|
WRAP_VERBOSE;
|
||||||
|
CHECK_RESULT(pReal3->SetClientProperties(pProperties));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetBufferSizeLimits(
|
||||||
|
const WAVEFORMATEX *pFormat,
|
||||||
|
BOOL bEventDriven,
|
||||||
|
REFERENCE_TIME *phnsMinBufferDuration,
|
||||||
|
REFERENCE_TIME *phnsMaxBufferDuration) {
|
||||||
|
|
||||||
|
WRAP_VERBOSE;
|
||||||
|
CHECK_RESULT(pReal3->GetBufferSizeLimits(
|
||||||
|
pFormat,
|
||||||
|
bEventDriven,
|
||||||
|
phnsMinBufferDuration,
|
||||||
|
phnsMaxBufferDuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetSharedModeEnginePeriod(
|
||||||
|
const WAVEFORMATEX *pFormat,
|
||||||
|
UINT32 *pDefaultPeriodInFrames,
|
||||||
|
UINT32 *pFundamentalPeriodInFrames,
|
||||||
|
UINT32 *pMinPeriodInFrames,
|
||||||
|
UINT32 *pMaxPeriodInFrames) {
|
||||||
|
|
||||||
|
WRAP_VERBOSE;
|
||||||
|
CHECK_RESULT(pReal3->GetSharedModeEnginePeriod(
|
||||||
|
pFormat,
|
||||||
|
pDefaultPeriodInFrames,
|
||||||
|
pFundamentalPeriodInFrames,
|
||||||
|
pMinPeriodInFrames,
|
||||||
|
pMaxPeriodInFrames));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetCurrentSharedModeEnginePeriod(
|
||||||
|
WAVEFORMATEX **ppFormat,
|
||||||
|
UINT32 *pCurrentPeriodInFrames) {
|
||||||
|
|
||||||
|
WRAP_VERBOSE;
|
||||||
|
CHECK_RESULT(pReal3->GetCurrentSharedModeEnginePeriod(
|
||||||
|
ppFormat,
|
||||||
|
pCurrentPeriodInFrames));
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::InitializeSharedAudioStream(
|
||||||
|
DWORD StreamFlags,
|
||||||
|
UINT32 PeriodInFrames,
|
||||||
|
const WAVEFORMATEX *pFormat,
|
||||||
|
LPCGUID AudioSessionGuid) {
|
||||||
|
|
||||||
|
if (!pFormat) {
|
||||||
|
return E_POINTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verbose output
|
||||||
|
log_info("audio::wasapi", "IAudioClient3::InitializeSharedAudioStream hook hit");
|
||||||
|
log_info("audio::wasapi", "... ShareMode : {}", share_mode_str(AUDCLNT_SHAREMODE_SHARED));
|
||||||
|
log_info("audio::wasapi", "... StreamFlags : {}", stream_flags_str(StreamFlags));
|
||||||
|
log_info("audio::wasapi", "... PeriodInFrames : {}", PeriodInFrames);
|
||||||
|
print_format(pFormat);
|
||||||
|
|
||||||
|
// call next
|
||||||
|
HRESULT ret = pReal3->InitializeSharedAudioStream(
|
||||||
|
StreamFlags,
|
||||||
|
PeriodInFrames,
|
||||||
|
pFormat,
|
||||||
|
AudioSessionGuid);
|
||||||
|
|
||||||
|
// check for failure
|
||||||
|
if (FAILED(ret)) {
|
||||||
|
PRINT_FAILED_RESULT("IAudioClient3", "InitializeSharedAudioStream", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("audio::wasapi", "IAudioClient3::InitializeSharedAudioStream success, hr={}", FMT_HRESULT(ret));
|
||||||
|
copy_wave_format(&hooks::audio::FORMAT, pFormat);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,9 +16,16 @@ static const GUID IID_WrappedIAudioClient = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
IAudioClient *wrap_audio_client(IAudioClient *client);
|
IAudioClient *wrap_audio_client(IAudioClient *client);
|
||||||
|
IAudioClient3 *wrap_audio_client3(IAudioClient3 *client);
|
||||||
|
|
||||||
struct WrappedIAudioClient : IAudioClient {
|
struct WrappedIAudioClient : IAudioClient3 {
|
||||||
explicit WrappedIAudioClient(IAudioClient *orig, AudioBackend *backend) : pReal(orig), backend(backend) {
|
|
||||||
|
explicit WrappedIAudioClient(IAudioClient3 *orig3, AudioBackend *backend) :
|
||||||
|
pReal(orig3), pReal3(orig3), backend(backend) {
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit WrappedIAudioClient(IAudioClient *orig, AudioBackend *backend) :
|
||||||
|
pReal(orig), pReal3(nullptr), backend(backend) {
|
||||||
}
|
}
|
||||||
|
|
||||||
WrappedIAudioClient(const WrappedIAudioClient &) = delete;
|
WrappedIAudioClient(const WrappedIAudioClient &) = delete;
|
||||||
@@ -47,9 +54,43 @@ struct WrappedIAudioClient : IAudioClient {
|
|||||||
HRESULT STDMETHODCALLTYPE GetService(REFIID riid, void **ppv) override;
|
HRESULT STDMETHODCALLTYPE GetService(REFIID riid, void **ppv) override;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
IAudioClient *const pReal;
|
#pragma region IAudioClient2
|
||||||
AudioBackend *const backend;
|
HRESULT STDMETHODCALLTYPE IsOffloadCapable(
|
||||||
|
AUDIO_STREAM_CATEGORY Category,
|
||||||
|
BOOL *pbOffloadCapable) override;
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE SetClientProperties(
|
||||||
|
const AudioClientProperties *pProperties) override;
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetBufferSizeLimits(
|
||||||
|
const WAVEFORMATEX *pFormat,
|
||||||
|
BOOL bEventDriven,
|
||||||
|
REFERENCE_TIME *phnsMinBufferDuration,
|
||||||
|
REFERENCE_TIME *phnsMaxBufferDuration) override;
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region IAudioClient3
|
||||||
|
HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod(
|
||||||
|
const WAVEFORMATEX *pFormat,
|
||||||
|
UINT32 *pDefaultPeriodInFrames,
|
||||||
|
UINT32 *pFundamentalPeriodInFrames,
|
||||||
|
UINT32 *pMinPeriodInFrames,
|
||||||
|
UINT32 *pMaxPeriodInFrames) override;
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod(
|
||||||
|
WAVEFORMATEX **ppFormat,
|
||||||
|
UINT32 *pCurrentPeriodInFrames) override;
|
||||||
|
|
||||||
|
HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream(
|
||||||
|
DWORD StreamFlags,
|
||||||
|
UINT32 PeriodInFrames,
|
||||||
|
const WAVEFORMATEX *pFormat,
|
||||||
|
LPCGUID AudioSessionGuid) override;
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
IAudioClient *const pReal;
|
||||||
|
IAudioClient3 *const pReal3;
|
||||||
|
AudioBackend *const backend;
|
||||||
bool exclusive_mode = false;
|
bool exclusive_mode = false;
|
||||||
int frame_size = 0;
|
int frame_size = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -149,5 +149,12 @@ LowLatencyAudioClient *LowLatencyAudioClient::Create(IMMDevice *device) {
|
|||||||
log_info("audio::lowlatency", "... max buffer size : {} samples ({} ms)", maxPeriod, 1000.0f * maxPeriod / pFormat->nSamplesPerSec);
|
log_info("audio::lowlatency", "... max buffer size : {} samples ({} ms)", maxPeriod, 1000.0f * maxPeriod / pFormat->nSamplesPerSec);
|
||||||
log_info("audio::lowlatency", "... default buffer size : {} samples ({} ms)", defaultPeriod, 1000.0f * defaultPeriod / pFormat->nSamplesPerSec);
|
log_info("audio::lowlatency", "... default buffer size : {} samples ({} ms)", defaultPeriod, 1000.0f * defaultPeriod / pFormat->nSamplesPerSec);
|
||||||
log_info("audio::lowlatency", "... Windows will use minimum buffer size (instead of default) for shared mode audio clients from now on");
|
log_info("audio::lowlatency", "... Windows will use minimum buffer size (instead of default) for shared mode audio clients from now on");
|
||||||
|
if (minPeriod < defaultPeriod) {
|
||||||
|
log_info("audio::lowlatency", "minimum period is less than default period, you will see latency improvement");
|
||||||
|
} else {
|
||||||
|
log_warning("audio::lowlatency", "minimum period is not less than default period, you will see NO latency improvement");
|
||||||
|
log_warning("audio::lowlatency", "this an audio driver / hardware limitation");
|
||||||
|
}
|
||||||
|
|
||||||
return new LowLatencyAudioClient(audioClient);
|
return new LowLatencyAudioClient(audioClient);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -812,6 +812,14 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
|||||||
"either use -graphics-force-refresh option or change the desktop resolution beforehand.");
|
"either use -graphics-force-refresh option or change the desktop resolution beforehand.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED && num_adapters >= 2 && GRAPHICS_FORCE_REFRESH_SUB.has_value()) {
|
||||||
|
log_info("graphics::d3d9", "force sub refresh rate: {} => {} Hz (-graphics-force-refresh-sub option)",
|
||||||
|
pPresentationParameters[1].FullScreen_RefreshRateInHz,
|
||||||
|
GRAPHICS_FORCE_REFRESH_SUB.value());
|
||||||
|
|
||||||
|
pPresentationParameters[1].FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH_SUB.value();
|
||||||
|
}
|
||||||
|
|
||||||
// force single adapter
|
// force single adapter
|
||||||
if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||||
log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode");
|
log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode");
|
||||||
@@ -1031,6 +1039,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED && num_adapters >= 2 && GRAPHICS_FORCE_REFRESH_SUB.has_value()) {
|
||||||
|
log_info("graphics::d3d9", "force sub refresh rate: {} => {} Hz (-graphics-force-refresh-sub option)",
|
||||||
|
pPresentationParameters[1].FullScreen_RefreshRateInHz,
|
||||||
|
GRAPHICS_FORCE_REFRESH_SUB.value());
|
||||||
|
|
||||||
|
pPresentationParameters[1].FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH_SUB.value();
|
||||||
|
if (pFullscreenDisplayMode) {
|
||||||
|
pFullscreenDisplayMode[1].RefreshRate = GRAPHICS_FORCE_REFRESH_SUB.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// force single adapter
|
// force single adapter
|
||||||
if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||||
log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode");
|
log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode");
|
||||||
|
|||||||
@@ -742,14 +742,12 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
|||||||
const int w = param.BackBufferWidth;
|
const int w = param.BackBufferWidth;
|
||||||
const int h = param.BackBufferHeight;
|
const int h = param.BackBufferHeight;
|
||||||
|
|
||||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
// this code used to clear the surface using ColorFill on every call, but
|
||||||
RECT rect {
|
// this turned out to be very expensive, leading to major frame drops in
|
||||||
0,
|
// SDVX Live2D scenario
|
||||||
0,
|
if (cfg::SCREENRESIZE->need_surface_clean) {
|
||||||
(LONG)topSurface_width,
|
pReal->ColorFill(topSurface, nullptr, D3DCOLOR_XRGB(0, 0, 0));
|
||||||
(LONG)topSurface_height,
|
cfg::SCREENRESIZE->need_surface_clean = false;
|
||||||
};
|
|
||||||
pReal->ColorFill(topSurface, &rect, D3DCOLOR_XRGB(0, 0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
D3DLOCKED_RECT rect;
|
D3DLOCKED_RECT rect;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ bool GRAPHICS_WINDOWED = false;
|
|||||||
std::vector<HWND> GRAPHICS_WINDOWS;
|
std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
std::optional<HWND> GRAPHICS_WINDOW_MAIN;
|
std::optional<HWND> GRAPHICS_WINDOW_MAIN;
|
||||||
UINT GRAPHICS_FORCE_REFRESH = 0;
|
UINT GRAPHICS_FORCE_REFRESH = 0;
|
||||||
|
std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
||||||
std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
||||||
bool GRAPHICS_FORCE_SINGLE_ADAPTER = false;
|
bool GRAPHICS_FORCE_SINGLE_ADAPTER = false;
|
||||||
bool GRAPHICS_PREVENT_SECONDARY_WINDOW = false;
|
bool GRAPHICS_PREVENT_SECONDARY_WINDOW = false;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
|||||||
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
extern std::optional<HWND> GRAPHICS_WINDOW_MAIN;
|
extern std::optional<HWND> GRAPHICS_WINDOW_MAIN;
|
||||||
extern UINT GRAPHICS_FORCE_REFRESH;
|
extern UINT GRAPHICS_FORCE_REFRESH;
|
||||||
|
extern std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
||||||
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;
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/peb.h"
|
#include "util/peb.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
#include "util/sysutils.h"
|
#include "util/sysutils.h"
|
||||||
#include "util/tapeled.h"
|
#include "util/tapeled.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
@@ -326,6 +327,10 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::GraphicsForceRefresh].is_active()) {
|
if (options[launcher::Options::GraphicsForceRefresh].is_active()) {
|
||||||
GRAPHICS_FORCE_REFRESH = options[launcher::Options::GraphicsForceRefresh].value_uint32();
|
GRAPHICS_FORCE_REFRESH = options[launcher::Options::GraphicsForceRefresh].value_uint32();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::FullscreenSubRefreshRate].is_active()) {
|
||||||
|
GRAPHICS_FORCE_REFRESH_SUB =
|
||||||
|
options[launcher::Options::FullscreenSubRefreshRate].value_uint32();
|
||||||
|
}
|
||||||
if (options[launcher::Options::GraphicsForceSingleAdapter].value_bool()) {
|
if (options[launcher::Options::GraphicsForceSingleAdapter].value_bool()) {
|
||||||
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
}
|
}
|
||||||
@@ -584,6 +589,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
|
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
|
||||||
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32();
|
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::GitaDoraWailHold].is_active()) {
|
||||||
|
socd::TILT_HOLD_MS = options[launcher::Options::GitaDoraWailHold].value_uint32();
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
|
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
|
||||||
attach_nostalgia = true;
|
attach_nostalgia = true;
|
||||||
}
|
}
|
||||||
@@ -605,6 +613,17 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::GitaDoraTwoChannelAudio].value_bool()) {
|
if (options[launcher::Options::GitaDoraTwoChannelAudio].value_bool()) {
|
||||||
games::gitadora::TWOCHANNEL = true;
|
games::gitadora::TWOCHANNEL = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::GitaDoraLefty].is_active()) {
|
||||||
|
const auto text = options[launcher::Options::GitaDoraLefty].value_text();
|
||||||
|
if (text == "p1") {
|
||||||
|
games::gitadora::P1_LEFTY = true;
|
||||||
|
} else if (text == "p2") {
|
||||||
|
games::gitadora::P2_LEFTY = true;
|
||||||
|
} else if (text == "both") {
|
||||||
|
games::gitadora::P1_LEFTY = true;
|
||||||
|
games::gitadora::P2_LEFTY = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadDDRModule].value_bool()) {
|
if (options[launcher::Options::LoadDDRModule].value_bool()) {
|
||||||
attach_ddr = true;
|
attach_ddr = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,9 +221,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Graphics (Full Screen)",
|
.category = "Graphics (Full Screen)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Force Refresh Rate",
|
.title = "Force Monitor Refresh Rate",
|
||||||
.name = "graphics-force-refresh",
|
.name = "graphics-force-refresh",
|
||||||
.desc = "Force the refresh rate for the primary display adapter; works in both full screen and windowed modes",
|
.desc =
|
||||||
|
"Attempt to change the refresh rate for the primary monitor before the game boots; "
|
||||||
|
"works in both full screen and windowed modes, but known to fail for some games",
|
||||||
.type = OptionType::Integer,
|
.type = OptionType::Integer,
|
||||||
.category = "Graphics (Common)",
|
.category = "Graphics (Common)",
|
||||||
},
|
},
|
||||||
@@ -242,21 +244,34 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// FullscreenOrientationFlip
|
// FullscreenOrientationFlip
|
||||||
.title = "Full Screen Orientation Swap",
|
.title = "Full Screen Orientation Swap (EXPERIMENTAL)",
|
||||||
.name = "forceresswap",
|
.name = "forceresswap",
|
||||||
.desc =
|
.desc =
|
||||||
"Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n"
|
"Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n"
|
||||||
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
|
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
|
||||||
"Strongly consider combining this with -forceres option to render at monitor native resolution\n\n"
|
"Strongly consider combining this with -forceres option to render at monitor native resolution\n\n"
|
||||||
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out!",
|
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out, and causes performance issues "
|
||||||
|
"with Live2D!",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
|
.hidden = true,
|
||||||
|
.category = "Graphics (Full Screen)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// FullscreenSubRefreshRate
|
||||||
|
.title = "Force Submonitor Refresh Rate (EXPERIMENTAL)",
|
||||||
|
.name = "graphics-force-refresh-sub",
|
||||||
|
.desc =
|
||||||
|
"Override fullscreen refresh rate requested by the game for second monitor, "
|
||||||
|
"useful if you have a sub monitor that is not quite exactly 60Hz.\n\n"
|
||||||
|
"WARNING: experimental as we have not done extensive testing to see if this causes desyncs",
|
||||||
|
.type = OptionType::Integer,
|
||||||
.category = "Graphics (Full Screen)"
|
.category = "Graphics (Full Screen)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Graphics9On12
|
// Graphics9On12
|
||||||
.title = "DirectX 9 on 12 (DEPRECATED - use -dx9on12 instead)",
|
.title = "DirectX 9 on 12 (DEPRECATED - use -dx9on12 instead)",
|
||||||
.name = "9on12",
|
.name = "9on12",
|
||||||
.desc = "Use D3D9On12 wrapper library, requires Windows 10 Insider Preview 18956 or later. Deprecated - use -dx9on12 instead",
|
.desc = "Use D3D9On12 wrapper library, requires Windows 10. Deprecated - use -dx9on12 instead",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.hidden = true,
|
.hidden = true,
|
||||||
.category = "Graphics (Common)",
|
.category = "Graphics (Common)",
|
||||||
@@ -267,7 +282,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "sp2x-dx9on12",
|
.name = "sp2x-dx9on12",
|
||||||
.display_name = "dx9on12",
|
.display_name = "dx9on12",
|
||||||
.aliases= "dx9on12",
|
.aliases= "dx9on12",
|
||||||
.desc = "Use D3D9On12 wrapper library, requires Windows 10 Insider Preview 18956 or later. Has no effect games on that don't use DX9. Can cause some games to crash.\n\n"
|
.desc = "Use D3D9On12 wrapper library, requires Windows 10. Has no effect games on that don't use DX9. Can cause some games to crash.\n\n"
|
||||||
"Default: auto (use DX9 for most games, but turn on 9on12 for games that require it on non-NVIDIA GPUs)",
|
"Default: auto (use DX9 for most games, but turn on 9on12 for games that require it on non-NVIDIA GPUs)",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
.category = "Graphics (Common)",
|
.category = "Graphics (Common)",
|
||||||
@@ -963,6 +978,39 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
.elements = {{"1", "DX"}, {"2", "SD"}, {"3", "SD2 - white cab"}},
|
.elements = {{"1", "DX"}, {"2", "SD"}, {"3", "SD2 - white cab"}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// GitaDoraLefty
|
||||||
|
.title = "GitaDora Lefty Guitar (for Digital Wailing)",
|
||||||
|
.name = "gdlefty",
|
||||||
|
.desc = "Enables lefty mode, flipping motion sensor directions. Default: off.\n\n"
|
||||||
|
"Without this option, enabling LEFT in the game option will continuously trigger UP wail.\n\n"
|
||||||
|
"Has no effect if you are using analog bindings for X/Y axis; expectation is that your controller "
|
||||||
|
"handles this correctly (most do not, however).\n\n"
|
||||||
|
"As always, remember to restart the game after changing options. If you need to change in the game, "
|
||||||
|
"use the I/O panel overlay window",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.game_name = "GitaDora",
|
||||||
|
.category = "Game Options",
|
||||||
|
.elements = {
|
||||||
|
{"off", "both righty"},
|
||||||
|
{"p1", "p1 is lefty"},
|
||||||
|
{"p2", "p2 is lefty"},
|
||||||
|
{"both", "both lefty"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// GitaDoraWailHold
|
||||||
|
.title = "GitaDora Digital Wail Hold",
|
||||||
|
.name = "gdwailhold",
|
||||||
|
.desc = "For digital wail input, how long (in milliseconds) to hold the wail state. Practically, this controls "
|
||||||
|
"how sensitive wailing is.\n\n"
|
||||||
|
"Default: 100 (ms)\n\n"
|
||||||
|
"If set to 0, no hold will occur, which means you need to press the wail button longer to register; "
|
||||||
|
"default value of 100ms will cause the game to trigger wail even on a light tap of the button",
|
||||||
|
.type = OptionType::Integer,
|
||||||
|
.game_name = "GitaDora",
|
||||||
|
.category = "Game Options",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "Force Load Jubeat Module",
|
.title = "Force Load Jubeat Module",
|
||||||
.name = "jb",
|
.name = "jb",
|
||||||
@@ -2225,7 +2273,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.display_name = "tapeledalgo",
|
.display_name = "tapeledalgo",
|
||||||
.aliases= "tapeledalgo",
|
.aliases= "tapeledalgo",
|
||||||
.desc = "For games with light arrays, determine the algorithm that is used to translate them into a single light binding in Lights tab. "
|
.desc = "For games with light arrays, determine the algorithm that is used to translate them into a single light binding in Lights tab. "
|
||||||
"Only applies to IIDX, SDVX, and DDR for now. Default: mid",
|
"Default: mid",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
.category = "I/O Options",
|
.category = "I/O Options",
|
||||||
.elements = {
|
.elements = {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace launcher {
|
|||||||
GraphicsForceRefresh,
|
GraphicsForceRefresh,
|
||||||
FullscreenResolution,
|
FullscreenResolution,
|
||||||
FullscreenOrientationFlip,
|
FullscreenOrientationFlip,
|
||||||
|
FullscreenSubRefreshRate,
|
||||||
Graphics9On12,
|
Graphics9On12,
|
||||||
spice2x_Dx9On12,
|
spice2x_Dx9On12,
|
||||||
NoLegacy,
|
NoLegacy,
|
||||||
@@ -99,6 +100,8 @@ namespace launcher {
|
|||||||
LoadGitaDoraModule,
|
LoadGitaDoraModule,
|
||||||
GitaDoraTwoChannelAudio,
|
GitaDoraTwoChannelAudio,
|
||||||
GitaDoraCabinetType,
|
GitaDoraCabinetType,
|
||||||
|
GitaDoraLefty,
|
||||||
|
GitaDoraWailHold,
|
||||||
LoadJubeatModule,
|
LoadJubeatModule,
|
||||||
LoadReflecBeatModule,
|
LoadReflecBeatModule,
|
||||||
LoadShogikaiModule,
|
LoadShogikaiModule,
|
||||||
|
|||||||
+88
-42
@@ -7,6 +7,8 @@
|
|||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/circular_buffer.h"
|
#include "util/circular_buffer.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "cfg/api.h"
|
#include "cfg/api.h"
|
||||||
#include "acio/icca/icca.h"
|
#include "acio/icca/icca.h"
|
||||||
@@ -336,6 +338,9 @@ static int __cdecl gfdm_unit2_boot_initialize() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr int LEFTY_X_CENTER = 2040;
|
||||||
|
|
||||||
|
// this seems to be only used in the I/O test menu, not in gameplay
|
||||||
static void *__cdecl gfdm_unit_get_button_p(void *a1, int a2, size_t player) {
|
static void *__cdecl gfdm_unit_get_button_p(void *a1, int a2, size_t player) {
|
||||||
memset(a1, 0, 64);
|
memset(a1, 0, 64);
|
||||||
|
|
||||||
@@ -344,31 +349,61 @@ static void *__cdecl gfdm_unit_get_button_p(void *a1, int a2, size_t player) {
|
|||||||
return a1;
|
return a1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log_info("gitadora", "gfdm_unit_get_button_p: a2={}, player={}", a2, player);
|
||||||
|
|
||||||
// get buttons
|
// get buttons
|
||||||
auto &buttons = games::gitadora::get_buttons();
|
auto &buttons = games::gitadora::get_buttons();
|
||||||
auto &analogs = games::gitadora::get_analogs();
|
auto &analogs = games::gitadora::get_analogs();
|
||||||
|
const auto wail_up =
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[12 + 11 * (size_t) player]));
|
||||||
|
const auto wail_down =
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[13 + 11 * (size_t) player]));
|
||||||
|
const auto wail_result =
|
||||||
|
socd::get_guitar_wail(player, wail_up, wail_down, get_performance_milliseconds());
|
||||||
|
|
||||||
// X
|
// wail X
|
||||||
((int *) a1)[4] = a2 == 1 ? 4080 : -4080;
|
((int *) a1)[4] = games::gitadora::is_player_lefty(player) ? LEFTY_X_CENTER : -4080;
|
||||||
if (analogs.at(player * 3 + 0).isSet())
|
auto &analog_x = analogs.at(gitadora_analog_mapping[player * 4 + 0]);
|
||||||
((int *) a1)[4] = lroundf(Analogs::getState(
|
if (analog_x.isSet())
|
||||||
RI_MGR, analogs.at(gitadora_analog_mapping[player * 4 + 0])) * 8160.f) - 4080;
|
((int *) a1)[4] = lroundf(Analogs::getState(RI_MGR, analog_x) * 8160.f) - 4080;
|
||||||
|
|
||||||
// Y
|
// wail Y
|
||||||
((int *) a1)[5] = 0;
|
((int *) a1)[5] = 0;
|
||||||
if (analogs.at(player * 3 + 1).isSet())
|
auto &analog_y = analogs.at(gitadora_analog_mapping[player * 4 + 1]);
|
||||||
((int *) a1)[5] = lroundf(Analogs::getState(
|
if (analog_y.isSet()) {
|
||||||
RI_MGR, analogs.at(gitadora_analog_mapping[player * 4 + 1])) * 8160.f) - 4080;
|
((int *) a1)[5] = lroundf(Analogs::getState(RI_MGR, analog_y) * 8160.f) - 4080;
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[12 + 11 * (size_t) player])))
|
}
|
||||||
((int *) a1)[5] = -4080;
|
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[13 + 11 * (size_t) player])))
|
|
||||||
((int *) a1)[5] = 4080;
|
|
||||||
|
|
||||||
// Z
|
// digital wailing
|
||||||
|
if (!games::gitadora::is_player_lefty(player)) {
|
||||||
|
// righty
|
||||||
|
if (wail_result == socd::TiltUp) {
|
||||||
|
// top center
|
||||||
|
((int *) a1)[4] = 0; // X
|
||||||
|
((int *) a1)[5] = -4080; // Y
|
||||||
|
} else if (wail_result == socd::TiltDown) {
|
||||||
|
// bottom left; for lefty, bottom right
|
||||||
|
((int *) a1)[4] = -4080; // X
|
||||||
|
((int *) a1)[5] = 4080; // Y
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// lefty
|
||||||
|
if (wail_result == socd::TiltUp) {
|
||||||
|
// left
|
||||||
|
((int *) a1)[4] = -4080; // X
|
||||||
|
((int *) a1)[5] = 0; // Y
|
||||||
|
} else if (wail_result == socd::TiltDown) {
|
||||||
|
// right
|
||||||
|
((int *) a1)[4] = 4080; // X
|
||||||
|
((int *) a1)[5] = 0; // Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wail Z
|
||||||
((int *) a1)[6] = 0;
|
((int *) a1)[6] = 0;
|
||||||
if (analogs.at(player * 3 + 2).isSet())
|
auto &analog_z = analogs.at(gitadora_analog_mapping[player * 4 + 2]);
|
||||||
((int *) a1)[6] = lroundf(Analogs::getState(
|
if (analog_z.isSet())
|
||||||
RI_MGR, analogs.at(gitadora_analog_mapping[player * 4 + 2])) * 8160.f) - 4080;
|
((int *) a1)[6] = lroundf(Analogs::getState(RI_MGR, analog_z) * 8160.f) - 4080;
|
||||||
|
|
||||||
// return the same buffer
|
// return the same buffer
|
||||||
return a1;
|
return a1;
|
||||||
@@ -585,63 +620,74 @@ static long __cdecl gfdm_unit2_get_input(int device) {
|
|||||||
return gfdm_unit_get_input_p(device, 1);
|
return gfdm_unit_get_input_p(device, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is used in gameplay
|
||||||
static long __cdecl gfdm_unit_get_sensor_gf_p(int a1, int a2, size_t player) {
|
static long __cdecl gfdm_unit_get_sensor_gf_p(int a1, int a2, size_t player) {
|
||||||
|
|
||||||
// return if it's actually drum mania
|
// return if it's actually drum mania
|
||||||
if (games::gitadora::is_drum())
|
if (games::gitadora::is_drum())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
// log_info("gitadora", "gfdm_unit_get_sensor_gf_p: a1={}, a2={}, player={}", a1, a2, player);
|
||||||
|
|
||||||
// get buttons and analogs
|
// get buttons and analogs
|
||||||
auto &buttons = games::gitadora::get_buttons();
|
auto &buttons = games::gitadora::get_buttons();
|
||||||
auto &analogs = games::gitadora::get_analogs();
|
auto &analogs = games::gitadora::get_analogs();
|
||||||
|
|
||||||
// X
|
// figure out digital wail Y
|
||||||
|
const size_t offset = (size_t) player * 11;
|
||||||
|
const auto wail_up = Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[12 + offset]));
|
||||||
|
const auto wail_down = Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[13 + offset]));
|
||||||
|
const auto wail_result = socd::get_guitar_wail(player, wail_up, wail_down, get_performance_milliseconds());
|
||||||
|
|
||||||
|
// wail X
|
||||||
if (a2 == 0) {
|
if (a2 == 0) {
|
||||||
|
long ret = games::gitadora::is_player_lefty(player) ? LEFTY_X_CENTER : -4080;
|
||||||
|
|
||||||
// analog override
|
// analog override
|
||||||
if (analogs.at(gitadora_analog_mapping[player * 3 + 0]).isSet()) {
|
auto &analog_x = analogs.at(gitadora_analog_mapping[player * 4 + 0]);
|
||||||
return lroundf(Analogs::getState(
|
if (analog_x.isSet()) {
|
||||||
RI_MGR, analogs.at(gitadora_analog_mapping[player * 4 + 0])) * 8160.f) - 4080;
|
ret = lroundf(Analogs::getState(RI_MGR, analog_x) * 8160.f) - 4080;
|
||||||
|
}
|
||||||
|
|
||||||
|
// digital wail up/down
|
||||||
|
if (wail_result == socd::TiltUp) {
|
||||||
|
ret = games::gitadora::is_player_lefty(player) ? -4080 : 0;
|
||||||
|
} else if (wail_result == socd::TiltDown) {
|
||||||
|
ret = games::gitadora::is_player_lefty(player) ? +4080 : -4080;
|
||||||
}
|
}
|
||||||
|
|
||||||
// default
|
// default
|
||||||
return a1 == 1 ? 4080 : -4080;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Y
|
// wail Y
|
||||||
if (a2 == 1) {
|
if (a2 == 1) {
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
// analog override
|
// analog override
|
||||||
if (analogs.at(player * 3 + 1).isSet()) {
|
auto &analog_y = analogs.at(gitadora_analog_mapping[player * 4 + 1]);
|
||||||
return lroundf(Analogs::getState(
|
if (analog_y.isSet()) {
|
||||||
RI_MGR, analogs.at(gitadora_analog_mapping[player * 4 + 1])) * 8160.f) - 4080;
|
ret = lroundf(Analogs::getState(RI_MGR, analog_y) * 8160.f) - 4080;
|
||||||
}
|
}
|
||||||
|
|
||||||
// variables
|
// digital wail up/down
|
||||||
long ret = 0;
|
if (wail_result == socd::TiltUp) {
|
||||||
size_t offset = (size_t) player * 11;
|
ret = games::gitadora::is_player_lefty(player) ? 0 : -4080;
|
||||||
|
} else if (wail_result == socd::TiltDown) {
|
||||||
// wail up
|
ret = games::gitadora::is_player_lefty(player) ? 0 : 4080;
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[12 + offset]))) {
|
|
||||||
ret -= 4080;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wail down
|
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[13 + offset]))) {
|
|
||||||
ret += 4080;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return value
|
// return value
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Z
|
// wail Z
|
||||||
if (a2 == 2) {
|
if (a2 == 2) {
|
||||||
|
|
||||||
// analog override
|
// analog override
|
||||||
if (analogs.at(gitadora_analog_mapping[player * 3 + 2]).isSet()) {
|
auto &analog_z = analogs.at(gitadora_analog_mapping[player * 4 + 2]);
|
||||||
return lroundf(Analogs::getState(
|
if (analog_z.isSet()) {
|
||||||
RI_MGR, analogs.at(gitadora_analog_mapping[player * 4 + 2])) * 8160.f) - 4080;
|
return lroundf(Analogs::getState(RI_MGR, analog_z) * 8160.f) - 4080;
|
||||||
}
|
}
|
||||||
|
|
||||||
// default
|
// default
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ namespace overlay::windows {
|
|||||||
// help text for binding buttons, if the game has one
|
// help text for binding buttons, if the game has one
|
||||||
const auto help_text = games::get_buttons_help(this->games_selected_name);
|
const auto help_text = games::get_buttons_help(this->games_selected_name);
|
||||||
if (!help_text.empty()) {
|
if (!help_text.empty()) {
|
||||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Button Layout");
|
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Button Bindings");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::TextWrapped("%s", help_text.c_str());
|
ImGui::TextWrapped("%s", help_text.c_str());
|
||||||
ImGui::TextUnformatted("");
|
ImGui::TextUnformatted("");
|
||||||
@@ -299,8 +299,18 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
if (ImGui::BeginTabItem("Analogs")) {
|
if (ImGui::BeginTabItem("Analogs")) {
|
||||||
tab_selected_new = ConfigTab::CONFIG_TAB_ANALOGS;
|
tab_selected_new = ConfigTab::CONFIG_TAB_ANALOGS;
|
||||||
|
|
||||||
ImGui::BeginChild("Analogs", ImVec2(
|
ImGui::BeginChild("Analogs", ImVec2(
|
||||||
0, ImGui::GetWindowContentRegionMax().y - page_offset2), false);
|
0, ImGui::GetWindowContentRegionMax().y - page_offset2), false);
|
||||||
|
|
||||||
|
// help text for binding analog, if the game has one
|
||||||
|
const auto help_text = games::get_analogs_help(this->games_selected_name);
|
||||||
|
if (!help_text.empty()) {
|
||||||
|
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Analog Bindings");
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextWrapped("%s", help_text.c_str());
|
||||||
|
ImGui::TextUnformatted("");
|
||||||
|
}
|
||||||
this->build_analogs("Game", games::get_analogs(this->games_selected_name));
|
this->build_analogs("Game", games::get_analogs(this->games_selected_name));
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include "external/fmt/include/fmt/chrono.h"
|
||||||
#include "fps.h"
|
#include "fps.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
@@ -15,7 +16,8 @@ namespace overlay::windows {
|
|||||||
| ImGuiWindowFlags_NoNavInputs
|
| ImGuiWindowFlags_NoNavInputs
|
||||||
| ImGuiWindowFlags_NoDocking;
|
| ImGuiWindowFlags_NoDocking;
|
||||||
this->bg_alpha = 0.5f;
|
this->bg_alpha = 0.5f;
|
||||||
this->start_time = std::chrono::system_clock::now();
|
this->start_time =
|
||||||
|
std::chrono::floor<std::chrono::seconds>(std::chrono::system_clock::now());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPS::calculate_initial_window() {
|
void FPS::calculate_initial_window() {
|
||||||
@@ -34,23 +36,23 @@ namespace overlay::windows {
|
|||||||
ImGui::Text("FPS: %.1f", io.Framerate);
|
ImGui::Text("FPS: %.1f", io.Framerate);
|
||||||
// ImGui::Text("FT: %.2fms", 1000 / io.Framerate);
|
// ImGui::Text("FT: %.2fms", 1000 / io.Framerate);
|
||||||
|
|
||||||
auto now = std::chrono::system_clock::now();
|
const auto now = std::chrono::system_clock::now();
|
||||||
|
const auto now_s = std::chrono::floor<std::chrono::seconds>(now);
|
||||||
|
|
||||||
// current time
|
// current time
|
||||||
{
|
{
|
||||||
auto now_t = std::chrono::system_clock::to_time_t(now);
|
const std::time_t tt = std::chrono::system_clock::to_time_t(now_s);
|
||||||
static CHAR buf[48];
|
std::tm local_tm{};
|
||||||
std::strftime(buf, sizeof(buf), "Time: %H:%M:%S", std::localtime(&now_t));
|
localtime_s(&local_tm, &tt);
|
||||||
ImGui::Text(buf);
|
ImGui::TextUnformatted(fmt::format("Time: {:%H:%M:%S}", local_tm).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// elapsed time
|
// elapsed time
|
||||||
{
|
{
|
||||||
auto d = now - this->start_time;
|
const auto uptime = now_s - this->start_time;
|
||||||
const auto h = std::chrono::duration_cast<std::chrono::hours>(d);
|
ImGui::TextUnformatted(
|
||||||
const auto m = std::chrono::duration_cast<std::chrono::minutes>(d - h);
|
fmt::format("Up: {:%H:%M:%S}",
|
||||||
const auto s = std::chrono::duration_cast<std::chrono::seconds>(d - h - m);
|
std::chrono::floor<std::chrono::seconds>(uptime)).c_str());
|
||||||
ImGui::Text("Up: %02d:%02d:%02d", (int)h.count(), (int)m.count(), (int)s.count());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "overlay/imgui/extensions.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
@@ -80,6 +81,9 @@ namespace overlay::windows {
|
|||||||
{
|
{
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFrameHeightWithSpacing());
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFrameHeightWithSpacing());
|
||||||
this->build_button("+", tall, this->test_button, this->service_button);
|
this->build_button("+", tall, this->test_button, this->service_button);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::HelpTooltip("SERVICE + TEST");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "games/gitadora/gitadora.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/gitadora/io.h"
|
#include "games/gitadora/io.h"
|
||||||
|
#include "overlay/imgui/extensions.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ namespace overlay::windows {
|
|||||||
GitadoraIOPanel::GitadoraIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
GitadoraIOPanel::GitadoraIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
||||||
this->title = "GITADORA IO Panel";
|
this->title = "GITADORA IO Panel";
|
||||||
|
|
||||||
|
this->has_menu_controls = true;
|
||||||
// by default, make a safer assumption that there are two players
|
// by default, make a safer assumption that there are two players
|
||||||
this->two_players = true;
|
this->two_players = true;
|
||||||
// by default, enable the extra input only available on DX cabs...
|
// by default, enable the extra input only available on DX cabs...
|
||||||
@@ -33,6 +35,12 @@ namespace overlay::windows {
|
|||||||
this->has_guitar_knobs = false;
|
this->has_guitar_knobs = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (games::gitadora::is_arena_model()) {
|
||||||
|
this->has_menu_controls = false;
|
||||||
|
this->two_players = false;
|
||||||
|
this->has_guitar_knobs = false;
|
||||||
|
}
|
||||||
|
|
||||||
find_gfdm_buttons();
|
find_gfdm_buttons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,23 +78,43 @@ namespace overlay::windows {
|
|||||||
void GitadoraIOPanel::build_io_panel() {
|
void GitadoraIOPanel::build_io_panel() {
|
||||||
ImGui::Dummy(overlay::apply_scaling_to_vector(12, 0));
|
ImGui::Dummy(overlay::apply_scaling_to_vector(12, 0));
|
||||||
|
|
||||||
ImGui::SameLine();
|
if (this->has_menu_controls) {
|
||||||
this->draw_buttons(0);
|
|
||||||
if (this->has_guitar_knobs) {
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
this->draw_sliders(0);
|
ImGui::PushID("P1");
|
||||||
|
this->draw_buttons(0);
|
||||||
|
if (this->has_guitar_knobs) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_sliders(0);
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
// draw p2 only if guitar freaks
|
||||||
|
if (this->two_players) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Dummy(overlay::apply_scaling_to_vector(12, 0));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushID("P2");
|
||||||
|
this->draw_buttons(1);
|
||||||
|
if (this->has_guitar_knobs) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
this->draw_sliders(1);
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw p2 only if guitar freaks
|
if (games::gitadora::is_guitar()) {
|
||||||
if (this->two_players) {
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Dummy(overlay::apply_scaling_to_vector(12, 0));
|
ImGui::Dummy(overlay::apply_scaling_to_vector(12, 0));
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
this->draw_buttons(1);
|
ImGui::BeginGroup();
|
||||||
if (this->has_guitar_knobs) {
|
{
|
||||||
ImGui::SameLine();
|
ImGui::Checkbox("P1 Lefty", &games::gitadora::P1_LEFTY);
|
||||||
this->draw_sliders(1);
|
if (this->two_players) {
|
||||||
|
ImGui::Checkbox("P2 Lefty", &games::gitadora::P2_LEFTY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::EndGroup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +135,10 @@ namespace overlay::windows {
|
|||||||
{
|
{
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||||
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
|
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
|
||||||
|
this->build_button("+", tiny_size, this->help[p], this->start[p], nullptr);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::HelpTooltip("HELP + START");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
|
|
||||||
@@ -135,6 +167,9 @@ namespace overlay::windows {
|
|||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
{
|
{
|
||||||
this->build_button("?", tiny_size, this->help[p], nullptr, this->help_light[p]);
|
this->build_button("?", tiny_size, this->help[p], nullptr, this->help_light[p]);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::HelpTooltip("HELP");
|
||||||
|
}
|
||||||
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
|
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
|
||||||
}
|
}
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace overlay::windows {
|
|||||||
void draw_buttons(const int player);
|
void draw_buttons(const int player);
|
||||||
void draw_sliders(const int player);
|
void draw_sliders(const int player);
|
||||||
|
|
||||||
|
bool has_menu_controls;
|
||||||
bool two_players;
|
bool two_players;
|
||||||
bool has_guitar_knobs;
|
bool has_guitar_knobs;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#include "patch_manager.h"
|
#include "patch_manager.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <winhttp.h>
|
#include <winhttp.h>
|
||||||
#include <psapi.h>
|
#include <psapi.h>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include "external/fmt/include/fmt/chrono.h"
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
#include "external/rapidjson/prettywriter.h"
|
#include "external/rapidjson/prettywriter.h"
|
||||||
#include "external/rapidjson/stringbuffer.h"
|
#include "external/rapidjson/stringbuffer.h"
|
||||||
@@ -1150,7 +1152,7 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_game_identifier(const std::filesystem::path& dll_path) {
|
std::string get_game_identifier(const std::filesystem::path& dll_path, bool print_info) {
|
||||||
uint32_t time_date_stamp = 0;
|
uint32_t time_date_stamp = 0;
|
||||||
uint32_t address_of_entry_point = 0;
|
uint32_t address_of_entry_point = 0;
|
||||||
|
|
||||||
@@ -1168,6 +1170,16 @@ namespace overlay::windows {
|
|||||||
time_date_stamp,
|
time_date_stamp,
|
||||||
address_of_entry_point);
|
address_of_entry_point);
|
||||||
|
|
||||||
|
if (print_info) {
|
||||||
|
const auto time = std::chrono::system_clock::from_time_t(time_date_stamp);
|
||||||
|
log_info(
|
||||||
|
"patchmanager",
|
||||||
|
"file: {}, patch id: {}, build timestamp of dll: {:%Y-%m-%d %H:%M}",
|
||||||
|
dll_path.has_filename() ? dll_path.filename().string() : dll_path.string(),
|
||||||
|
identifier,
|
||||||
|
time);
|
||||||
|
}
|
||||||
|
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1657,14 +1669,12 @@ namespace overlay::windows {
|
|||||||
ACTIVE_JSON_FILE = "";
|
ACTIVE_JSON_FILE = "";
|
||||||
|
|
||||||
std::string firstDll = avs::game::DLL_NAME;
|
std::string firstDll = avs::game::DLL_NAME;
|
||||||
std::string first_id = get_game_identifier(MODULE_PATH / firstDll);
|
std::string first_id = get_game_identifier(MODULE_PATH / firstDll, true);
|
||||||
std::filesystem::path firstPath = fmt::format("patches/{}.json", first_id);
|
std::filesystem::path firstPath = fmt::format("patches/{}.json", first_id);
|
||||||
|
|
||||||
log_misc("patchmanager", "patch identifier of {}: {}", firstDll, first_id);
|
|
||||||
|
|
||||||
auto extraDlls = getExtraDlls(firstDll);
|
auto extraDlls = getExtraDlls(firstDll);
|
||||||
std::erase_if(extraDlls, [](const std::string& dll) {
|
std::erase_if(extraDlls, [](const std::string& dll) {
|
||||||
auto identifier = get_game_identifier(MODULE_PATH / dll);
|
auto identifier = get_game_identifier(MODULE_PATH / dll, true);
|
||||||
return identifier.empty() || !fileutils::file_exists(fmt::format("patches/{}.json", identifier));
|
return identifier.empty() || !fileutils::file_exists(fmt::format("patches/{}.json", identifier));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
extern std::optional<std::string> PATCH_MANAGER_CFG_PATH_OVERRIDE;
|
extern std::optional<std::string> PATCH_MANAGER_CFG_PATH_OVERRIDE;
|
||||||
|
|
||||||
std::string get_game_identifier(const std::filesystem::path& dll_path);
|
std::string get_game_identifier(const std::filesystem::path& dll_path, bool print_info=false);
|
||||||
|
|
||||||
class PatchManager : public Window {
|
class PatchManager : public Window {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -100,6 +100,21 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ScreenResize::build_fullscreen_config() {
|
void ScreenResize::build_fullscreen_config() {
|
||||||
|
|
||||||
|
if (avs::game::is_model("KFC")) {
|
||||||
|
ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||||
|
"Warning: Enabling Image Resize uses more GPU\n"
|
||||||
|
"resources and may significantly lower framerate\n"
|
||||||
|
"for songs with Live2D! Results may vary, use at\n"
|
||||||
|
"your own risk.");
|
||||||
|
} else {
|
||||||
|
ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||||
|
"Warning: Enabling Image Resize uses more GPU\n"
|
||||||
|
"resources and may significantly lower framerate\n"
|
||||||
|
"in some situations! Results may vary, use at\n"
|
||||||
|
"your own risk.");
|
||||||
|
}
|
||||||
|
|
||||||
// enable checkbox
|
// enable checkbox
|
||||||
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -141,22 +156,27 @@ namespace overlay::windows {
|
|||||||
// aspect ratio
|
// aspect ratio
|
||||||
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
||||||
if (scene.keep_aspect_ratio) {
|
if (scene.keep_aspect_ratio) {
|
||||||
if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.5f, 2.5f)) {
|
|
||||||
|
// we want to avoid zooming out below 0.6 as it will cause StretchRect calls to
|
||||||
|
// go on a slow sync path with the GPU, leading to frame drops in some situations
|
||||||
|
// (e.g., SDVX Live2D)
|
||||||
|
|
||||||
|
if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.6f, 2.5f)) {
|
||||||
scene.scale_y = scene.scale_x;
|
scene.scale_y = scene.scale_x;
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||||
} else {
|
} else {
|
||||||
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.5f, 2.5f);
|
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.6f, 2.5f);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||||
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.5f, 2.5f);
|
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.6f, 2.5f);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* dupe_items[] = { "None", "Copy Left", "Copy Right" };
|
static const char* dupe_items[] = { "None", "Copy Left", "Copy Right" };
|
||||||
ImGui::Combo(
|
const bool duplicate_changed = ImGui::Combo(
|
||||||
"Duplicate",
|
"Duplicate",
|
||||||
reinterpret_cast<int *>(&scene.duplicate),
|
reinterpret_cast<int *>(&scene.duplicate),
|
||||||
dupe_items,
|
dupe_items,
|
||||||
@@ -167,6 +187,11 @@ namespace overlay::windows {
|
|||||||
"wrap-around effect when an offset is set.");
|
"wrap-around effect when an offset is set.");
|
||||||
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
// tell d3d9 device to clear surface to get rid of ghost image
|
||||||
|
if (duplicate_changed) {
|
||||||
|
cfg::SCREENRESIZE->need_surface_clean = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenResize::build_windowed_config() {
|
void ScreenResize::build_windowed_config() {
|
||||||
@@ -176,7 +201,6 @@ namespace overlay::windows {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TextUnformatted("Warning: may cause some games to crash.");
|
|
||||||
ImGui::BeginDisabled(graphics_window_change_crashes_game());
|
ImGui::BeginDisabled(graphics_window_change_crashes_game());
|
||||||
if (ImGui::Combo(
|
if (ImGui::Combo(
|
||||||
"Window Style",
|
"Window Style",
|
||||||
|
|||||||
@@ -1986,7 +1986,7 @@ void CALLBACK rawinput::RawInputManager::input_midi_proc(HMIDIIN hMidiIn, UINT w
|
|||||||
device.midiInfo->v2_last_off_time[midi_index] = get_performance_milliseconds();
|
device.midiInfo->v2_last_off_time[midi_index] = get_performance_milliseconds();
|
||||||
device.updated = true;
|
device.updated = true;
|
||||||
}
|
}
|
||||||
// for v2_drum, NOTE ON is ignored
|
// for v2_drum, NOTE OFF is ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ std::string_view log_get_datetime() {
|
|||||||
std::string_view log_get_datetime(std::time_t now) {
|
std::string_view log_get_datetime(std::time_t now) {
|
||||||
static thread_local char buf[64];
|
static thread_local char buf[64];
|
||||||
|
|
||||||
// `localtime` on Windows is thread-safe
|
std::tm local_tm{};
|
||||||
strftime(buf, sizeof(buf), "[%Y/%m/%d %X]", localtime(&now));
|
localtime_s(&local_tm, &now);
|
||||||
|
strftime(buf, sizeof(buf), "[%Y/%m/%d %X]", &local_tm);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,4 +84,63 @@ namespace socd {
|
|||||||
return SocdNone;
|
return SocdNone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// first dimension: p1/p2
|
||||||
|
// second dimension: TiltUp / TiltDown
|
||||||
|
// value: last timestamp when it was on
|
||||||
|
static double most_recent_active[2][2] = {};
|
||||||
|
|
||||||
|
uint32_t TILT_HOLD_MS = 100;
|
||||||
|
|
||||||
|
TiltResult get_guitar_wail(uint8_t device, bool up, bool down, double time_now) {
|
||||||
|
if (device >= 2) {
|
||||||
|
log_fatal("socd", "invalid device index in socd_clean: {}", device);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto socd_result = socd_clean(device, up, down, time_now);
|
||||||
|
|
||||||
|
if (up) {
|
||||||
|
most_recent_active[device][TiltUp] = time_now;
|
||||||
|
}
|
||||||
|
if (down) {
|
||||||
|
most_recent_active[device][TiltDown] = time_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug(
|
||||||
|
"socd",
|
||||||
|
"p{} wail up={}, down={}",
|
||||||
|
device + 1,
|
||||||
|
most_recent_active[device][TiltUp],
|
||||||
|
most_recent_active[device][TiltDown]);
|
||||||
|
|
||||||
|
const auto delta_up = time_now - most_recent_active[device][TiltUp];
|
||||||
|
const auto delta_down = time_now - most_recent_active[device][TiltDown];
|
||||||
|
const bool is_up = delta_up <= TILT_HOLD_MS;
|
||||||
|
const bool is_down = delta_down <= TILT_HOLD_MS;
|
||||||
|
|
||||||
|
auto result = TiltNone;
|
||||||
|
if (is_up && is_down) {
|
||||||
|
// both held recently: prefer more recently held using SOCD logic
|
||||||
|
if (socd_result == SocdCCW) {
|
||||||
|
result = TiltUp;
|
||||||
|
} else if (socd_result == SocdCW) {
|
||||||
|
result = TiltDown;
|
||||||
|
} else {
|
||||||
|
result = TiltUp;
|
||||||
|
}
|
||||||
|
} else if (is_up) {
|
||||||
|
result = TiltUp;
|
||||||
|
} else if (is_down) {
|
||||||
|
result = TiltDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear opposite direction being held
|
||||||
|
if (result == TiltUp) {
|
||||||
|
most_recent_active[device][TiltDown] = 0.f;
|
||||||
|
} else if (result == TiltDown) {
|
||||||
|
most_recent_active[device][TiltUp] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
namespace socd {
|
namespace socd {
|
||||||
|
|
||||||
|
// SOCD for knobs / turntables
|
||||||
|
|
||||||
enum class SocdAlgorithm {
|
enum class SocdAlgorithm {
|
||||||
Neutral,
|
Neutral,
|
||||||
PreferRecent,
|
PreferRecent,
|
||||||
@@ -19,4 +21,16 @@ namespace socd {
|
|||||||
} SocdResult;
|
} SocdResult;
|
||||||
|
|
||||||
SocdResult socd_clean(uint8_t device, bool ccw, bool cw, double time_now);
|
SocdResult socd_clean(uint8_t device, bool ccw, bool cw, double time_now);
|
||||||
|
|
||||||
|
// for guitar wail (up/down only)
|
||||||
|
|
||||||
|
extern uint32_t TILT_HOLD_MS;
|
||||||
|
|
||||||
|
typedef enum _TiltResult {
|
||||||
|
TiltUp = 0,
|
||||||
|
TiltDown = 1,
|
||||||
|
TiltNone = 2
|
||||||
|
} TiltResult;
|
||||||
|
|
||||||
|
TiltResult get_guitar_wail(uint8_t device, bool up, bool down, double time_now);
|
||||||
}
|
}
|
||||||
@@ -235,6 +235,12 @@ namespace sysutils {
|
|||||||
} else {
|
} else {
|
||||||
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
|
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"gpuinfo", "{} {} is primary : {}",
|
||||||
|
prefix.c_str(),
|
||||||
|
index,
|
||||||
|
(adapter->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user