mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9db2db2340 | |||
| 1bb612f3c9 | |||
| e886ab77af | |||
| fbb3c1d4c3 | |||
| 73a69b2e8f | |||
| 63a0acac24 | |||
| 75ba49ff4a | |||
| b31ddb1ffc | |||
| 458a1495c8 | |||
| eb029faf2e | |||
| d65eb6ed4f | |||
| 97ec2e5d37 | |||
| 97d65b137a | |||
| a9f0e86aa3 | |||
| 88679b7cb2 | |||
| 9ec62a61ac | |||
| 44befb7e9a | |||
| be388f7b49 | |||
| 34ef034345 | |||
| ba4623b009 | |||
| a0a7b86508 | |||
| 6d7927927c | |||
| 837c8a46d6 | |||
| 22aeb64ff9 | |||
| e4b08064b7 | |||
| 1e6b4c16cc | |||
| d6eb8f7125 | |||
| dc6850e479 | |||
| 2d623e179b | |||
| 884d665c1c | |||
| 2842fe7ec6 | |||
| 954e6022d9 | |||
| 955c50a9f3 | |||
| dc82c980b1 | |||
| 408ed17521 | |||
| f4050e9adb | |||
| badae01357 | |||
| 38d9939eb7 | |||
| f5a3caf536 | |||
| 027445afb1 | |||
| 63a3900b79 | |||
| f542a78347 | |||
| 4d32dde83e | |||
| 927170ce73 | |||
| 875a0f0765 | |||
| 3ad88ef15c | |||
| 7a6cadb176 | |||
| 4feca23240 | |||
| 2e52bebd3d | |||
| aa218d5be7 | |||
| 94f009991a | |||
| c3b9ce3fdc | |||
| 1517cf9a7c | |||
| f0f46296f2 | |||
| 1d1981fcde | |||
| 2f538fe2e4 | |||
| 8e01b2e5c8 | |||
| cd14ca800e | |||
| cb59fe14e9 | |||
| 9fd9290cd3 | |||
| 1dd99aac74 | |||
| 9e376f373e | |||
| d4e51b77d1 | |||
| 61181836b0 | |||
| 4e26c8afa5 | |||
| 7684028f99 | |||
| 1d6be8fe4b | |||
| c70cf40af3 | |||
| 5715d4e605 | |||
| 821a705a7d | |||
| 86f2a1fd39 | |||
| 86f218329a | |||
| 66094de38a | |||
| c00cff2afd | |||
| b2cc8d404c | |||
| a0a04ab62f | |||
| a3a59e689e | |||
| a6f29d6a5a | |||
| 9be618d791 | |||
| 6bf7ae9074 | |||
| e3d63c65c1 | |||
| 5d2a9fa1cf | |||
| 0078b72b03 | |||
| b1ee37a066 | |||
| ebf6382e7f | |||
| 8e23ef224a | |||
| 6706ffc321 | |||
| e755b06e35 | |||
| 9ef22712c0 | |||
| 7b903d35e2 | |||
| 7f281a50c5 | |||
| 1354d63b85 | |||
| e745cdcac8 | |||
| bf7666939a | |||
| 7e89037db3 | |||
| 6ffaa77f58 | |||
| 7a1f46d1be | |||
| cf59ae5f89 | |||
| 9c06dd36a0 | |||
| d5d082ec37 | |||
| 17723d290a |
+11
-3
@@ -1,8 +1,7 @@
|
|||||||
## Contributing
|
## Contributing
|
||||||
Baseline rules for patch submissions are as follows. Any patches violating the rules below will not be accepted.
|
Baseline rules for patch submissions are as follows. Any patches violating the rules below will not be accepted.
|
||||||
|
|
||||||
### New as of March 2025
|
To contribute, fork the repo (just the main branch), make changes in your fork, and contribute to upstream by opening a pull request to the main repo.
|
||||||
Pull requests in GitHub are open. You don't need to bother with patch file submissions anymore. To contribute, fork the repo (just the main branch), make changes in your fork, and contribute to upstream by opening a pull request to the main repo.
|
|
||||||
|
|
||||||
### Adding support for games
|
### Adding support for games
|
||||||
|
|
||||||
@@ -12,6 +11,14 @@ Pull requests in GitHub are open. You don't need to bother with patch file submi
|
|||||||
* For a new game that was never supported (not a new version of a game, but rather a new series): please wait 1 year after official AC release in Japan.
|
* For a new game that was never supported (not a new version of a game, but rather a new series): please wait 1 year after official AC release in Japan.
|
||||||
* For new version of an already supported game: proceed with caution and use generally-accepted community guidelines.
|
* For new version of an already supported game: proceed with caution and use generally-accepted community guidelines.
|
||||||
|
|
||||||
|
### Forbidden "features" that will never be accepted
|
||||||
|
|
||||||
|
* Performing decryption of encrypted files
|
||||||
|
* Including (bundling), distributing, or linking to game data, or making it easier to find/download game data
|
||||||
|
* Any features that [phone home](https://en.wikipedia.org/wiki/Phoning_home)
|
||||||
|
* This includes automatic updaters.
|
||||||
|
* Exception: we have allowed the "patch import from URL" feature, but this has only been done under very careful considerations and plenty of warnings to the user.
|
||||||
|
|
||||||
### Avoiding regressions
|
### Avoiding regressions
|
||||||
|
|
||||||
The biggest risk in making code changes to spice is the risk of regressions. There is a **lot** of shared code used by many different games, reaching back game versions that are more than a decade old. It is practically impossible to test all supported games and versions.
|
The biggest risk in making code changes to spice is the risk of regressions. There is a **lot** of shared code used by many different games, reaching back game versions that are more than a decade old. It is practically impossible to test all supported games and versions.
|
||||||
@@ -22,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
|
||||||
|
|
||||||
@@ -51,6 +58,7 @@ Watch out for legacy OS compatibility. Currently, the minimum support floor is W
|
|||||||
* Indents are four spaces.
|
* Indents are four spaces.
|
||||||
* Try to keep under 100 characters per line. This includes comments and string literals.
|
* Try to keep under 100 characters per line. This includes comments and string literals.
|
||||||
* Avoid making unnecessary formatting or whitespace changes in a PR; e.g., watch out for text editor automatically stripping trailing whitespace. It pollutes the diff and slows down code review.
|
* Avoid making unnecessary formatting or whitespace changes in a PR; e.g., watch out for text editor automatically stripping trailing whitespace. It pollutes the diff and slows down code review.
|
||||||
|
* For signed/unsigned integer types, prefer `cstdint` types; e.g., use `int32_t`, `uint64_t` and not `int` and `unsigned long long`.
|
||||||
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
||||||
|
|
||||||
OK:
|
OK:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
> Feel free to remove this section after you have read it.
|
> Feel free to remove this section after you have read it.
|
||||||
|
|
||||||
## Link to GitHub Issue, if one exists
|
## Link to GitHub Issue or related Pull Request, if one exists
|
||||||
#0
|
#0
|
||||||
|
|
||||||
## Description of change
|
## Description of change
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|||||||
# RapidJSON does this
|
# RapidJSON does this
|
||||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||||
|
|
||||||
|
add_compile_options("-Wno-nontrivial-memcall") # imgui, rapidjson
|
||||||
|
add_compile_options("-Wno-deprecated-builtins") # robin_hood
|
||||||
|
add_compile_options("-Wno-deprecated-declarations") # rapidjson
|
||||||
|
add_compile_options("-Wno-inconsistent-missing-override") # headsocket
|
||||||
|
add_compile_options("-Wno-microsoft-exception-spec") # gcc/clang have different noexcept specifiers on d3d9 COM interfaces
|
||||||
|
|
||||||
# warnings
|
# warnings
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
||||||
|
|
||||||
@@ -170,8 +176,8 @@ else()
|
|||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||||
|
|
||||||
# file prefix map for relative working directory
|
# file prefix map for relative working directory
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# release with debug info flags
|
# release with debug info flags
|
||||||
@@ -346,6 +352,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
cfg/configurator.cpp
|
cfg/configurator.cpp
|
||||||
cfg/configurator_wnd.cpp
|
cfg/configurator_wnd.cpp
|
||||||
cfg/screen_resize.cpp
|
cfg/screen_resize.cpp
|
||||||
|
cfg/controller_presets.cpp
|
||||||
|
|
||||||
# easrv
|
# easrv
|
||||||
easrv/easrv.cpp
|
easrv/easrv.cpp
|
||||||
@@ -441,6 +448,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/scotto/io.cpp
|
games/scotto/io.cpp
|
||||||
games/drs/drs.cpp
|
games/drs/drs.cpp
|
||||||
games/drs/io.cpp
|
games/drs/io.cpp
|
||||||
|
games/drs/rgb_cam.cpp
|
||||||
games/we/we.cpp
|
games/we/we.cpp
|
||||||
games/we/io.cpp
|
games/we/io.cpp
|
||||||
games/we/touchpanel.cpp
|
games/we/touchpanel.cpp
|
||||||
@@ -554,9 +562,11 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
overlay/windows/camera_control.cpp
|
overlay/windows/camera_control.cpp
|
||||||
overlay/windows/card_manager.cpp
|
overlay/windows/card_manager.cpp
|
||||||
overlay/windows/drs_dancefloor.cpp
|
overlay/windows/drs_dancefloor.cpp
|
||||||
|
overlay/windows/gfdm_sub.cpp
|
||||||
overlay/windows/screen_resize.cpp
|
overlay/windows/screen_resize.cpp
|
||||||
overlay/windows/sdvx_sub.cpp
|
overlay/windows/sdvx_sub.cpp
|
||||||
overlay/windows/config.cpp
|
overlay/windows/config.cpp
|
||||||
|
overlay/windows/controller_presets.cpp
|
||||||
overlay/windows/control.cpp
|
overlay/windows/control.cpp
|
||||||
overlay/windows/eadev.cpp
|
overlay/windows/eadev.cpp
|
||||||
overlay/windows/fps.cpp
|
overlay/windows/fps.cpp
|
||||||
@@ -624,6 +634,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
util/execexe.cpp
|
util/execexe.cpp
|
||||||
util/dependencies.cpp
|
util/dependencies.cpp
|
||||||
util/deferlog.cpp
|
util/deferlog.cpp
|
||||||
|
util/socd_cleaner.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||||
|
|||||||
@@ -9,11 +9,22 @@
|
|||||||
#include "games/drs/drs.h"
|
#include "games/drs/drs.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "util/tapeled.h"
|
#include "util/tapeled.h"
|
||||||
|
|
||||||
using namespace GameAPI;
|
using namespace GameAPI;
|
||||||
|
|
||||||
|
#define DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
// state
|
// state
|
||||||
static uint8_t STATUS_BUFFER[272] {};
|
static uint8_t STATUS_BUFFER[272] {};
|
||||||
static bool STATUS_BUFFER_FREEZE = false;
|
static bool STATUS_BUFFER_FREEZE = false;
|
||||||
@@ -94,18 +105,25 @@ static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
const auto now = get_performance_milliseconds();
|
||||||
|
const auto vol_l_state = socd::socd_clean(0,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_l_state == socd::SocdCCW) {
|
||||||
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_l_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
|
||||||
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
const auto vol_r_state = socd::socd_clean(1,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_r_state == socd::SocdCCW) {
|
||||||
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_r_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
|
||||||
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +145,12 @@ static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
|||||||
// save volumes in buffer
|
// save volumes in buffer
|
||||||
*((uint16_t*) &STATUS_BUFFER[17]) = (uint16_t) ((vol_left) << 2);
|
*((uint16_t*) &STATUS_BUFFER[17]) = (uint16_t) ((vol_left) << 2);
|
||||||
*((uint16_t*) &STATUS_BUFFER[19]) = (uint16_t) ((vol_right) << 2);
|
*((uint16_t*) &STATUS_BUFFER[19]) = (uint16_t) ((vol_right) << 2);
|
||||||
|
|
||||||
|
log_debug(
|
||||||
|
"bi2a",
|
||||||
|
"knobs = {} {}",
|
||||||
|
*((uint16_t*) &STATUS_BUFFER[17]),
|
||||||
|
*((uint16_t*) &STATUS_BUFFER[19]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// DanceDanceRevolution
|
// DanceDanceRevolution
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -8,10 +8,21 @@
|
|||||||
#include "games/sdvx/io.h"
|
#include "games/sdvx/io.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
using namespace GameAPI;
|
using namespace GameAPI;
|
||||||
|
|
||||||
|
#define DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
// globals
|
// globals
|
||||||
uint8_t KFCA_VOL_SOUND = 96;
|
uint8_t KFCA_VOL_SOUND = 96;
|
||||||
uint8_t KFCA_VOL_HEADPHONE = 96;
|
uint8_t KFCA_VOL_HEADPHONE = 96;
|
||||||
@@ -342,18 +353,25 @@ static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
const auto now = get_performance_milliseconds();
|
||||||
|
const auto vol_l_state = socd::socd_clean(0,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_l_state == socd::SocdCCW) {
|
||||||
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_l_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
|
||||||
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
const auto vol_r_state = socd::socd_clean(1,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_r_state == socd::SocdCCW) {
|
||||||
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_r_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
|
||||||
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,6 +389,8 @@ static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
|||||||
// proper loops
|
// proper loops
|
||||||
vol_left %= 1024;
|
vol_left %= 1024;
|
||||||
vol_right %= 1024;
|
vol_right %= 1024;
|
||||||
|
|
||||||
|
log_debug("kfca", "knobs = {} {}", vol_left, vol_right);
|
||||||
|
|
||||||
// save volumes in buffer
|
// save volumes in buffer
|
||||||
STATUS_BUFFER[input_offset + 16 + 0] |= (unsigned char) ((vol_left << 6) & 0xFF);
|
STATUS_BUFFER[input_offset + 16 + 0] |= (unsigned char) ((vol_left << 6) & 0xFF);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "acio/icca/icca.h"
|
#include "acio/icca/icca.h"
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -101,13 +102,10 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
|||||||
|
|
||||||
// send version data
|
// send version data
|
||||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||||
if (
|
if (avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
||||||
avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
games::sdvx::is_valkyrie_model()) {
|
||||||
// SDVX Valkyrie cabinet mode
|
|
||||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
|
||||||
) {
|
|
||||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
||||||
} else if (avs::game::is_model({"VFG"})) {
|
} else if (avs::game::is_model("VFG")) {
|
||||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
||||||
} else {
|
} else {
|
||||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||||
@@ -520,11 +518,7 @@ void ICCADevice::update_status(int unit) {
|
|||||||
buffer[1] = felica ? 0x01 : 0x00;
|
buffer[1] = felica ? 0x01 : 0x00;
|
||||||
buffer[10] = felica ? 0x01 : 0x00;
|
buffer[10] = felica ? 0x01 : 0x00;
|
||||||
|
|
||||||
} else if (
|
} else if (avs::game::is_model({"LDJ", "TBS", "XIF"}) || games::sdvx::is_valkyrie_model()) {
|
||||||
avs::game::is_model({"LDJ", "TBS", "XIF"}) ||
|
|
||||||
// SDVX Valkyrie cabinet mode
|
|
||||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))) {
|
|
||||||
|
|
||||||
// set status to 0 otherwise reader power on fails
|
// set status to 0 otherwise reader power on fails
|
||||||
buffer[0] = 0x00;
|
buffer[0] = 0x00;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace api::modules {
|
|||||||
void DRS::touch_set(Request &req, Response &res) {
|
void DRS::touch_set(Request &req, Response &res) {
|
||||||
|
|
||||||
// get all touch points
|
// get all touch points
|
||||||
games::drs::drs_touch_t touches[16];
|
auto touches = std::make_unique<games::drs::drs_touch_t[]>(req.params.Size());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (Value ¶m : req.params.GetArray()) {
|
for (Value ¶m : req.params.GetArray()) {
|
||||||
|
|
||||||
@@ -86,6 +86,6 @@ namespace api::modules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// apply touch points
|
// apply touch points
|
||||||
games::drs::fire_touches(touches, i);
|
games::drs::fire_touches(touches.get(), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1234,6 +1234,7 @@ namespace avs {
|
|||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
new_style = logger::Style::RED;
|
new_style = logger::Style::RED;
|
||||||
|
deferredlogs::report_fatal_message();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
position = 0;
|
position = 0;
|
||||||
@@ -1302,6 +1303,7 @@ namespace avs {
|
|||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
style = logger::Style::RED;
|
style = logger::Style::RED;
|
||||||
|
deferredlogs::report_fatal_message();
|
||||||
break;
|
break;
|
||||||
case 'W':
|
case 'W':
|
||||||
style = logger::Style::YELLOW;
|
style = logger::Style::YELLOW;
|
||||||
|
|||||||
+24
-1
@@ -5,8 +5,9 @@
|
|||||||
#include "build/defs.h"
|
#include "build/defs.h"
|
||||||
#include "easrv/easrv.h"
|
#include "easrv/easrv.h"
|
||||||
#include "easrv/smartea.h"
|
#include "easrv/smartea.h"
|
||||||
#include "games/mfc/mfc.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "hooks/avshook.h"
|
#include "hooks/avshook.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/deferlog.h"
|
#include "util/deferlog.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
@@ -528,6 +529,28 @@ namespace avs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update -WINDOW in app-config
|
||||||
|
if (games::gitadora::is_arena_model()) {
|
||||||
|
avs::core::property_search_remove_safe(app_config, nullptr, "/param/cmdline");
|
||||||
|
std::string cmdline{};
|
||||||
|
if (games::gitadora::is_drum()) {
|
||||||
|
if (GRAPHICS_WINDOWED) {
|
||||||
|
cmdline = "-d -DM -WINDOW";
|
||||||
|
} else {
|
||||||
|
cmdline = "-d -DM";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (GRAPHICS_WINDOWED) {
|
||||||
|
cmdline = "-g -GF -WINDOW";
|
||||||
|
} else {
|
||||||
|
cmdline = "-g -GF";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_info("avs-ea3", "using cmdline: `{}`", cmdline);
|
||||||
|
avs::core::property_node_create(app_config, nullptr,
|
||||||
|
avs::core::NODE_TYPE_str, "/param/cmdline", cmdline.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
||||||
|
|
||||||
// call the game init
|
// call the game init
|
||||||
|
|||||||
@@ -0,0 +1,327 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates>
|
||||||
|
<!-- PHOENIXWAN controller preset for Beatmania IIDX
|
||||||
|
|
||||||
|
Button indices 0-6: keys 1-7
|
||||||
|
Button indices 8-11: E1 (Start for Deluxe Model Style), E2 (Effect), E3 (VEFX), E4 (P1 Start for Lightning Model Style)
|
||||||
|
|
||||||
|
P1 Side P2 Side
|
||||||
|
8 9 10 11 8 9 10 11
|
||||||
|
2 4 6 2 4 6
|
||||||
|
TT 1 3 5 7 1 3 5 7 TT -->
|
||||||
|
<template name="DJ DAO PHOENIXWAN" game="Beatmania IIDX">
|
||||||
|
<buttons>
|
||||||
|
<button name="P1 1" vkey="0" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 2" vkey="1" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 3" vkey="2" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 4" vkey="3" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 5" vkey="4" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 6" vkey="5" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 7" vkey="6" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 1" vkey="0" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 2" vkey="1" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 3" vkey="2" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 4" vkey="3" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 5" vkey="4" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 6" vkey="5" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 7" vkey="6" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<!-- P1 Start: E1 primary (index 8), E4 alternative (index 11) -->
|
||||||
|
<button name="P1 Start" vkey="8" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 Start" vkey="11" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<!-- P2 Start: E1 primary (index 8), E4 alternative (index 11) -->
|
||||||
|
<button name="P2 Start" vkey="8" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 Start" vkey="11" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<!-- EFFECT: E2 (index 9), primary P2 side, alternative P1 side -->
|
||||||
|
<button name="EFFECT" vkey="9" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="EFFECT" vkey="9" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<!-- VEFX: E3 (index 10), primary P2 side, alternative P1 side -->
|
||||||
|
<button name="VEFX" vkey="10" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="VEFX" vkey="10" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
</buttons>
|
||||||
|
<analogs>
|
||||||
|
<analog name="Turntable P1" devid="Player 1" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Turntable P2" devid="Player 2" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
</analogs>
|
||||||
|
<lights>
|
||||||
|
<light name="P1 1" devid="Player 1" index="0"/>
|
||||||
|
<light name="P1 2" devid="Player 1" index="1"/>
|
||||||
|
<light name="P1 3" devid="Player 1" index="2"/>
|
||||||
|
<light name="P1 4" devid="Player 1" index="3"/>
|
||||||
|
<light name="P1 5" devid="Player 1" index="4"/>
|
||||||
|
<light name="P1 6" devid="Player 1" index="5"/>
|
||||||
|
<light name="P1 7" devid="Player 1" index="6"/>
|
||||||
|
<light name="P2 1" devid="Player 2" index="0"/>
|
||||||
|
<light name="P2 2" devid="Player 2" index="1"/>
|
||||||
|
<light name="P2 3" devid="Player 2" index="2"/>
|
||||||
|
<light name="P2 4" devid="Player 2" index="3"/>
|
||||||
|
<light name="P2 5" devid="Player 2" index="4"/>
|
||||||
|
<light name="P2 6" devid="Player 2" index="5"/>
|
||||||
|
<light name="P2 7" devid="Player 2" index="6"/>
|
||||||
|
<!-- Start buttons bind both E1 (index 8) and E4 (index 11), but lights use
|
||||||
|
Lightning Model style only (one per side) to avoid both lighting up at once -->
|
||||||
|
<light name="P1 Start" devid="Player 1" index="10"/>
|
||||||
|
<light name="P2 Start" devid="Player 2" index="7"/>
|
||||||
|
<light name="VEFX" devid="Player 1" index="8"/>
|
||||||
|
<light name="VEFX" devid="Player 2" index="8"/>
|
||||||
|
<light name="Effect" devid="Player 1" index="9"/>
|
||||||
|
<light name="Effect" devid="Player 2" index="9"/>
|
||||||
|
</lights>
|
||||||
|
</template>
|
||||||
|
<!-- Common "double scratch" BMS playstyle: LSHIFT A S D SPACE L ; ' RSHIFT -->
|
||||||
|
<!-- Uses Up/Down arrows for turntable navigation -->
|
||||||
|
<template name="Keyboard (linear, double scratch, QWERTY)" game="Beatmania IIDX">
|
||||||
|
<buttons>
|
||||||
|
<button name="P1 1" vkey="65" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 2" vkey="83" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 3" vkey="68" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 4" vkey="32" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 5" vkey="76" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 6" vkey="186" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 7" vkey="222" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 TT+" vkey="40" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 TT-" vkey="38" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 TT+/-" vkey="160" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 TT+/- Alternate" vkey="161" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 Start" vkey="13" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 Start" vkey="69" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="EFFECT" vkey="81" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="VEFX" vkey="87" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
</buttons>
|
||||||
|
<keypad_buttons/>
|
||||||
|
<analogs/>
|
||||||
|
<lights/>
|
||||||
|
</template>
|
||||||
|
<template name="arcin (arcin-infinitas)" game="Beatmania IIDX">
|
||||||
|
<buttons>
|
||||||
|
<button name="P1 1" vkey="0" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 2" vkey="1" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 3" vkey="2" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 4" vkey="3" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 5" vkey="4" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 6" vkey="5" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 7" vkey="6" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P1 Start" vkey="9" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 1" vkey="0" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 2" vkey="1" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 3" vkey="2" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 4" vkey="3" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 5" vkey="4" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 6" vkey="5" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 7" vkey="6" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="P2 Start" vkey="9" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="EFFECT" vkey="11" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="EFFECT" vkey="11" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="VEFX" vkey="10" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="VEFX" vkey="10" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
</buttons>
|
||||||
|
<keypad_buttons/>
|
||||||
|
<!-- Assumes analog mode. Need to invert direction since this FW is designed for INF. -->
|
||||||
|
<analogs>
|
||||||
|
<analog name="Turntable P1" devid="arcin (1p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Turntable P2" devid="arcin (2p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
</analogs>
|
||||||
|
<lights>
|
||||||
|
<light name="P1 1" devid="arcin (1p)" index="0"/>
|
||||||
|
<light name="P1 2" devid="arcin (1p)" index="1"/>
|
||||||
|
<light name="P1 3" devid="arcin (1p)" index="2"/>
|
||||||
|
<light name="P1 4" devid="arcin (1p)" index="3"/>
|
||||||
|
<light name="P1 5" devid="arcin (1p)" index="4"/>
|
||||||
|
<light name="P1 6" devid="arcin (1p)" index="5"/>
|
||||||
|
<light name="P1 7" devid="arcin (1p)" index="6"/>
|
||||||
|
<light name="P2 1" devid="arcin (2p)" index="0"/>
|
||||||
|
<light name="P2 2" devid="arcin (2p)" index="1"/>
|
||||||
|
<light name="P2 3" devid="arcin (2p)" index="2"/>
|
||||||
|
<light name="P2 4" devid="arcin (2p)" index="3"/>
|
||||||
|
<light name="P2 5" devid="arcin (2p)" index="4"/>
|
||||||
|
<light name="P2 6" devid="arcin (2p)" index="5"/>
|
||||||
|
<light name="P2 7" devid="arcin (2p)" index="6"/>
|
||||||
|
<light name="P1 Start" devid="arcin (1p)" index="9"/>
|
||||||
|
<light name="P2 Start" devid="arcin (2p)" index="9"/>
|
||||||
|
</lights>
|
||||||
|
</template>
|
||||||
|
<!-- This controller has many modes; here, we use HID Analog mode which is meant for data -->
|
||||||
|
<template name="DJ DAO Nostroller (Analog mode)" game="Nostalgia">
|
||||||
|
<buttons/>
|
||||||
|
<keypad_buttons/>
|
||||||
|
<analogs>
|
||||||
|
<analog name="Key 1" devid="Analogs key 01-28" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 2" devid="Analogs key 01-28" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 3" devid="Analogs key 01-28" index="6" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 4" devid="Analogs key 01-28" index="7" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 5" devid="Analogs key 01-28" index="8" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 6" devid="Analogs key 01-28" index="9" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 7" devid="Analogs key 01-28" index="10" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 8" devid="Analogs key 01-28" index="11" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 9" devid="Analogs key 01-28" index="12" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 10" devid="Analogs key 01-28" index="13" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 11" devid="Analogs key 01-28" index="14" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 12" devid="Analogs key 01-28" index="15" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 13" devid="Analogs key 01-28" index="16" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 14" devid="Analogs key 01-28" index="17" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 15" devid="Analogs key 01-28" index="18" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 16" devid="Analogs key 01-28" index="19" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 17" devid="Analogs key 01-28" index="20" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 18" devid="Analogs key 01-28" index="21" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 19" devid="Analogs key 01-28" index="22" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 20" devid="Analogs key 01-28" index="23" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 21" devid="Analogs key 01-28" index="24" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 22" devid="Analogs key 01-28" index="25" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 23" devid="Analogs key 01-28" index="26" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 24" devid="Analogs key 01-28" index="27" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 25" devid="Analogs key 01-28" index="28" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 26" devid="Analogs key 01-28" index="29" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 27" devid="Analogs key 01-28" index="30" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Key 28" devid="Analogs key 01-28" index="31" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
</analogs>
|
||||||
|
<lights>
|
||||||
|
<light name="Key 1 R" devid="Lights 01-14" index="0"/>
|
||||||
|
<light name="Key 1 G" devid="Lights 01-14" index="1"/>
|
||||||
|
<light name="Key 1 B" devid="Lights 01-14" index="2"/>
|
||||||
|
<light name="Key 2 R" devid="Lights 01-14" index="3"/>
|
||||||
|
<light name="Key 2 G" devid="Lights 01-14" index="4"/>
|
||||||
|
<light name="Key 2 B" devid="Lights 01-14" index="5"/>
|
||||||
|
<light name="Key 3 R" devid="Lights 01-14" index="6"/>
|
||||||
|
<light name="Key 3 G" devid="Lights 01-14" index="7"/>
|
||||||
|
<light name="Key 3 B" devid="Lights 01-14" index="8"/>
|
||||||
|
<light name="Key 4 R" devid="Lights 01-14" index="9"/>
|
||||||
|
<light name="Key 4 G" devid="Lights 01-14" index="10"/>
|
||||||
|
<light name="Key 4 B" devid="Lights 01-14" index="11"/>
|
||||||
|
<light name="Key 5 R" devid="Lights 01-14" index="12"/>
|
||||||
|
<light name="Key 5 G" devid="Lights 01-14" index="13"/>
|
||||||
|
<light name="Key 5 B" devid="Lights 01-14" index="14"/>
|
||||||
|
<light name="Key 6 R" devid="Lights 01-14" index="15"/>
|
||||||
|
<light name="Key 6 G" devid="Lights 01-14" index="16"/>
|
||||||
|
<light name="Key 6 B" devid="Lights 01-14" index="17"/>
|
||||||
|
<light name="Key 7 R" devid="Lights 01-14" index="18"/>
|
||||||
|
<light name="Key 7 G" devid="Lights 01-14" index="19"/>
|
||||||
|
<light name="Key 7 B" devid="Lights 01-14" index="20"/>
|
||||||
|
<light name="Key 8 R" devid="Lights 01-14" index="21"/>
|
||||||
|
<light name="Key 8 G" devid="Lights 01-14" index="22"/>
|
||||||
|
<light name="Key 8 B" devid="Lights 01-14" index="23"/>
|
||||||
|
<light name="Key 9 R" devid="Lights 01-14" index="24"/>
|
||||||
|
<light name="Key 9 G" devid="Lights 01-14" index="25"/>
|
||||||
|
<light name="Key 9 B" devid="Lights 01-14" index="26"/>
|
||||||
|
<light name="Key 10 R" devid="Lights 01-14" index="27"/>
|
||||||
|
<light name="Key 10 G" devid="Lights 01-14" index="28"/>
|
||||||
|
<light name="Key 10 B" devid="Lights 01-14" index="29"/>
|
||||||
|
<light name="Key 11 R" devid="Lights 01-14" index="30"/>
|
||||||
|
<light name="Key 11 G" devid="Lights 01-14" index="31"/>
|
||||||
|
<light name="Key 11 B" devid="Lights 01-14" index="32"/>
|
||||||
|
<light name="Key 12 R" devid="Lights 01-14" index="33"/>
|
||||||
|
<light name="Key 12 G" devid="Lights 01-14" index="34"/>
|
||||||
|
<light name="Key 12 B" devid="Lights 01-14" index="35"/>
|
||||||
|
<light name="Key 13 R" devid="Lights 01-14" index="36"/>
|
||||||
|
<light name="Key 13 G" devid="Lights 01-14" index="37"/>
|
||||||
|
<light name="Key 13 B" devid="Lights 01-14" index="38"/>
|
||||||
|
<light name="Key 14 R" devid="Lights 01-14" index="39"/>
|
||||||
|
<light name="Key 14 G" devid="Lights 01-14" index="40"/>
|
||||||
|
<light name="Key 14 B" devid="Lights 01-14" index="41"/>
|
||||||
|
<light name="Key 15 R" devid="Lights 15-28" index="0"/>
|
||||||
|
<light name="Key 15 G" devid="Lights 15-28" index="1"/>
|
||||||
|
<light name="Key 15 B" devid="Lights 15-28" index="2"/>
|
||||||
|
<light name="Key 16 R" devid="Lights 15-28" index="3"/>
|
||||||
|
<light name="Key 16 G" devid="Lights 15-28" index="4"/>
|
||||||
|
<light name="Key 16 B" devid="Lights 15-28" index="5"/>
|
||||||
|
<light name="Key 17 R" devid="Lights 15-28" index="6"/>
|
||||||
|
<light name="Key 17 G" devid="Lights 15-28" index="7"/>
|
||||||
|
<light name="Key 17 B" devid="Lights 15-28" index="8"/>
|
||||||
|
<light name="Key 18 R" devid="Lights 15-28" index="9"/>
|
||||||
|
<light name="Key 18 G" devid="Lights 15-28" index="10"/>
|
||||||
|
<light name="Key 18 B" devid="Lights 15-28" index="11"/>
|
||||||
|
<light name="Key 19 R" devid="Lights 15-28" index="12"/>
|
||||||
|
<light name="Key 19 G" devid="Lights 15-28" index="13"/>
|
||||||
|
<light name="Key 19 B" devid="Lights 15-28" index="14"/>
|
||||||
|
<light name="Key 20 R" devid="Lights 15-28" index="15"/>
|
||||||
|
<light name="Key 20 G" devid="Lights 15-28" index="16"/>
|
||||||
|
<light name="Key 20 B" devid="Lights 15-28" index="17"/>
|
||||||
|
<light name="Key 21 R" devid="Lights 15-28" index="18"/>
|
||||||
|
<light name="Key 21 G" devid="Lights 15-28" index="19"/>
|
||||||
|
<light name="Key 21 B" devid="Lights 15-28" index="20"/>
|
||||||
|
<light name="Key 22 R" devid="Lights 15-28" index="21"/>
|
||||||
|
<light name="Key 22 G" devid="Lights 15-28" index="22"/>
|
||||||
|
<light name="Key 22 B" devid="Lights 15-28" index="23"/>
|
||||||
|
<light name="Key 23 R" devid="Lights 15-28" index="24"/>
|
||||||
|
<light name="Key 23 G" devid="Lights 15-28" index="25"/>
|
||||||
|
<light name="Key 23 B" devid="Lights 15-28" index="26"/>
|
||||||
|
<light name="Key 24 R" devid="Lights 15-28" index="27"/>
|
||||||
|
<light name="Key 24 G" devid="Lights 15-28" index="28"/>
|
||||||
|
<light name="Key 24 B" devid="Lights 15-28" index="29"/>
|
||||||
|
<light name="Key 25 R" devid="Lights 15-28" index="30"/>
|
||||||
|
<light name="Key 25 G" devid="Lights 15-28" index="31"/>
|
||||||
|
<light name="Key 25 B" devid="Lights 15-28" index="32"/>
|
||||||
|
<light name="Key 26 R" devid="Lights 15-28" index="33"/>
|
||||||
|
<light name="Key 26 G" devid="Lights 15-28" index="34"/>
|
||||||
|
<light name="Key 26 B" devid="Lights 15-28" index="35"/>
|
||||||
|
<light name="Key 27 R" devid="Lights 15-28" index="36"/>
|
||||||
|
<light name="Key 27 G" devid="Lights 15-28" index="37"/>
|
||||||
|
<light name="Key 27 B" devid="Lights 15-28" index="38"/>
|
||||||
|
<light name="Key 28 R" devid="Lights 15-28" index="39"/>
|
||||||
|
<light name="Key 28 G" devid="Lights 15-28" index="40"/>
|
||||||
|
<light name="Key 28 B" devid="Lights 15-28" index="41"/>
|
||||||
|
</lights>
|
||||||
|
</template>
|
||||||
|
<!-- Gitaller has two modes - digital tilt and analog axis. This binds both of them at the same time. -->
|
||||||
|
<template name="DJ DAO Gitaller" game="GitaDora">
|
||||||
|
<buttons>
|
||||||
|
<button name="Guitar P1 Start" vkey="9" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 Help" vkey="8" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 Pick Up" vkey="5" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 Pick Down" vkey="6" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 R" vkey="0" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 G" vkey="1" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 B" vkey="2" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 Y" vkey="3" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 P" vkey="4" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 Wail Up" vkey="10" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Guitar P1 Wail Down" vkey="11" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
</buttons>
|
||||||
|
<keypad_buttons/>
|
||||||
|
<analogs>
|
||||||
|
<analog name="Guitar P1 Wail X" devid="Gitaller P1" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Guitar P1 Wail Y" devid="Gitaller P1" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="Guitar P1 Wail Z" devid="Gitaller P1" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
</analogs>
|
||||||
|
<lights/>
|
||||||
|
<!-- Gen 1 FAUCETWO in HID mode; should also work for later gens -->
|
||||||
|
</template>
|
||||||
|
<template name="DJ DAO FAUCETWO" game="Sound Voltex">
|
||||||
|
<buttons>
|
||||||
|
<button name="BT-A" vkey="1" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="BT-B" vkey="2" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="BT-C" vkey="3" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="BT-D" vkey="4" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="FX-L" vkey="5" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="FX-R" vkey="6" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
<button name="Start" vkey="0" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
|
</buttons>
|
||||||
|
<keypad_buttons/>
|
||||||
|
<analogs>
|
||||||
|
<analog name="VOL-L" devid="Buttons (MI_01)" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
<analog name="VOL-R" devid="Buttons (MI_01)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||||
|
</analogs>
|
||||||
|
<lights>
|
||||||
|
<light name="BT-A" devid="Lights (MI_02)" index="24"/>
|
||||||
|
<light name="BT-B" devid="Lights (MI_02)" index="25"/>
|
||||||
|
<light name="BT-C" devid="Lights (MI_02)" index="26"/>
|
||||||
|
<light name="BT-D" devid="Lights (MI_02)" index="27"/>
|
||||||
|
<light name="FX-L" devid="Lights (MI_02)" index="28"/>
|
||||||
|
<light name="FX-R" devid="Lights (MI_02)" index="29"/>
|
||||||
|
<light name="Start" devid="Lights (MI_02)" index="30"/>
|
||||||
|
<light name="Upper Left Speaker Avg R" devid="Lights (MI_02)" index="0"/>
|
||||||
|
<light name="Upper Left Speaker Avg G" devid="Lights (MI_02)" index="1"/>
|
||||||
|
<light name="Upper Left Speaker Avg B" devid="Lights (MI_02)" index="2"/>
|
||||||
|
<light name="Upper Right Speaker Avg R" devid="Lights (MI_02)" index="9"/>
|
||||||
|
<light name="Upper Right Speaker Avg G" devid="Lights (MI_02)" index="10"/>
|
||||||
|
<light name="Upper Right Speaker Avg B" devid="Lights (MI_02)" index="11"/>
|
||||||
|
<light name="Lower Left Speaker Avg R" devid="Lights (MI_02)" index="3"/>
|
||||||
|
<light name="Lower Left Speaker Avg G" devid="Lights (MI_02)" index="4"/>
|
||||||
|
<light name="Lower Left Speaker Avg B" devid="Lights (MI_02)" index="5"/>
|
||||||
|
<light name="Lower Right Speaker Avg R" devid="Lights (MI_02)" index="6"/>
|
||||||
|
<light name="Lower Right Speaker Avg G" devid="Lights (MI_02)" index="7"/>
|
||||||
|
<light name="Lower Right Speaker Avg B" devid="Lights (MI_02)" index="8"/>
|
||||||
|
<light name="Control Panel Avg R" devid="Lights (MI_02)" index="15"/>
|
||||||
|
<light name="Control Panel Avg G" devid="Lights (MI_02)" index="16"/>
|
||||||
|
<light name="Control Panel Avg B" devid="Lights (MI_02)" index="17"/>
|
||||||
|
</lights>
|
||||||
|
</template>
|
||||||
|
</templates>
|
||||||
@@ -41,8 +41,8 @@ done
|
|||||||
# settings
|
# settings
|
||||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
||||||
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
||||||
TOOLCHAIN_32="/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"
|
TOOLCHAIN_32="${TOOLCHAIN_32:-"/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"}"
|
||||||
TOOLCHAIN_64="/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"
|
TOOLCHAIN_64="${TOOLCHAIN_64:-"/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"}"
|
||||||
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
||||||
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
||||||
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||||
@@ -74,7 +74,7 @@ then
|
|||||||
BUILD_TYPE="Debug"
|
BUILD_TYPE="Debug"
|
||||||
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
||||||
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
||||||
DIST_NAME=$(echo ${DIST_NAME} | sed 's/\.[^.]*$/-dbg&/')
|
DIST_NAME=$(echo "${DIST_NAME}" | sed 's/\.[^.]*$/-dbg&/')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# determine number of cores
|
# determine number of cores
|
||||||
@@ -114,7 +114,7 @@ time (
|
|||||||
fi
|
fi
|
||||||
mkdir -p ${BUILDDIR_32}
|
mkdir -p ${BUILDDIR_32}
|
||||||
pushd ${BUILDDIR_32} > /dev/null
|
pushd ${BUILDDIR_32} > /dev/null
|
||||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
|
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_32}
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
# 64 bit
|
# 64 bit
|
||||||
@@ -127,7 +127,7 @@ time (
|
|||||||
fi
|
fi
|
||||||
mkdir -p ${BUILDDIR_64}
|
mkdir -p ${BUILDDIR_64}
|
||||||
pushd ${BUILDDIR_64} > /dev/null
|
pushd ${BUILDDIR_64} > /dev/null
|
||||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
|
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_64}
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -234,7 +234,7 @@ mkdir -p ${OUTDIR}/src
|
|||||||
if ((INCLUDE_SRC > 0))
|
if ((INCLUDE_SRC > 0))
|
||||||
then
|
then
|
||||||
echo "Generating source file archive..."
|
echo "Generating source file archive..."
|
||||||
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \
|
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > "${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz" 2>/dev/null || \
|
||||||
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -249,11 +249,11 @@ if ((DIST_ENABLE > 0))
|
|||||||
then
|
then
|
||||||
echo "Building dist..."
|
echo "Building dist..."
|
||||||
mkdir -p ${DIST_FOLDER}
|
mkdir -p ${DIST_FOLDER}
|
||||||
rm -rf ${DIST_FOLDER}/${DIST_NAME}
|
rm -rf "${DIST_FOLDER}/${DIST_NAME}"
|
||||||
pushd ${OUTDIR}/.. > /dev/null
|
pushd ${OUTDIR}/.. > /dev/null
|
||||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME}" spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
||||||
echo "Building extras..."
|
echo "Building extras..."
|
||||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x -z <<< "$DIST_COMMENT"
|
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS}" spice2x -z <<< "$DIST_COMMENT"
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
docker build --pull $PWD/external/docker -t spicetools/deps --platform linux/x86_64
|
docker build --pull "$PWD/external/docker" -t spicetools/deps --platform linux/x86_64
|
||||||
docker build --build-context gitroot=$PWD/../../.git . -t spicetools/spice:latest
|
docker build --build-context gitroot="$PWD/../../.git" . -t spicetools/spice:latest
|
||||||
docker run --rm -v $PWD/dist:/src/src/spice2x/dist -v $PWD/bin:/src/src/spice2x/bin -v $PWD/.ccache:/src/src/spice2x/.ccache spicetools/spice "$@"
|
docker run --rm -v "$PWD/dist:/src/src/spice2x/dist" -v "$PWD/bin:/src/src/spice2x/bin" -v "$PWD/.ccache:/src/src/spice2x/.ccache" spicetools/spice "$@"
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ IDR_LICENSES RCDATA "../licenses.txt"
|
|||||||
IDR_PATCHES RCDATA "../build/patches.json"
|
IDR_PATCHES RCDATA "../build/patches.json"
|
||||||
IDR_README RCDATA "../readme.txt"
|
IDR_README RCDATA "../readme.txt"
|
||||||
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
||||||
|
IDR_CONTROLLER_PRESETS_BUILTIN RCDATA "../build/controller_presets.xml"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ public:
|
|||||||
setMultiplier(1);
|
setMultiplier(1);
|
||||||
setRelativeMode(false);
|
setRelativeMode(false);
|
||||||
setDelayBufferDepth(0);
|
setDelayBufferDepth(0);
|
||||||
|
setLastState(0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void clearBindings() {
|
inline void clearBindings() {
|
||||||
|
|||||||
+50
-5
@@ -37,7 +37,11 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
|||||||
for (auto &bt : buttons) {
|
for (auto &bt : buttons) {
|
||||||
if (name == bt.getName()) {
|
if (name == bt.getName()) {
|
||||||
button_found = true;
|
button_found = true;
|
||||||
sorted.push_back(bt);
|
Button button_new = bt;
|
||||||
|
if (vkey_defaults) {
|
||||||
|
button_new.setVKeyDefault(vkey_defaults->at(index));
|
||||||
|
}
|
||||||
|
sorted.push_back(button_new);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,6 +51,7 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
|||||||
|
|
||||||
if (vkey_defaults) {
|
if (vkey_defaults) {
|
||||||
button.setVKey(vkey_defaults->at(index));
|
button.setVKey(vkey_defaults->at(index));
|
||||||
|
button.setVKeyDefault(vkey_defaults->at(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,10 +649,12 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
|||||||
case rawinput::HID: {
|
case rawinput::HID: {
|
||||||
|
|
||||||
// get value
|
// get value
|
||||||
if (inverted) {
|
if (index < device->hidInfo->value_states.size()) {
|
||||||
value = 1.f - device->hidInfo->value_states[index];
|
if (inverted) {
|
||||||
} else {
|
value = 1.f - device->hidInfo->value_states[index];
|
||||||
value = device->hidInfo->value_states[index];
|
} else {
|
||||||
|
value = device->hidInfo->value_states[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// deadzone
|
// deadzone
|
||||||
@@ -859,6 +866,44 @@ std::vector<Light> GameAPI::Lights::sortLights(
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<Light> sortLightsWithCategoryInternal(
|
||||||
|
const std::vector<Light> &lights,
|
||||||
|
const std::initializer_list<GameAPI::Lights::LightAndCategory> list)
|
||||||
|
{
|
||||||
|
std::vector<Light> sorted;
|
||||||
|
|
||||||
|
bool light_found;
|
||||||
|
for (auto &name : list) {
|
||||||
|
light_found = false;
|
||||||
|
|
||||||
|
for (auto &light : lights) {
|
||||||
|
if (name.light_name == light.getName()) {
|
||||||
|
light_found = true;
|
||||||
|
Light light_new = light;
|
||||||
|
light_new.setCategory(name.category_name);
|
||||||
|
sorted.push_back(light_new);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!light_found) {
|
||||||
|
sorted.emplace_back(name.light_name, name.category_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameAPI::Lights::sortLightsWithCategory(
|
||||||
|
std::vector<Light> *lights,
|
||||||
|
const std::initializer_list<LightAndCategory> list) {
|
||||||
|
|
||||||
|
if (lights) {
|
||||||
|
*lights = sortLightsWithCategoryInternal(*lights, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
|
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
|
||||||
|
|
||||||
// check device
|
// check device
|
||||||
|
|||||||
+15
-21
@@ -6,24 +6,20 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "option.h"
|
#include "option.h"
|
||||||
|
|
||||||
|
#include "button.h"
|
||||||
|
#include "analog.h"
|
||||||
|
#include "light.h"
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
struct Device;
|
struct Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Button;
|
|
||||||
class Analog;
|
|
||||||
class Light;
|
|
||||||
class Game;
|
class Game;
|
||||||
class Option;
|
class Option;
|
||||||
|
|
||||||
namespace GameAPI {
|
namespace GameAPI {
|
||||||
namespace Buttons {
|
namespace Buttons {
|
||||||
enum State {
|
|
||||||
BUTTON_PRESSED = true,
|
|
||||||
BUTTON_NOT_PRESSED = false
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the config and returns the buttons set by the user.
|
* Parses the config and returns the buttons set by the user.
|
||||||
*
|
*
|
||||||
@@ -113,21 +109,13 @@ namespace GameAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace Lights {
|
namespace Lights {
|
||||||
|
|
||||||
std::vector<Light> getLights(const std::string &game_name);
|
std::vector<Light> getLights(const std::string &game_name);
|
||||||
|
|
||||||
std::vector<Light> sortLights(
|
std::vector<Light> sortLights(
|
||||||
const std::vector<Light> &lights,
|
const std::vector<Light> &lights,
|
||||||
const std::vector<std::string> &light_names);
|
const std::vector<std::string> &light_names);
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void sortLights(std::vector<Light> *lights, T t) {
|
|
||||||
const std::vector<std::string> light_names { t };
|
|
||||||
|
|
||||||
if (lights) {
|
|
||||||
*lights = GameAPI::Lights::sortLights(*lights, light_names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename... Rest>
|
template<typename T, typename... Rest>
|
||||||
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
|
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
|
||||||
const std::vector<std::string> light_names { t, rest... };
|
const std::vector<std::string> light_names { t, rest... };
|
||||||
@@ -137,6 +125,16 @@ namespace GameAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LightAndCategory {
|
||||||
|
const std::string category_name;
|
||||||
|
const std::string light_name;
|
||||||
|
|
||||||
|
LightAndCategory(std::string category_name, std::string light_name) :
|
||||||
|
category_name(std::move(category_name)), light_name(std::move(light_name)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void sortLightsWithCategory(std::vector<Light> *lights, const std::initializer_list<LightAndCategory> list);
|
||||||
|
|
||||||
void writeLight(rawinput::Device *device, int index, float value);
|
void writeLight(rawinput::Device *device, int index, float value);
|
||||||
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
||||||
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
||||||
@@ -152,7 +150,3 @@ namespace GameAPI {
|
|||||||
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
|
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "button.h"
|
|
||||||
#include "analog.h"
|
|
||||||
#include "light.h"
|
|
||||||
|
|||||||
+50
-30
@@ -44,7 +44,7 @@ std::string Button::getVKeyString() {
|
|||||||
case 0x0C:
|
case 0x0C:
|
||||||
return "Clear";
|
return "Clear";
|
||||||
case 0x0D:
|
case 0x0D:
|
||||||
return "Enter";
|
return "Enter/Return";
|
||||||
case 0x10:
|
case 0x10:
|
||||||
return "Shift";
|
return "Shift";
|
||||||
case 0x11:
|
case 0x11:
|
||||||
@@ -58,8 +58,28 @@ std::string Button::getVKeyString() {
|
|||||||
return "Pause";
|
return "Pause";
|
||||||
case 0x14:
|
case 0x14:
|
||||||
return "Caps Lock";
|
return "Caps Lock";
|
||||||
|
case 0x15:
|
||||||
|
return "IME Kana/Hangul";
|
||||||
|
case 0x16:
|
||||||
|
return "IME On";
|
||||||
|
case 0x17:
|
||||||
|
return "IME Junja";
|
||||||
|
case 0x18:
|
||||||
|
return "IME Final";
|
||||||
|
case 0x19:
|
||||||
|
return "IME Hanja/Kanji";
|
||||||
|
case 0x1A:
|
||||||
|
return "IME Off";
|
||||||
case 0x1B:
|
case 0x1B:
|
||||||
return "Escape";
|
return "Escape";
|
||||||
|
case 0x1C:
|
||||||
|
return "IME Convert";
|
||||||
|
case 0x1D:
|
||||||
|
return "IME Nonconvert";
|
||||||
|
case 0x1E:
|
||||||
|
return "IME Accept";
|
||||||
|
case 0x1F:
|
||||||
|
return "IME Mode Change";
|
||||||
case 0x20:
|
case 0x20:
|
||||||
return "Space";
|
return "Space";
|
||||||
case 0x21:
|
case 0x21:
|
||||||
@@ -163,37 +183,37 @@ std::string Button::getVKeyString() {
|
|||||||
case 0x5D:
|
case 0x5D:
|
||||||
return "Apps";
|
return "Apps";
|
||||||
case 0x60:
|
case 0x60:
|
||||||
return "Num 0";
|
return "Numpad 0";
|
||||||
case 0x61:
|
case 0x61:
|
||||||
return "Num 1";
|
return "Numpad 1";
|
||||||
case 0x62:
|
case 0x62:
|
||||||
return "Num 2";
|
return "Numpad 2";
|
||||||
case 0x63:
|
case 0x63:
|
||||||
return "Num 3";
|
return "Numpad 3";
|
||||||
case 0x64:
|
case 0x64:
|
||||||
return "Num 4";
|
return "Numpad 4";
|
||||||
case 0x65:
|
case 0x65:
|
||||||
return "Num 5";
|
return "Numpad 5";
|
||||||
case 0x66:
|
case 0x66:
|
||||||
return "Num 6";
|
return "Numpad 6";
|
||||||
case 0x67:
|
case 0x67:
|
||||||
return "Num 7";
|
return "Numpad 7";
|
||||||
case 0x68:
|
case 0x68:
|
||||||
return "Num 8";
|
return "Numpad 8";
|
||||||
case 0x69:
|
case 0x69:
|
||||||
return "Num 9";
|
return "Numpad 9";
|
||||||
case 0x6A:
|
case 0x6A:
|
||||||
return "*";
|
return "Numpad *";
|
||||||
case 0x6B:
|
case 0x6B:
|
||||||
return "+";
|
return "Numpad +";
|
||||||
case 0x6C:
|
case 0x6C:
|
||||||
return "Seperator";
|
return "Numpad Seperator";
|
||||||
case 0x6D:
|
case 0x6D:
|
||||||
return "-";
|
return "Numpad -";
|
||||||
case 0x6E:
|
case 0x6E:
|
||||||
return ".";
|
return "Numpad .";
|
||||||
case 0x6F:
|
case 0x6F:
|
||||||
return "/";
|
return "Numpad /";
|
||||||
case 0x70:
|
case 0x70:
|
||||||
return "F1";
|
return "F1";
|
||||||
case 0x71:
|
case 0x71:
|
||||||
@@ -255,9 +275,9 @@ std::string Button::getVKeyString() {
|
|||||||
case 0xA3:
|
case 0xA3:
|
||||||
return "Right Control";
|
return "Right Control";
|
||||||
case 0xA4:
|
case 0xA4:
|
||||||
return "Left Menu";
|
return "Left Alt";
|
||||||
case 0xA5:
|
case 0xA5:
|
||||||
return "Right Menu";
|
return "Right Alt";
|
||||||
default:
|
default:
|
||||||
|
|
||||||
// check win API
|
// check win API
|
||||||
@@ -301,17 +321,17 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
case rawinput::MOUSE: {
|
case rawinput::MOUSE: {
|
||||||
const char *btn = "Unknown";
|
const char *btn = "Unknown";
|
||||||
static const char *MOUSE_NAMES[] = {
|
static const std::array<const char *, 8>MOUSE_NAMES = {
|
||||||
"Left Mouse",
|
"Left Mouse",
|
||||||
"Right Mouse",
|
"Right Mouse",
|
||||||
"Middle Mouse",
|
"Middle Mouse",
|
||||||
"Mouse 1",
|
"Mouse 1",
|
||||||
"Mouse 2",
|
"Mouse 2",
|
||||||
"Mouse 3",
|
"Mouse 3",
|
||||||
"Mouse 4",
|
"Mouse 4",
|
||||||
"Mouse 5",
|
"Mouse 5",
|
||||||
};
|
};
|
||||||
if (vKey < sizeof(MOUSE_NAMES)) {
|
if (vKey < MOUSE_NAMES.size()) {
|
||||||
btn = MOUSE_NAMES[vKey];
|
btn = MOUSE_NAMES[vKey];
|
||||||
}
|
}
|
||||||
return fmt::format("{} ({})", btn, device->desc);
|
return fmt::format("{} ({})", btn, device->desc);
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api.h"
|
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameAPI {
|
||||||
|
namespace Buttons {
|
||||||
|
enum State {
|
||||||
|
BUTTON_PRESSED = true,
|
||||||
|
BUTTON_NOT_PRESSED = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ButtonAnalogType {
|
enum ButtonAnalogType {
|
||||||
BAT_NONE = 0,
|
BAT_NONE = 0,
|
||||||
BAT_POSITIVE = 1,
|
BAT_POSITIVE = 1,
|
||||||
@@ -41,19 +49,24 @@ private:
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string device_identifier = "";
|
std::string device_identifier = "";
|
||||||
unsigned short vKey = INVALID_VKEY;
|
unsigned short vKey = INVALID_VKEY;
|
||||||
|
|
||||||
|
// default bindings are always naive
|
||||||
|
unsigned short vKey_default = INVALID_VKEY;
|
||||||
|
|
||||||
ButtonAnalogType analog_type = BAT_NONE;
|
ButtonAnalogType analog_type = BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
|
bool is_temporary = false;
|
||||||
|
|
||||||
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||||
float last_velocity = 0.f;
|
float last_velocity = 0.f;
|
||||||
unsigned short velocity_threshold = 0;
|
unsigned short velocity_threshold = 0;
|
||||||
|
|
||||||
std::string getVKeyString();
|
|
||||||
std::string getMidiNoteString();
|
std::string getMidiNoteString();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
std::string getVKeyString();
|
||||||
|
|
||||||
// overrides
|
// overrides
|
||||||
bool override_enabled = false;
|
bool override_enabled = false;
|
||||||
@@ -116,6 +129,14 @@ public:
|
|||||||
this->vKey = vKey;
|
this->vKey = vKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline unsigned short getVKeyDefault() const {
|
||||||
|
return this->vKey_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVKeyDefault(unsigned short vKey_default) {
|
||||||
|
this->vKey_default = vKey_default;
|
||||||
|
}
|
||||||
|
|
||||||
inline ButtonAnalogType getAnalogType() const {
|
inline ButtonAnalogType getAnalogType() const {
|
||||||
return this->analog_type;
|
return this->analog_type;
|
||||||
}
|
}
|
||||||
@@ -164,6 +185,14 @@ public:
|
|||||||
this->last_velocity = last_velocity;
|
this->last_velocity = last_velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isTemporary() const {
|
||||||
|
return this->is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setTemporary(bool is_temporary) {
|
||||||
|
this->is_temporary = is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
inline unsigned short getVelocityThreshold() const {
|
inline unsigned short getVelocityThreshold() const {
|
||||||
return this->velocity_threshold;
|
return this->velocity_threshold;
|
||||||
}
|
}
|
||||||
@@ -172,6 +201,20 @@ public:
|
|||||||
this->velocity_threshold = velocity_threshold;
|
this->velocity_threshold = velocity_threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isValid() {
|
||||||
|
// temporarily created by UI
|
||||||
|
if (isTemporary()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing is set
|
||||||
|
if (getDeviceIdentifier().empty() && getVKey() == INVALID_VKEY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void getMidiVKey(int& channel, int& index);
|
void getMidiVKey(int& channel, int& index);
|
||||||
void setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
|
void setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "cfg/button.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This code absolutely sucks.
|
* This code absolutely sucks.
|
||||||
@@ -10,6 +11,14 @@
|
|||||||
// settings
|
// settings
|
||||||
std::string CONFIG_PATH_OVERRIDE = "";
|
std::string CONFIG_PATH_OVERRIDE = "";
|
||||||
|
|
||||||
|
#define CFG_DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if CFG_DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
/// Constructor ///
|
/// Constructor ///
|
||||||
@@ -510,8 +519,10 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tinyxml2::XMLElement *gameButtonNode = nullptr;
|
||||||
|
|
||||||
// iterate button nodes
|
// iterate button nodes
|
||||||
tinyxml2::XMLElement *gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||||
int button_count = 0;
|
int button_count = 0;
|
||||||
while (gameButtonNode != nullptr) {
|
while (gameButtonNode != nullptr) {
|
||||||
const char *buttonNodeName = gameButtonNode->Attribute("name");
|
const char *buttonNodeName = gameButtonNode->Attribute("name");
|
||||||
@@ -574,6 +585,39 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trim the trailing invalid (unbound) alternatives
|
||||||
|
tinyxml2::XMLElement *to_delete = nullptr;
|
||||||
|
gameButtonNode = gameButtonsNode->LastChildElement("button");
|
||||||
|
while (gameButtonNode != nullptr) {
|
||||||
|
const char *name = gameButtonNode->Attribute("name");
|
||||||
|
const char *devid = gameButtonNode->Attribute("devid");
|
||||||
|
int vkey = INVALID_VKEY;
|
||||||
|
gameButtonNode->QueryIntAttribute("vkey", &vkey);
|
||||||
|
|
||||||
|
// wrong button, ignore and keep iterating backwards
|
||||||
|
if (name == nullptr || std::string(name) != button.getName()) {
|
||||||
|
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// found a node for this button, which means to_delete is not the first one
|
||||||
|
// delete the node for to_delete
|
||||||
|
if (to_delete != nullptr) {
|
||||||
|
log_debug("cfg", "deleting excessive button binding for {}", name);
|
||||||
|
gameButtonsNode->DeleteChild(to_delete);
|
||||||
|
to_delete = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((devid == nullptr || strlen(devid) == 0) && vkey == INVALID_VKEY) {
|
||||||
|
// unbound button with no device ID, make a note of it so it can be deleted later
|
||||||
|
to_delete = gameButtonNode;
|
||||||
|
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||||
|
} else {
|
||||||
|
// valid binding found; stop iterating
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if button was not found
|
// check if button was not found
|
||||||
if (button_count == 0) {
|
if (button_count == 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -760,6 +804,37 @@ bool Config::updateBinding(const Game &game, const Light &light, int alternative
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trim the trailing invalid (unbound) alternatives
|
||||||
|
tinyxml2::XMLElement *to_delete = nullptr;
|
||||||
|
gameLightNode = gameLightsNode->LastChildElement("light");
|
||||||
|
while (gameLightNode != nullptr) {
|
||||||
|
const char *name = gameLightNode->Attribute("name");
|
||||||
|
const char *devid = gameLightNode->Attribute("devid");
|
||||||
|
|
||||||
|
// wrong light, ignore and keep iterating backwards
|
||||||
|
if (name == nullptr || std::string(name) != light.getName()) {
|
||||||
|
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// found a node for this button, which means to_delete is not the first one
|
||||||
|
// delete the node for to_delete
|
||||||
|
if (to_delete != nullptr) {
|
||||||
|
log_debug("cfg", "deleting excessive light binding for {}", name);
|
||||||
|
gameLightsNode->DeleteChild(to_delete);
|
||||||
|
to_delete = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devid == nullptr || strlen(devid) == 0) {
|
||||||
|
// unbound light, make a note of it so it can be deleted later
|
||||||
|
to_delete = gameLightNode;
|
||||||
|
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||||
|
} else {
|
||||||
|
// valid binding found; stop iterating
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if light was not found
|
// check if light was not found
|
||||||
if (light_count == 0) {
|
if (light_count == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -0,0 +1,460 @@
|
|||||||
|
#include "overlay/windows/controller_presets.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "cfg/resource.h"
|
||||||
|
#include "external/tinyxml2/tinyxml2.h"
|
||||||
|
#include "util/fileutils.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/resutils.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
static std::filesystem::path get_templates_path() {
|
||||||
|
return fileutils::get_config_file_path("ControllerPresets", "spicetools_presets.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_button_entry(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parent,
|
||||||
|
const std::string &name, const TemplateButtonEntry &entry) {
|
||||||
|
auto *el = doc.NewElement("button");
|
||||||
|
el->SetAttribute("name", name.c_str());
|
||||||
|
el->SetAttribute("vkey", entry.vKey);
|
||||||
|
el->SetAttribute("analogtype", (int)entry.analog_type);
|
||||||
|
el->SetAttribute("devid", entry.device_identifier.c_str());
|
||||||
|
el->SetAttribute("invert", entry.invert);
|
||||||
|
el->SetAttribute("debounce_up", entry.debounce_up);
|
||||||
|
el->SetAttribute("debounce_down", entry.debounce_down);
|
||||||
|
el->SetAttribute("velocity_threshold", entry.velocity_threshold);
|
||||||
|
parent->InsertEndChild(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TemplateButtonEntry read_button_entry(tinyxml2::XMLElement *el) {
|
||||||
|
TemplateButtonEntry entry;
|
||||||
|
int vkey = INVALID_VKEY;
|
||||||
|
el->QueryIntAttribute("vkey", &vkey);
|
||||||
|
entry.vKey = (unsigned short)vkey;
|
||||||
|
|
||||||
|
int atype = 0;
|
||||||
|
el->QueryIntAttribute("analogtype", &atype);
|
||||||
|
entry.analog_type = (ButtonAnalogType)atype;
|
||||||
|
|
||||||
|
const char *devid = el->Attribute("devid");
|
||||||
|
entry.device_identifier = devid ? devid : "";
|
||||||
|
|
||||||
|
el->QueryBoolAttribute("invert", &entry.invert);
|
||||||
|
el->QueryDoubleAttribute("debounce_up", &entry.debounce_up);
|
||||||
|
el->QueryDoubleAttribute("debounce_down", &entry.debounce_down);
|
||||||
|
|
||||||
|
int vel = 0;
|
||||||
|
el->QueryIntAttribute("velocity_threshold", &vel);
|
||||||
|
entry.velocity_threshold = (unsigned short)vel;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_analog(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parent,
|
||||||
|
const TemplateAnalogBinding &analog) {
|
||||||
|
auto *el = doc.NewElement("analog");
|
||||||
|
el->SetAttribute("name", analog.name.c_str());
|
||||||
|
el->SetAttribute("devid", analog.device_identifier.c_str());
|
||||||
|
el->SetAttribute("index", analog.index);
|
||||||
|
el->SetAttribute("sensivity", analog.sensitivity); // keep typo for compat
|
||||||
|
el->SetAttribute("deadzone", analog.deadzone);
|
||||||
|
el->SetAttribute("deadzone_mirror", analog.deadzone_mirror);
|
||||||
|
el->SetAttribute("invert", analog.invert);
|
||||||
|
el->SetAttribute("smoothing", analog.smoothing);
|
||||||
|
el->SetAttribute("multiplier", analog.multiplier);
|
||||||
|
el->SetAttribute("relative", analog.relative_mode);
|
||||||
|
el->SetAttribute("delay", analog.delay_buffer_depth);
|
||||||
|
parent->InsertEndChild(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TemplateAnalogBinding read_analog(tinyxml2::XMLElement *el) {
|
||||||
|
TemplateAnalogBinding a;
|
||||||
|
const char *name = el->Attribute("name");
|
||||||
|
a.name = name ? name : "";
|
||||||
|
|
||||||
|
const char *devid = el->Attribute("devid");
|
||||||
|
a.device_identifier = devid ? devid : "";
|
||||||
|
|
||||||
|
int idx = 0xFF;
|
||||||
|
el->QueryIntAttribute("index", &idx);
|
||||||
|
a.index = (unsigned short)idx;
|
||||||
|
|
||||||
|
el->QueryFloatAttribute("sensivity", &a.sensitivity); // keep typo
|
||||||
|
el->QueryFloatAttribute("deadzone", &a.deadzone);
|
||||||
|
el->QueryBoolAttribute("deadzone_mirror", &a.deadzone_mirror);
|
||||||
|
el->QueryBoolAttribute("invert", &a.invert);
|
||||||
|
el->QueryBoolAttribute("smoothing", &a.smoothing);
|
||||||
|
el->QueryIntAttribute("multiplier", &a.multiplier);
|
||||||
|
el->QueryBoolAttribute("relative", &a.relative_mode);
|
||||||
|
el->QueryIntAttribute("delay", &a.delay_buffer_depth);
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_light_entry(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parent,
|
||||||
|
const std::string &name, const TemplateLightEntry &entry) {
|
||||||
|
auto *el = doc.NewElement("light");
|
||||||
|
el->SetAttribute("name", name.c_str());
|
||||||
|
el->SetAttribute("devid", entry.device_identifier.c_str());
|
||||||
|
el->SetAttribute("index", entry.index);
|
||||||
|
parent->InsertEndChild(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TemplateLightEntry read_light_entry(tinyxml2::XMLElement *el) {
|
||||||
|
TemplateLightEntry entry;
|
||||||
|
const char *devid = el->Attribute("devid");
|
||||||
|
entry.device_identifier = devid ? devid : "";
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
el->QueryIntAttribute("index", &idx);
|
||||||
|
entry.index = (unsigned int)idx;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse all templates from an already-loaded XMLDocument.
|
||||||
|
// is_builtin controls the is_builtin flag on each returned template.
|
||||||
|
static std::vector<ControllerTemplate> load_templates_from_doc(
|
||||||
|
tinyxml2::XMLDocument &doc, bool is_builtin) {
|
||||||
|
std::vector<ControllerTemplate> templates;
|
||||||
|
|
||||||
|
auto *root = doc.RootElement();
|
||||||
|
if (!root) {
|
||||||
|
return templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *tmpl_el = root->FirstChildElement("template");
|
||||||
|
while (tmpl_el) {
|
||||||
|
ControllerTemplate tmpl;
|
||||||
|
const char *name = tmpl_el->Attribute("name");
|
||||||
|
const char *game = tmpl_el->Attribute("game");
|
||||||
|
tmpl.name = name ? name : "";
|
||||||
|
tmpl.game_name = game ? game : "";
|
||||||
|
tmpl.is_builtin = is_builtin;
|
||||||
|
|
||||||
|
// buttons
|
||||||
|
auto *buttons_el = tmpl_el->FirstChildElement("buttons");
|
||||||
|
if (buttons_el) {
|
||||||
|
auto *btn_el = buttons_el->FirstChildElement("button");
|
||||||
|
while (btn_el) {
|
||||||
|
const char *btn_name = btn_el->Attribute("name");
|
||||||
|
std::string btn_name_str = btn_name ? btn_name : "";
|
||||||
|
|
||||||
|
TemplateButtonBinding *binding = nullptr;
|
||||||
|
for (auto &b : tmpl.buttons) {
|
||||||
|
if (b.name == btn_name_str) { binding = &b; break; }
|
||||||
|
}
|
||||||
|
if (!binding) {
|
||||||
|
tmpl.buttons.push_back({btn_name_str, read_button_entry(btn_el), {}});
|
||||||
|
} else {
|
||||||
|
binding->alternatives.push_back(read_button_entry(btn_el));
|
||||||
|
}
|
||||||
|
btn_el = btn_el->NextSiblingElement("button");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keypad buttons
|
||||||
|
auto *kp_el = tmpl_el->FirstChildElement("keypad_buttons");
|
||||||
|
if (kp_el) {
|
||||||
|
auto *btn_el = kp_el->FirstChildElement("button");
|
||||||
|
while (btn_el) {
|
||||||
|
const char *btn_name = btn_el->Attribute("name");
|
||||||
|
std::string btn_name_str = btn_name ? btn_name : "";
|
||||||
|
|
||||||
|
TemplateButtonBinding *binding = nullptr;
|
||||||
|
for (auto &b : tmpl.keypad_buttons) {
|
||||||
|
if (b.name == btn_name_str) { binding = &b; break; }
|
||||||
|
}
|
||||||
|
if (!binding) {
|
||||||
|
tmpl.keypad_buttons.push_back({btn_name_str, read_button_entry(btn_el), {}});
|
||||||
|
} else {
|
||||||
|
binding->alternatives.push_back(read_button_entry(btn_el));
|
||||||
|
}
|
||||||
|
btn_el = btn_el->NextSiblingElement("button");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// analogs
|
||||||
|
auto *analogs_el = tmpl_el->FirstChildElement("analogs");
|
||||||
|
if (analogs_el) {
|
||||||
|
auto *analog_el = analogs_el->FirstChildElement("analog");
|
||||||
|
while (analog_el) {
|
||||||
|
tmpl.analogs.push_back(read_analog(analog_el));
|
||||||
|
analog_el = analog_el->NextSiblingElement("analog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lights
|
||||||
|
auto *lights_el = tmpl_el->FirstChildElement("lights");
|
||||||
|
if (lights_el) {
|
||||||
|
auto *light_el = lights_el->FirstChildElement("light");
|
||||||
|
while (light_el) {
|
||||||
|
const char *light_name = light_el->Attribute("name");
|
||||||
|
std::string light_name_str = light_name ? light_name : "";
|
||||||
|
|
||||||
|
TemplateLightBinding *binding = nullptr;
|
||||||
|
for (auto &l : tmpl.lights) {
|
||||||
|
if (l.name == light_name_str) { binding = &l; break; }
|
||||||
|
}
|
||||||
|
if (!binding) {
|
||||||
|
tmpl.lights.push_back({light_name_str, read_light_entry(light_el), {}});
|
||||||
|
} else {
|
||||||
|
binding->alternatives.push_back(read_light_entry(light_el));
|
||||||
|
}
|
||||||
|
light_el = light_el->NextSiblingElement("light");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templates.push_back(std::move(tmpl));
|
||||||
|
tmpl_el = tmpl_el->NextSiblingElement("template");
|
||||||
|
}
|
||||||
|
|
||||||
|
return templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<ControllerTemplate> load_builtin_templates() {
|
||||||
|
auto xml = resutil::load_file_string(IDR_CONTROLLER_PRESETS_BUILTIN);
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
auto err = doc.Parse(xml.c_str());
|
||||||
|
if (err != tinyxml2::XML_SUCCESS) {
|
||||||
|
log_warning("templates", "failed to parse builtin templates XML: {}", (int)err);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return load_templates_from_doc(doc, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ControllerTemplate> load_user_templates() {
|
||||||
|
std::vector<ControllerTemplate> templates;
|
||||||
|
auto path = get_templates_path();
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||||
|
return templates;
|
||||||
|
}
|
||||||
|
return load_templates_from_doc(doc, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool save_user_template(
|
||||||
|
const ControllerTemplate &tmpl,
|
||||||
|
const bool save_buttons,
|
||||||
|
const bool save_keypads,
|
||||||
|
const bool save_analogs,
|
||||||
|
const bool save_lights) {
|
||||||
|
|
||||||
|
auto path = get_templates_path();
|
||||||
|
auto path_tmp = path;
|
||||||
|
path_tmp.replace_extension(L"tmp");
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||||
|
auto *decl = doc.NewDeclaration();
|
||||||
|
doc.InsertFirstChild(decl);
|
||||||
|
auto *root = doc.NewElement("templates");
|
||||||
|
doc.InsertEndChild(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *root = doc.RootElement();
|
||||||
|
if (!root) {
|
||||||
|
root = doc.NewElement("templates");
|
||||||
|
doc.InsertEndChild(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove existing template with same name and game
|
||||||
|
auto *existing = root->FirstChildElement("template");
|
||||||
|
while (existing) {
|
||||||
|
auto *next = existing->NextSiblingElement("template");
|
||||||
|
const char *ename = existing->Attribute("name");
|
||||||
|
const char *egame = existing->Attribute("game");
|
||||||
|
if (ename && egame &&
|
||||||
|
std::string(ename) == tmpl.name &&
|
||||||
|
std::string(egame) == tmpl.game_name) {
|
||||||
|
root->DeleteChild(existing);
|
||||||
|
}
|
||||||
|
existing = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *tmpl_el = doc.NewElement("template");
|
||||||
|
tmpl_el->SetAttribute("name", tmpl.name.c_str());
|
||||||
|
tmpl_el->SetAttribute("game", tmpl.game_name.c_str());
|
||||||
|
|
||||||
|
// buttons — skip unbound entries
|
||||||
|
auto *buttons_el = doc.NewElement("buttons");
|
||||||
|
if (save_buttons) {
|
||||||
|
for (auto &btn : tmpl.buttons) {
|
||||||
|
if (!btn.primary.is_unbound()) {
|
||||||
|
write_button_entry(doc, buttons_el, btn.name, btn.primary);
|
||||||
|
}
|
||||||
|
for (auto &alt : btn.alternatives) {
|
||||||
|
if (!alt.is_unbound()) {
|
||||||
|
write_button_entry(doc, buttons_el, btn.name, alt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl_el->InsertEndChild(buttons_el);
|
||||||
|
|
||||||
|
// keypad buttons — skip unbound entries
|
||||||
|
if (!tmpl.keypad_buttons.empty()) {
|
||||||
|
auto *kp_el = doc.NewElement("keypad_buttons");
|
||||||
|
if (save_keypads) {
|
||||||
|
for (auto &btn : tmpl.keypad_buttons) {
|
||||||
|
if (!btn.primary.is_unbound()) {
|
||||||
|
write_button_entry(doc, kp_el, btn.name, btn.primary);
|
||||||
|
}
|
||||||
|
for (auto &alt : btn.alternatives) {
|
||||||
|
if (!alt.is_unbound()) {
|
||||||
|
write_button_entry(doc, kp_el, btn.name, alt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl_el->InsertEndChild(kp_el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// analogs — skip unbound entries
|
||||||
|
auto *analogs_el = doc.NewElement("analogs");
|
||||||
|
if (save_analogs) {
|
||||||
|
for (auto &a : tmpl.analogs) {
|
||||||
|
if (!a.is_unbound()) {
|
||||||
|
write_analog(doc, analogs_el, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl_el->InsertEndChild(analogs_el);
|
||||||
|
|
||||||
|
// lights — skip unbound entries
|
||||||
|
auto *lights_el = doc.NewElement("lights");
|
||||||
|
if (save_lights) {
|
||||||
|
for (auto &l : tmpl.lights) {
|
||||||
|
if (!l.primary.is_unbound()) {
|
||||||
|
write_light_entry(doc, lights_el, l.name, l.primary);
|
||||||
|
}
|
||||||
|
for (auto &alt : l.alternatives) {
|
||||||
|
if (!alt.is_unbound()) {
|
||||||
|
write_light_entry(doc, lights_el, l.name, alt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl_el->InsertEndChild(lights_el);
|
||||||
|
|
||||||
|
root->InsertEndChild(tmpl_el);
|
||||||
|
|
||||||
|
// ensure directory exists
|
||||||
|
if (!path.parent_path().empty() && !std::filesystem::exists(path.parent_path())) {
|
||||||
|
fileutils::dir_create_recursive(path.parent_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
// save atomically via temp file
|
||||||
|
auto save_err = doc.SaveFile(path_tmp.c_str());
|
||||||
|
if (save_err != tinyxml2::XML_SUCCESS) {
|
||||||
|
log_warning("templates", "failed to save templates file: {}", (int)save_err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::rename(path_tmp, path, ec);
|
||||||
|
if (ec) {
|
||||||
|
log_warning("templates", "failed to rename templates file: {}", ec.message());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delete_user_template(const std::string &game_name, const std::string &template_name) {
|
||||||
|
auto path = get_templates_path();
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) return false;
|
||||||
|
|
||||||
|
auto *root = doc.RootElement();
|
||||||
|
if (!root) return false;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
auto *tmpl_el = root->FirstChildElement("template");
|
||||||
|
while (tmpl_el) {
|
||||||
|
auto *next = tmpl_el->NextSiblingElement("template");
|
||||||
|
const char *ename = tmpl_el->Attribute("name");
|
||||||
|
const char *egame = tmpl_el->Attribute("game");
|
||||||
|
if (ename && egame &&
|
||||||
|
std::string(ename) == template_name &&
|
||||||
|
std::string(egame) == game_name) {
|
||||||
|
root->DeleteChild(tmpl_el);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
tmpl_el = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
auto path_tmp = path;
|
||||||
|
path_tmp.replace_extension(L"tmp");
|
||||||
|
if (doc.SaveFile(path_tmp.c_str()) == tinyxml2::XML_SUCCESS) {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::rename(path_tmp, path, ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rename_user_template(const std::string &game_name,
|
||||||
|
const std::string &old_name, const std::string &new_name) {
|
||||||
|
auto path = get_templates_path();
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) return false;
|
||||||
|
|
||||||
|
auto *root = doc.RootElement();
|
||||||
|
if (!root) return false;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
auto *tmpl_el = root->FirstChildElement("template");
|
||||||
|
while (tmpl_el) {
|
||||||
|
const char *ename = tmpl_el->Attribute("name");
|
||||||
|
const char *egame = tmpl_el->Attribute("game");
|
||||||
|
if (ename && egame &&
|
||||||
|
std::string(ename) == old_name &&
|
||||||
|
std::string(egame) == game_name) {
|
||||||
|
tmpl_el->SetAttribute("name", new_name.c_str());
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tmpl_el = tmpl_el->NextSiblingElement("template");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
auto path_tmp = path;
|
||||||
|
path_tmp.replace_extension(L"tmp");
|
||||||
|
if (doc.SaveFile(path_tmp.c_str()) == tinyxml2::XML_SUCCESS) {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::rename(path_tmp, path, ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ControllerTemplate> get_templates_for_game(const std::string &game_name) {
|
||||||
|
std::vector<ControllerTemplate> result;
|
||||||
|
|
||||||
|
log_misc("templates", "loading built-in templates for {}", game_name);
|
||||||
|
for (auto &t : load_builtin_templates()) {
|
||||||
|
if (t.game_name == game_name) {
|
||||||
|
result.push_back(std::move(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_misc("templates", "loading user templates for {}", game_name);
|
||||||
|
for (auto &t : load_user_templates()) {
|
||||||
|
if (t.game_name == game_name) {
|
||||||
|
result.push_back(std::move(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,8 +11,10 @@ class Light {
|
|||||||
private:
|
private:
|
||||||
std::vector<Light> alternatives;
|
std::vector<Light> alternatives;
|
||||||
std::string lightName;
|
std::string lightName;
|
||||||
|
std::string lightCategory;
|
||||||
std::string deviceIdentifier = "";
|
std::string deviceIdentifier = "";
|
||||||
unsigned int index = 0;
|
unsigned int index = 0;
|
||||||
|
bool is_temporary = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float last_state = 0.f;
|
float last_state = 0.f;
|
||||||
@@ -22,6 +24,8 @@ public:
|
|||||||
float override_state = 0.f;
|
float override_state = 0.f;
|
||||||
|
|
||||||
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
|
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
|
||||||
|
explicit Light(std::string lightName, std::string lightCategory) :
|
||||||
|
lightName(std::move(lightName)), lightCategory(std::move(lightCategory)) {};
|
||||||
|
|
||||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||||
|
|
||||||
@@ -50,6 +54,14 @@ public:
|
|||||||
return this->lightName;
|
return this->lightName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const std::string &getCategory() const {
|
||||||
|
return this->lightCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setCategory(std::string category) {
|
||||||
|
this->lightCategory = std::move(category);
|
||||||
|
}
|
||||||
|
|
||||||
inline const std::string &getDeviceIdentifier() const {
|
inline const std::string &getDeviceIdentifier() const {
|
||||||
return this->deviceIdentifier;
|
return this->deviceIdentifier;
|
||||||
}
|
}
|
||||||
@@ -65,4 +77,26 @@ public:
|
|||||||
inline void setIndex(unsigned int index) {
|
inline void setIndex(unsigned int index) {
|
||||||
this->index = index;
|
this->index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isTemporary() const {
|
||||||
|
return this->is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setTemporary(bool is_temporary) {
|
||||||
|
this->is_temporary = is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isValid() {
|
||||||
|
// temporarily created by UI
|
||||||
|
if (isTemporary()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing is set
|
||||||
|
if (getDeviceIdentifier().empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ enum class OptionType {
|
|||||||
Hex,
|
Hex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class OptionPickerType {
|
||||||
|
None,
|
||||||
|
AsioDriver,
|
||||||
|
EACard,
|
||||||
|
CpuAffinity,
|
||||||
|
FilePath,
|
||||||
|
DirectoryPath,
|
||||||
|
};
|
||||||
|
|
||||||
struct OptionDefinition {
|
struct OptionDefinition {
|
||||||
std::string title;
|
std::string title;
|
||||||
// unique identifier used for flag matching but also stored in config files
|
// unique identifier used for flag matching but also stored in config files
|
||||||
@@ -32,6 +41,10 @@ struct OptionDefinition {
|
|||||||
bool sensitive = false;
|
bool sensitive = false;
|
||||||
std::vector<std::pair<std::string, std::string>> elements = {};
|
std::vector<std::pair<std::string, std::string>> elements = {};
|
||||||
bool disabled = false;
|
bool disabled = false;
|
||||||
|
OptionPickerType picker = OptionPickerType::None;
|
||||||
|
|
||||||
|
// for OptionPickerType::FilePath
|
||||||
|
std::string file_extension = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class Option {
|
class Option {
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
#define IDR_PATCHES 132
|
#define IDR_PATCHES 132
|
||||||
#define IDR_README 133
|
#define IDR_README 133
|
||||||
#define IDR_DSEGFONT 134
|
#define IDR_DSEGFONT 134
|
||||||
|
#define IDR_CONTROLLER_PRESETS_BUILTIN 135
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -69,6 +73,7 @@ namespace cfg {
|
|||||||
uint32_t init_client_height = 0;
|
uint32_t init_client_height = 0;
|
||||||
float init_client_aspect_ratio = 1.f;
|
float init_client_aspect_ratio = 1.f;
|
||||||
uint32_t init_window_style = 0;
|
uint32_t init_window_style = 0;
|
||||||
|
uint32_t init_window_style_ex = 0;
|
||||||
uint32_t window_deco_width = 0;
|
uint32_t window_deco_width = 0;
|
||||||
uint32_t window_deco_height = 0;
|
uint32_t window_deco_height = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -16,15 +18,19 @@ namespace games::ccj {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct AIO_SCI_COMM {
|
struct AIO_SCI_COMM {
|
||||||
|
uint8_t data[0xf8];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB2 {
|
struct AIO_NMGR_IOB2 {
|
||||||
|
uint8_t data[0xe30];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_TBS {
|
struct AIO_IOB2_BI2X_TBS {
|
||||||
|
uint8_t data[0x4680];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_WRFIRM {
|
struct AIO_IOB2_BI2X_WRFIRM {
|
||||||
|
uint8_t data[0x20f48];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB__NODEINFO {
|
struct AIO_NMGR_IOB__NODEINFO {
|
||||||
@@ -72,6 +78,12 @@ namespace games::ccj {
|
|||||||
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// checked with UJK-001-2024060400
|
||||||
|
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_TBS) == 0x4680);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||||
|
|
||||||
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value);
|
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -621,4 +633,6 @@ namespace games::ccj {
|
|||||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
namespace games::ccj {
|
namespace games::ccj {
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -68,9 +68,13 @@ namespace games::ccj {
|
|||||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||||
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
// insert BI2X hooks
|
// insert BI2X hooks
|
||||||
bi2x_hook_init();
|
bi2x_hook_init();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// insert trackball hooks
|
// insert trackball hooks
|
||||||
trackball_hook_init();
|
trackball_hook_init();
|
||||||
|
|
||||||
|
|||||||
@@ -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"});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+114
-110
@@ -41,136 +41,140 @@ std::vector<Light> &games::ddr::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"P1 Foot Left",
|
{
|
||||||
"P1 Foot Up",
|
{"General", "P1 Foot Left"},
|
||||||
"P1 Foot Right",
|
{"General", "P1 Foot Up"},
|
||||||
"P1 Foot Down",
|
{"General", "P1 Foot Right"},
|
||||||
"P2 Foot Left",
|
{"General", "P1 Foot Down"},
|
||||||
"P2 Foot Up",
|
{"General", "P2 Foot Left"},
|
||||||
"P2 Foot Right",
|
{"General", "P2 Foot Up"},
|
||||||
"P2 Foot Down",
|
{"General", "P2 Foot Right"},
|
||||||
"Spot Red",
|
{"General", "P2 Foot Down"},
|
||||||
"Spot Blue",
|
|
||||||
"Top Spot Red",
|
|
||||||
"Top Spot Blue",
|
|
||||||
"P1 Halogen Upper",
|
|
||||||
"P1 Halogen Lower",
|
|
||||||
"P2 Halogen Upper",
|
|
||||||
"P2 Halogen Lower",
|
|
||||||
"P1 Button",
|
|
||||||
"P2 Button",
|
|
||||||
"Neon",
|
|
||||||
"HD P1 Start",
|
|
||||||
"HD P1 Menu Left-Right",
|
|
||||||
"HD P1 Menu Up-Down",
|
|
||||||
"HD P2 Start",
|
|
||||||
"HD P2 Menu Left-Right",
|
|
||||||
"HD P2 Menu Up-Down",
|
|
||||||
"HD P1 Speaker F R",
|
|
||||||
"HD P1 Speaker F G",
|
|
||||||
"HD P1 Speaker F B",
|
|
||||||
"HD P1 Speaker W R",
|
|
||||||
"HD P1 Speaker W G",
|
|
||||||
"HD P1 Speaker W B",
|
|
||||||
"HD P2 Speaker F R",
|
|
||||||
"HD P2 Speaker F G",
|
|
||||||
"HD P2 Speaker F B",
|
|
||||||
"HD P2 Speaker W R",
|
|
||||||
"HD P2 Speaker W G",
|
|
||||||
"HD P2 Speaker W B",
|
|
||||||
|
|
||||||
"WHITE Speaker Top R",
|
{"SD", "Spot Red"},
|
||||||
"WHITE Speaker Top G",
|
{"SD", "Spot Blue"},
|
||||||
"WHITE Speaker Top B",
|
{"SD", "Top Spot Red"},
|
||||||
"WHITE Speaker Bottom R",
|
{"SD", "Top Spot Blue"},
|
||||||
"WHITE Speaker Bottom G",
|
{"SD", "P1 Halogen Upper"},
|
||||||
"WHITE Speaker Bottom B",
|
{"SD", "P1 Halogen Lower"},
|
||||||
"WHITE Woofer R",
|
{"SD", "P2 Halogen Upper"},
|
||||||
"WHITE Woofer G",
|
{"SD", "P2 Halogen Lower"},
|
||||||
"WHITE Woofer B",
|
{"SD", "P1 Button"},
|
||||||
|
{"SD", "P2 Button"},
|
||||||
|
{"SD", "Neon"},
|
||||||
|
|
||||||
"GOLD P1 Menu Start",
|
{"HD", "HD P1 Start"},
|
||||||
"GOLD P1 Menu Up",
|
{"HD", "HD P1 Menu Left-Right"},
|
||||||
"GOLD P1 Menu Down",
|
{"HD", "HD P1 Menu Up-Down"},
|
||||||
"GOLD P1 Menu Left",
|
{"HD", "HD P2 Start"},
|
||||||
"GOLD P1 Menu Right",
|
{"HD", "HD P2 Menu Left-Right"},
|
||||||
|
{"HD", "HD P2 Menu Up-Down"},
|
||||||
|
{"HD", "HD P1 Speaker F R"},
|
||||||
|
{"HD", "HD P1 Speaker F G"},
|
||||||
|
{"HD", "HD P1 Speaker F B"},
|
||||||
|
{"HD", "HD P1 Speaker W R"},
|
||||||
|
{"HD", "HD P1 Speaker W G"},
|
||||||
|
{"HD", "HD P1 Speaker W B"},
|
||||||
|
{"HD", "HD P2 Speaker F R"},
|
||||||
|
{"HD", "HD P2 Speaker F G"},
|
||||||
|
{"HD", "HD P2 Speaker F B"},
|
||||||
|
{"HD", "HD P2 Speaker W R"},
|
||||||
|
{"HD", "HD P2 Speaker W G"},
|
||||||
|
{"HD", "HD P2 Speaker W B"},
|
||||||
|
|
||||||
"GOLD P2 Menu Start",
|
{"WHITE", "WHITE Speaker Top R"},
|
||||||
"GOLD P2 Menu Up",
|
{"WHITE", "WHITE Speaker Top G"},
|
||||||
"GOLD P2 Menu Down",
|
{"WHITE", "WHITE Speaker Top B"},
|
||||||
"GOLD P2 Menu Left",
|
{"WHITE", "WHITE Speaker Bottom R"},
|
||||||
"GOLD P2 Menu Right",
|
{"WHITE", "WHITE Speaker Bottom G"},
|
||||||
|
{"WHITE", "WHITE Speaker Bottom B"},
|
||||||
|
{"WHITE", "WHITE Woofer R"},
|
||||||
|
{"WHITE", "WHITE Woofer G"},
|
||||||
|
{"WHITE", "WHITE Woofer B"},
|
||||||
|
|
||||||
"GOLD Title Panel Left",
|
{"GOLD", "GOLD P1 Menu Start"},
|
||||||
"GOLD Title Panel Center",
|
{"GOLD", "GOLD P1 Menu Up"},
|
||||||
"GOLD Title Panel Right",
|
{"GOLD", "GOLD P1 Menu Down"},
|
||||||
|
{"GOLD", "GOLD P1 Menu Left"},
|
||||||
|
{"GOLD", "GOLD P1 Menu Right"},
|
||||||
|
|
||||||
"GOLD P1 Woofer Corner",
|
{"GOLD", "GOLD P2 Menu Start"},
|
||||||
"GOLD P2 Woofer Corner",
|
{"GOLD", "GOLD P2 Menu Up"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Down"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Left"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Right"},
|
||||||
|
|
||||||
"GOLD P1 Card Unit R",
|
{"GOLD", "GOLD Title Panel Left"},
|
||||||
"GOLD P1 Card Unit G",
|
{"GOLD", "GOLD Title Panel Center"},
|
||||||
"GOLD P1 Card Unit B",
|
{"GOLD", "GOLD Title Panel Right"},
|
||||||
|
|
||||||
"GOLD P2 Card Unit R",
|
{"GOLD", "GOLD P1 Woofer Corner"},
|
||||||
"GOLD P2 Card Unit G",
|
{"GOLD", "GOLD P2 Woofer Corner"},
|
||||||
"GOLD P2 Card Unit B",
|
|
||||||
|
|
||||||
"GOLD Top Panel Avg R",
|
{"GOLD", "GOLD P1 Card Unit R"},
|
||||||
"GOLD Top Panel Avg G",
|
{"GOLD", "GOLD P1 Card Unit G"},
|
||||||
"GOLD Top Panel Avg B",
|
{"GOLD", "GOLD P1 Card Unit B"},
|
||||||
|
|
||||||
"GOLD Monitor Side Left Avg R",
|
{"GOLD", "GOLD P2 Card Unit R"},
|
||||||
"GOLD Monitor Side Left Avg G",
|
{"GOLD", "GOLD P2 Card Unit G"},
|
||||||
"GOLD Monitor Side Left Avg B",
|
{"GOLD", "GOLD P2 Card Unit B"},
|
||||||
|
|
||||||
"GOLD Monitor Side Right Avg R",
|
{"GOLD", "GOLD Top Panel Avg R"},
|
||||||
"GOLD Monitor Side Right Avg G",
|
{"GOLD", "GOLD Top Panel Avg G"},
|
||||||
"GOLD Monitor Side Right Avg B",
|
{"GOLD", "GOLD Top Panel Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Up Avg R",
|
{"GOLD", "GOLD Monitor Side Left Avg R"},
|
||||||
"GOLD P1 Foot Up Avg G",
|
{"GOLD", "GOLD Monitor Side Left Avg G"},
|
||||||
"GOLD P1 Foot Up Avg B",
|
{"GOLD", "GOLD Monitor Side Left Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Down Avg R",
|
{"GOLD", "GOLD Monitor Side Right Avg R"},
|
||||||
"GOLD P1 Foot Down Avg G",
|
{"GOLD", "GOLD Monitor Side Right Avg G"},
|
||||||
"GOLD P1 Foot Down Avg B",
|
{"GOLD", "GOLD Monitor Side Right Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Left Avg R",
|
{"GOLD", "GOLD P1 Foot Up Avg R"},
|
||||||
"GOLD P1 Foot Left Avg G",
|
{"GOLD", "GOLD P1 Foot Up Avg G"},
|
||||||
"GOLD P1 Foot Left Avg B",
|
{"GOLD", "GOLD P1 Foot Up Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Right Avg R",
|
{"GOLD", "GOLD P1 Foot Down Avg R"},
|
||||||
"GOLD P1 Foot Right Avg G",
|
{"GOLD", "GOLD P1 Foot Down Avg G"},
|
||||||
"GOLD P1 Foot Right Avg B",
|
{"GOLD", "GOLD P1 Foot Down Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Up Avg R",
|
{"GOLD", "GOLD P1 Foot Left Avg R"},
|
||||||
"GOLD P2 Foot Up Avg G",
|
{"GOLD", "GOLD P1 Foot Left Avg G"},
|
||||||
"GOLD P2 Foot Up Avg B",
|
{"GOLD", "GOLD P1 Foot Left Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Down Avg R",
|
{"GOLD", "GOLD P1 Foot Right Avg R"},
|
||||||
"GOLD P2 Foot Down Avg G",
|
{"GOLD", "GOLD P1 Foot Right Avg G"},
|
||||||
"GOLD P2 Foot Down Avg B",
|
{"GOLD", "GOLD P1 Foot Right Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Left Avg R",
|
{"GOLD", "GOLD P2 Foot Up Avg R"},
|
||||||
"GOLD P2 Foot Left Avg G",
|
{"GOLD", "GOLD P2 Foot Up Avg G"},
|
||||||
"GOLD P2 Foot Left Avg B",
|
{"GOLD", "GOLD P2 Foot Up Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Right Avg R",
|
{"GOLD", "GOLD P2 Foot Down Avg R"},
|
||||||
"GOLD P2 Foot Right Avg G",
|
{"GOLD", "GOLD P2 Foot Down Avg G"},
|
||||||
"GOLD P2 Foot Right Avg B",
|
{"GOLD", "GOLD P2 Foot Down Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Stage Corner Up-Left",
|
{"GOLD", "GOLD P2 Foot Left Avg R"},
|
||||||
"GOLD P1 Stage Corner Up-Right",
|
{"GOLD", "GOLD P2 Foot Left Avg G"},
|
||||||
"GOLD P1 Stage Corner Down-Left",
|
{"GOLD", "GOLD P2 Foot Left Avg B"},
|
||||||
"GOLD P1 Stage Corner Down-Right",
|
|
||||||
|
|
||||||
"GOLD P2 Stage Corner Up-Left",
|
{"GOLD", "GOLD P2 Foot Right Avg R"},
|
||||||
"GOLD P2 Stage Corner Up-Right",
|
{"GOLD", "GOLD P2 Foot Right Avg G"},
|
||||||
"GOLD P2 Stage Corner Down-Left",
|
{"GOLD", "GOLD P2 Foot Right Avg B"},
|
||||||
"GOLD P2 Stage Corner Down-Right"
|
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Up-Left"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Up-Right"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Down-Left"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Down-Right"},
|
||||||
|
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Up-Left"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Up-Right"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Down-Left"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Down-Right"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ int games::ddr::DDRP3IOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use both sat spots for a neon pulse
|
// use both sat spots for a neon pulse
|
||||||
float value_neon = (light_bits & hd_mapping_bits[0] && hd_mapping_bits[1]) ? 1.f : 0.f;
|
float value_neon = (light_bits & hd_mapping_bits[0] && light_bits & hd_mapping_bits[1]) ? 1.f : 0.f;
|
||||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/memutils.h"
|
#include "util/memutils.h"
|
||||||
|
#include "rgb_cam.h"
|
||||||
|
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
|
|
||||||
@@ -150,6 +151,7 @@ namespace games::drs {
|
|||||||
char DRS_TAPELED[DRS_TAPELED_COLS_TOTAL][3] {};
|
char DRS_TAPELED[DRS_TAPELED_COLS_TOTAL][3] {};
|
||||||
bool DISABLE_TOUCH = false;
|
bool DISABLE_TOUCH = false;
|
||||||
bool TRANSPOSE_TOUCH = false;
|
bool TRANSPOSE_TOUCH = false;
|
||||||
|
bool RGB_CAMERA_HOOK = false;
|
||||||
|
|
||||||
std::vector<TouchEvent> TOUCH_EVENTS;
|
std::vector<TouchEvent> TOUCH_EVENTS;
|
||||||
|
|
||||||
@@ -313,6 +315,10 @@ namespace games::drs {
|
|||||||
} else {
|
} else {
|
||||||
log_info("drs", "no native input method detected");
|
log_info("drs", "no native input method detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RGB_CAMERA_HOOK) {
|
||||||
|
init_rgb_camera_hook();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRSGame::detach() {
|
void DRSGame::detach() {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace games::drs {
|
|||||||
extern std::vector<TouchEvent> TOUCH_EVENTS;
|
extern std::vector<TouchEvent> TOUCH_EVENTS;
|
||||||
extern bool DISABLE_TOUCH;
|
extern bool DISABLE_TOUCH;
|
||||||
extern bool TRANSPOSE_TOUCH;
|
extern bool TRANSPOSE_TOUCH;
|
||||||
|
extern bool RGB_CAMERA_HOOK;
|
||||||
|
|
||||||
void fire_touches(drs_touch_t *events, size_t event_count);
|
void fire_touches(drs_touch_t *events, size_t event_count);
|
||||||
void start_touch();
|
void start_touch();
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
#include "games/drs/rgb_cam.h"
|
||||||
|
|
||||||
|
// set version to Windows 7 to enable Media Foundation functions
|
||||||
|
#ifdef _WIN32_WINNT
|
||||||
|
#undef _WIN32_WINNT
|
||||||
|
#endif
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
|
// turn on C-style COM objects
|
||||||
|
#define CINTERFACE
|
||||||
|
|
||||||
|
#include <mfobjects.h>
|
||||||
|
#include <mfidl.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
|
#include "cfg/configurator.h"
|
||||||
|
#include "hooks/cfgmgr32hook.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/memutils.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
|
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
|
||||||
|
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
|
||||||
|
|
||||||
|
namespace games::drs {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Camera related stuff
|
||||||
|
*/
|
||||||
|
static std::wstring CAMERA0_ID;
|
||||||
|
|
||||||
|
static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue,
|
||||||
|
UINT32 *pcchLength)
|
||||||
|
{
|
||||||
|
// call the original
|
||||||
|
HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength);
|
||||||
|
|
||||||
|
// log the cam name
|
||||||
|
log_misc("drs::rgbcam", "obtained {}", ws2s(*ppwszValue));
|
||||||
|
|
||||||
|
// try first camera
|
||||||
|
wchar_t *pwc = nullptr;
|
||||||
|
if (CAMERA0_ID.length() == 23) {
|
||||||
|
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if camera could be identified
|
||||||
|
if (pwc) {
|
||||||
|
|
||||||
|
// fake the USB IDs
|
||||||
|
pwc[4] = L'2';
|
||||||
|
pwc[5] = L'8';
|
||||||
|
pwc[6] = L'8';
|
||||||
|
pwc[7] = L'c';
|
||||||
|
|
||||||
|
pwc[13] = L'0';
|
||||||
|
pwc[14] = L'0';
|
||||||
|
pwc[15] = L'0';
|
||||||
|
pwc[16] = L'2';
|
||||||
|
|
||||||
|
pwc[21] = L'0';
|
||||||
|
pwc[22] = L'0';
|
||||||
|
|
||||||
|
log_misc("drs::rgbcam", "replaced {}", ws2s(*ppwszValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return original result
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hook_camera(std::wstring camera_id, std::string camera_instance) {
|
||||||
|
// don't hook if camera 0 is already hooked
|
||||||
|
if (CAMERA0_ID.length() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the camera ID
|
||||||
|
CAMERA0_ID = camera_id;
|
||||||
|
|
||||||
|
// cfgmgr hook
|
||||||
|
CFGMGR32_HOOK_SETTING camera_setting;
|
||||||
|
camera_setting.device_instance = 0xDEADBEEF;
|
||||||
|
camera_setting.parent_instance = ~camera_setting.device_instance;
|
||||||
|
camera_setting.device_id = "USB\\VEN_8086&DEV_8C2D";
|
||||||
|
camera_setting.device_node_id = "USB\\VID_288C&PID_0002&MI_00\\?&????????&?&????";
|
||||||
|
if (camera_instance.length() == 17) {
|
||||||
|
for (int i = 0; i < 17; i++) {
|
||||||
|
camera_setting.device_node_id[28 + i] = camera_instance[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfgmgr32hook_add(camera_setting);
|
||||||
|
|
||||||
|
log_info("drs::rgbcam", "hooked camera @ {}", ws2s(camera_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hook_camera_vtable(IMFActivate *camera) {
|
||||||
|
|
||||||
|
// hook allocated string method for camera identification
|
||||||
|
memutils::VProtectGuard camera_guard(camera->lpVtbl);
|
||||||
|
camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI MFEnumDeviceSources_hook(IMFAttributes *pAttributes, IMFActivate ***pppSourceActivate,
|
||||||
|
UINT32 *pcSourceActivate) {
|
||||||
|
|
||||||
|
// call original function
|
||||||
|
HRESULT result_orig = MFEnumDeviceSources_orig(pAttributes, pppSourceActivate, pcSourceActivate);
|
||||||
|
|
||||||
|
// check for capture devices
|
||||||
|
if (FAILED(result_orig) || !*pcSourceActivate) {
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get camera
|
||||||
|
IMFActivate *camera = **pppSourceActivate;
|
||||||
|
|
||||||
|
// save original method for later use
|
||||||
|
if (GetAllocatedString_orig == nullptr) {
|
||||||
|
GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hook allocated string method for camera identification
|
||||||
|
hook_camera_vtable(camera);
|
||||||
|
|
||||||
|
// get camera link
|
||||||
|
LPWSTR camera_link_lpwstr;
|
||||||
|
UINT32 camera_link_length;
|
||||||
|
if (SUCCEEDED(GetAllocatedString_orig(
|
||||||
|
camera,
|
||||||
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
||||||
|
&camera_link_lpwstr,
|
||||||
|
&camera_link_length))) {
|
||||||
|
|
||||||
|
// cut name to make ID
|
||||||
|
std::wstring camera_link_ws = std::wstring(camera_link_lpwstr);
|
||||||
|
std::wstring camera_id = camera_link_ws.substr(8, 23);
|
||||||
|
|
||||||
|
log_info("drs::rgbcam", "found video capture device: {}", ws2s(camera_link_ws));
|
||||||
|
|
||||||
|
// only support cameras that start with \\?\usb
|
||||||
|
if (!string_begins_with(camera_link_ws, L"\\\\?\\usb")) {
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get camera instance
|
||||||
|
std::string camera_link = ws2s(camera_link_ws);
|
||||||
|
std::string camera_instance = camera_link.substr(32, 17);
|
||||||
|
|
||||||
|
// hook the camera
|
||||||
|
hook_camera(camera_id, camera_instance);
|
||||||
|
} else {
|
||||||
|
log_warning("drs::rgbcam", "failed to open camera");
|
||||||
|
}
|
||||||
|
|
||||||
|
// return result
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_rgb_camera_hook() {
|
||||||
|
log_info("drs::rgbcam", "initializing camera hook");
|
||||||
|
|
||||||
|
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// camera media framework hook
|
||||||
|
MFEnumDeviceSources_orig = detour::iat_try(
|
||||||
|
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace games::drs {
|
||||||
|
void init_rgb_camera_hook();
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -17,24 +20,29 @@ namespace games::gitadora {
|
|||||||
// */
|
// */
|
||||||
|
|
||||||
struct AIO_NMGR_IOB2 {
|
struct AIO_NMGR_IOB2 {
|
||||||
|
uint8_t data[0xe18];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB5 {
|
struct AIO_NMGR_IOB5 {
|
||||||
|
uint8_t data[0xb10];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_SCI_COMM {
|
struct AIO_SCI_COMM {
|
||||||
|
uint8_t data[0x100];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB5_Y32D {
|
struct AIO_IOB5_Y32D {
|
||||||
|
uint8_t data[0x2e8];
|
||||||
};
|
};
|
||||||
|
|
||||||
// struct AIO_IOB5_Y33I {
|
// struct AIO_IOB5_Y33I {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_AC1 {
|
struct AIO_IOB2_BI2X_AC1 {
|
||||||
|
uint8_t data[0x4570];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB__NODEINFO {
|
struct AIO_NMGR_IOB__NODEINFO {
|
||||||
@@ -42,6 +50,7 @@ namespace games::gitadora {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_WRFIRM {
|
struct AIO_IOB2_BI2X_WRFIRM {
|
||||||
|
uint8_t data[0x20f48];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_SCI_COMM_T {
|
struct AIO_SCI_COMM_T {
|
||||||
@@ -158,6 +167,14 @@ namespace games::gitadora {
|
|||||||
uint8_t unk[0xB1];
|
uint8_t unk[0xB1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// verified with M32-007-2025092400
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe18);
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB5) == 0xb10);
|
||||||
|
static_assert(sizeof(AIO_SCI_COMM) == 0x100);
|
||||||
|
static_assert(sizeof(AIO_IOB5_Y32D) == 0x2e8);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4570);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* typedefs
|
* typedefs
|
||||||
*/
|
*/
|
||||||
@@ -323,10 +340,38 @@ namespace games::gitadora {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void __fastcall aioIob2Bi2xAC1_SetOutputData(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data) {
|
void __fastcall aioIob2Bi2xAC1_SetOutputData(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data) {
|
||||||
|
|
||||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||||
return aioIob2Bi2xAC1_SetOutputData_orig(i_pNodeCtl, i_CnPin, i_Data);
|
return aioIob2Bi2xAC1_SetOutputData_orig(i_pNodeCtl, i_CnPin, i_Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Lights::gitadora_lights_t> light;
|
||||||
|
switch (i_CnPin) {
|
||||||
|
case 14:
|
||||||
|
light = Lights::ArenaWooferR;
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
light = Lights::ArenaWooferG;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
light = Lights::ArenaWooferB;
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
light = Lights::ArenaCardR;
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
light = Lights::ArenaCardG;
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
light = Lights::ArenaCardB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (light.has_value()) {
|
||||||
|
auto &lights = games::gitadora::get_lights();
|
||||||
|
GameAPI::Lights::writeLight(RI_MGR, lights.at(light.value()), i_Data / 255.f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void __fastcall aioIob2Bi2xAC1_SetTapeLedDataPart(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
|
void __fastcall aioIob2Bi2xAC1_SetTapeLedDataPart(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
|
||||||
@@ -336,6 +381,16 @@ namespace games::gitadora {
|
|||||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||||
return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse);
|
return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tapeledutils::is_enabled() &&
|
||||||
|
i_TapeLedCh == 0 && i_Offset == 0 && i_cntTapeLed == 62) {
|
||||||
|
|
||||||
|
auto &lights = get_lights();
|
||||||
|
const auto rgb = tapeledutils::pick_color_from_led_tape(i_pData, i_cntTapeLed);
|
||||||
|
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::ArenaTitleAvgR], rgb.r);
|
||||||
|
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::ArenaTitleAvgG], rgb.g);
|
||||||
|
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::ArenaTitleAvgB], rgb.b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void __fastcall aioIob2Bi2x_SetTapeLedDataLimit(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Channel,
|
void __fastcall aioIob2Bi2x_SetTapeLedDataLimit(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Channel,
|
||||||
@@ -551,6 +606,7 @@ namespace games::gitadora {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// libaio-iob5_y32.dll
|
// libaio-iob5_y32.dll
|
||||||
|
|
||||||
static AIO_IOB5_Y32D *__fastcall aioIob5Y32d_Create(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode) {
|
static AIO_IOB5_Y32D *__fastcall aioIob5Y32d_Create(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode) {
|
||||||
|
|
||||||
if (i_pSci == aioNmgrIob5) {
|
if (i_pSci == aioNmgrIob5) {
|
||||||
@@ -661,4 +717,6 @@ namespace games::gitadora {
|
|||||||
detour::trampoline_try(libaioDll, "aioNodeCtl_UpdateDevicesStatus",
|
detour::trampoline_try(libaioDll, "aioNodeCtl_UpdateDevicesStatus",
|
||||||
aioNodeCtl_UpdateDevicesStatus, &aioNodeCtl_UpdateDevicesStatus_orig);
|
aioNodeCtl_UpdateDevicesStatus, &aioNodeCtl_UpdateDevicesStatus_orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
namespace games::gitadora {
|
namespace games::gitadora {
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -5,11 +5,14 @@
|
|||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
#include "hooks/audio/mme.h"
|
#include "hooks/audio/mme.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
|
#include "overlay/overlay.h"
|
||||||
#include "util/cpuutils.h"
|
#include "util/cpuutils.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#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 {
|
||||||
@@ -17,6 +20,11 @@ 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 ARENA_SINGLE_WINDOW = false;
|
||||||
|
bool P1_LEFTY = false;
|
||||||
|
bool P2_LEFTY = false;
|
||||||
|
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||||
|
std::optional<socd::SocdAlgorithm> PICK_ALGO = socd::SocdAlgorithm::PreferRecent;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prevent GitaDora from creating folders on F drive
|
* Prevent GitaDora from creating folders on F drive
|
||||||
@@ -212,6 +220,23 @@ namespace games::gitadora {
|
|||||||
log_fatal("gitadora", "Cabinet type 3 (SD2) not supported on XG series");
|
log_fatal("gitadora", "Cabinet type 3 (SD2) not supported on XG series");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// arena model launches a tiny window yet backbuffer at 4k, resulting in unusable overlay
|
||||||
|
// force scaling to make things usable
|
||||||
|
if (!overlay::UI_SCALE_PERCENT.has_value() && is_arena_model() &&
|
||||||
|
GRAPHICS_WINDOWED && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
overlay::UI_SCALE_PERCENT = 250;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for guitar wail SOCD cleaning
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||||
|
|
||||||
|
// for guitar picking
|
||||||
|
if (PICK_ALGO.has_value()) {
|
||||||
|
log_info("gitadora", "guitar pick SOCD algorithm: {}", static_cast<int>(PICK_ALGO.value()));
|
||||||
|
} else {
|
||||||
|
log_info("gitadora", "guitar pick SOCD algorithm: legacy");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,6 +296,13 @@ namespace games::gitadora {
|
|||||||
|
|
||||||
// volume change prevention
|
// volume change prevention
|
||||||
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// touch hook
|
||||||
|
if (ARENA_SINGLE_WINDOW) {
|
||||||
|
wintouchemu::FORCE = true;
|
||||||
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
|
wintouchemu::hook("GITADORA", avs::game::DLL_INSTANCE);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,18 @@
|
|||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
|
||||||
namespace games::gitadora {
|
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 ARENA_SINGLE_WINDOW;
|
||||||
|
extern bool P1_LEFTY;
|
||||||
|
extern bool P2_LEFTY;
|
||||||
|
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||||
|
extern std::optional<socd::SocdAlgorithm> PICK_ALGO;
|
||||||
|
|
||||||
class GitaDoraGame : public games::Game {
|
class GitaDoraGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
@@ -38,4 +44,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,17 +65,18 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
|||||||
"Drum Help",
|
"Drum Help",
|
||||||
"Drum Button Extra 1",
|
"Drum Button Extra 1",
|
||||||
"Drum Button Extra 2",
|
"Drum Button Extra 2",
|
||||||
|
|
||||||
|
"Drum Left Cymbal",
|
||||||
"Drum Hi-Hat",
|
"Drum Hi-Hat",
|
||||||
"Drum Hi-Hat Closed",
|
"Drum Left Pedal",
|
||||||
"Drum Hi-Hat Half-Open",
|
|
||||||
"Drum Snare",
|
"Drum Snare",
|
||||||
"Drum Hi-Tom",
|
"Drum Hi-Tom",
|
||||||
"Drum Low-Tom",
|
|
||||||
"Drum Right Cymbal",
|
|
||||||
"Drum Bass Pedal",
|
"Drum Bass Pedal",
|
||||||
"Drum Left Cymbal",
|
"Drum Low-Tom",
|
||||||
"Drum Left Pedal",
|
"Drum Floor Tom",
|
||||||
"Drum Floor Tom"
|
"Drum Right Cymbal",
|
||||||
|
"Drum Hi-Hat Closed",
|
||||||
|
"Drum Hi-Hat Half-Open"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,9 +108,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 normally hold your guitar left-handed (frets on right hand):\n"
|
||||||
|
" * here, hold the guitar as right-handed (frets on left hand),\n"
|
||||||
|
" and bind the buttons, pick, and wail controls\n"
|
||||||
|
" * in Options tab, turn on GitaDora Lefty Guitar\n"
|
||||||
|
" * finally, use the in-game option to turn on LEFT mode\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 +127,28 @@ 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"
|
" For MIDI drums with Open/Closed HiHat configurations or pads with\n"
|
||||||
|
" 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"
|
"\n"
|
||||||
"For MIDI drums with Open/Closed HiHat configurations, bind variations below. "
|
"X axis: 0% when body is held facing monitor, 50% when flat on a table\n"
|
||||||
"v2_drum algorithm might work better for those drums."
|
"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."
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,88 +158,102 @@ std::vector<Light> &games::gitadora::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("GitaDora");
|
lights = GameAPI::Lights::getLights("GitaDora");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(&lights,
|
GameAPI::Lights::sortLightsWithCategory(&lights,
|
||||||
"Guitar P1 Motor",
|
{
|
||||||
"Guitar P2 Motor",
|
{"Guitar", "Guitar P1 Motor"},
|
||||||
|
{"Guitar", "Guitar P2 Motor"},
|
||||||
|
|
||||||
"P1 Start",
|
{"Menu", "P1 Start"},
|
||||||
"P1 Menu Up Down (DX)",
|
{"Menu", "P1 Menu Up Down (DX)"},
|
||||||
"P1 Menu Left Right (DX)",
|
{"Menu", "P1 Menu Left Right (DX)"},
|
||||||
"P1 Help (DX)",
|
{"Menu", "P1 Help (DX)"},
|
||||||
|
|
||||||
"P2 Start",
|
{"Menu", "P2 Start"},
|
||||||
"P2 Menu Up Down (DX)",
|
{"Menu", "P2 Menu Up Down (DX)"},
|
||||||
"P2 Menu Left Right (DX)",
|
{"Menu", "P2 Menu Left Right (DX)"},
|
||||||
"P2 Help (DX)",
|
{"Menu", "P2 Help (DX)"},
|
||||||
|
|
||||||
"Drum Left Cymbal",
|
{"Drums", "Drum Left Cymbal"},
|
||||||
"Drum Hi-Hat",
|
{"Drums", "Drum Hi-Hat"},
|
||||||
"Drum Snare",
|
{"Drums", "Drum Snare"},
|
||||||
"Drum High Tom",
|
{"Drums", "Drum High Tom"},
|
||||||
"Drum Low Tom",
|
{"Drums", "Drum Low Tom"},
|
||||||
"Drum Floor Tom",
|
{"Drums", "Drum Floor Tom"},
|
||||||
"Drum Right Cymbal",
|
{"Drums", "Drum Right Cymbal"},
|
||||||
|
|
||||||
"Drum Woofer R",
|
{"DX/SD", "Drum Woofer R"},
|
||||||
"Drum Woofer G",
|
{"DX/SD", "Drum Woofer G"},
|
||||||
"Drum Woofer B",
|
{"DX/SD", "Drum Woofer B"},
|
||||||
|
|
||||||
"Drum Stage R (DX)",
|
{"DX/SD", "Drum Stage R (DX)"},
|
||||||
"Drum Stage G (DX)",
|
{"DX/SD", "Drum Stage G (DX)"},
|
||||||
"Drum Stage B (DX)",
|
{"DX/SD", "Drum Stage B (DX)"},
|
||||||
|
|
||||||
"Spot Left (DX)",
|
{"DX/SD", "Spot Left (DX)"},
|
||||||
"Spot Right (DX)",
|
{"DX/SD", "Spot Right (DX)"},
|
||||||
"Spot Center Left (DX)",
|
{"DX/SD", "Spot Center Left (DX)"},
|
||||||
"Spot Center Right (DX)",
|
{"DX/SD", "Spot Center Right (DX)"},
|
||||||
|
|
||||||
"Drum Spot Rear Left (DX)",
|
{"DX/SD", "Drum Spot Rear Left (DX)"},
|
||||||
"Drum Spot Rear Right (DX)",
|
{"DX/SD", "Drum Spot Rear Right (DX)"},
|
||||||
|
|
||||||
"Guitar Lower Left R (DX)",
|
{"DX/SD", "Guitar Lower Left R (DX)"},
|
||||||
"Guitar Lower Left G (DX)",
|
{"DX/SD", "Guitar Lower Left G (DX)"},
|
||||||
"Guitar Lower Left B (DX)",
|
{"DX/SD", "Guitar Lower Left B (DX)"},
|
||||||
"Guitar Lower Right R (DX)",
|
{"DX/SD", "Guitar Lower Right R (DX)"},
|
||||||
"Guitar Lower Right G (DX)",
|
{"DX/SD", "Guitar Lower Right G (DX)"},
|
||||||
"Guitar Lower Right B (DX)",
|
{"DX/SD", "Guitar Lower Right B (DX)"},
|
||||||
|
|
||||||
"Guitar Left Speaker Upper R (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper R (DX)"},
|
||||||
"Guitar Left Speaker Upper G (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper G (DX)"},
|
||||||
"Guitar Left Speaker Upper B (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper B (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left R (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left G (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left B (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right R (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right G (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right B (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left R (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left G (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left B (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right R (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right G (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right B (DX)"},
|
||||||
"Guitar Left Speaker Lower R (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower R (DX)"},
|
||||||
"Guitar Left Speaker Lower G (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower G (DX)"},
|
||||||
"Guitar Left Speaker Lower B (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower B (DX)"},
|
||||||
|
|
||||||
"Guitar Right Speaker Upper R (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper R (DX)"},
|
||||||
"Guitar Right Speaker Upper G (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper G (DX)"},
|
||||||
"Guitar Right Speaker Upper B (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper B (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left R (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left G (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left B (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right R (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right G (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right B (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left R (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left G (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left B (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right R (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right G (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right B (DX)"},
|
||||||
"Guitar Right Speaker Lower R (DX)",
|
{"DX/SD", "Guitar Right Speaker Lower R (DX)"},
|
||||||
"Guitar Right Speaker Lower G (DX)",
|
{"DX/SD", "Guitar Right Speaker Lower G (DX)"},
|
||||||
"Guitar Right Speaker Lower B (DX)"
|
{"DX/SD", "Guitar Right Speaker Lower B (DX)"},
|
||||||
|
|
||||||
|
{"Arena Model", "Title Average R (Arena)"},
|
||||||
|
{"Arena Model", "Title Average G (Arena)"},
|
||||||
|
{"Arena Model", "Title Average B (Arena)"},
|
||||||
|
|
||||||
|
{"Arena Model", "Woofer R (Arena)"},
|
||||||
|
{"Arena Model", "Woofer G (Arena)"},
|
||||||
|
{"Arena Model", "Woofer B (Arena)"},
|
||||||
|
|
||||||
|
{"Arena Model", "Card Reader R (Arena)"},
|
||||||
|
{"Arena Model", "Card Reader G (Arena)"},
|
||||||
|
{"Arena Model", "Card Reader B (Arena)"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,18 +66,21 @@ namespace games::gitadora {
|
|||||||
DrumHelp,
|
DrumHelp,
|
||||||
DrumButtonExtra1,
|
DrumButtonExtra1,
|
||||||
DrumButtonExtra2,
|
DrumButtonExtra2,
|
||||||
// note: this is the order they appear in the test menu
|
// note: this is the order of drums in the default layout
|
||||||
|
DrumLeftCymbal,
|
||||||
DrumHiHat,
|
DrumHiHat,
|
||||||
DrumHiHatClosed,
|
DrumLeftPedal,
|
||||||
DrumHiHatHalfOpen,
|
|
||||||
DrumSnare,
|
DrumSnare,
|
||||||
DrumHiTom,
|
DrumHiTom,
|
||||||
DrumLowTom,
|
|
||||||
DrumRightCymbal,
|
|
||||||
DrumBassPedal,
|
DrumBassPedal,
|
||||||
DrumLeftCymbal,
|
DrumLowTom,
|
||||||
DrumLeftPedal,
|
DrumFloorTom,
|
||||||
DrumFloorTom
|
DrumRightCymbal,
|
||||||
|
|
||||||
|
// these only exist for back compat
|
||||||
|
// (created before it was possible to map multiple input to a button)
|
||||||
|
DrumHiHatClosed,
|
||||||
|
DrumHiHatHalfOpen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,13 +194,25 @@ namespace games::gitadora {
|
|||||||
GuitarRightSpeakerMidLowRightB,
|
GuitarRightSpeakerMidLowRightB,
|
||||||
GuitarRightSpeakerLowerR,
|
GuitarRightSpeakerLowerR,
|
||||||
GuitarRightSpeakerLowerG,
|
GuitarRightSpeakerLowerG,
|
||||||
GuitarRightSpeakerLowerB
|
GuitarRightSpeakerLowerB,
|
||||||
|
|
||||||
|
// arena model (common for both guitar and drum)
|
||||||
|
ArenaTitleAvgR,
|
||||||
|
ArenaTitleAvgG,
|
||||||
|
ArenaTitleAvgB,
|
||||||
|
ArenaWooferR,
|
||||||
|
ArenaWooferG,
|
||||||
|
ArenaWooferB,
|
||||||
|
ArenaCardR,
|
||||||
|
ArenaCardG,
|
||||||
|
ArenaCardB,
|
||||||
} gitadora_lights_t;
|
} gitadora_lights_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -91,28 +92,50 @@ bool games::gitadora::J33ISerialDevice::parse_msg(
|
|||||||
payload.buttons |= 1 << GUITAR_BTN_P;
|
payload.buttons |= 1 << GUITAR_BTN_P;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickUp])) {
|
|
||||||
if (!GFDM_GF_PICK_STATE_UP) {
|
const auto pick_up = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickUp]);
|
||||||
GFDM_GF_PICK_STATE_UP = true;
|
const auto pick_down = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickDown]);
|
||||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
if (!games::gitadora::PICK_ALGO.has_value()) {
|
||||||
|
// legacy behavior (detect rising edges only, input for 1 frame)
|
||||||
|
if (pick_up) {
|
||||||
|
if (!GFDM_GF_PICK_STATE_UP) {
|
||||||
|
GFDM_GF_PICK_STATE_UP = true;
|
||||||
|
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GFDM_GF_PICK_STATE_UP = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pick_down) {
|
||||||
|
if (!GFDM_GF_PICK_STATE_DOWN) {
|
||||||
|
GFDM_GF_PICK_STATE_DOWN = true;
|
||||||
|
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GFDM_GF_PICK_STATE_DOWN = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GFDM_GF_PICK_STATE_UP = false;
|
const auto socd = socd::socd_clean(
|
||||||
}
|
0,
|
||||||
|
pick_up,
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickDown])) {
|
pick_down,
|
||||||
if (!GFDM_GF_PICK_STATE_DOWN) {
|
get_performance_milliseconds(),
|
||||||
GFDM_GF_PICK_STATE_DOWN = true;
|
games::gitadora::PICK_ALGO.value()
|
||||||
|
);
|
||||||
|
if (socd == socd::SocdCCW) {
|
||||||
|
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||||
|
} else if (socd == socd::SocdCW) {
|
||||||
|
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||||
|
} else if (socd == socd::SocdBoth) {
|
||||||
|
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
GFDM_GF_PICK_STATE_DOWN = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +146,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 +153,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;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -24,17 +26,28 @@ namespace games::iidx {
|
|||||||
uint8_t data[0x10];
|
uint8_t data[0x10];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB2 {
|
struct AIO_NMGR_IOB2_VTABLE {
|
||||||
uint8_t dummy0[0x50];
|
// other functions here, but they never get called
|
||||||
|
uint8_t dummy[0x50];
|
||||||
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
||||||
uint8_t dummy1[0x9A0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR_IOB2 {
|
||||||
|
AIO_NMGR_IOB2_VTABLE *vptr;
|
||||||
|
uint8_t dummy[0x9F0];
|
||||||
|
};
|
||||||
|
|
||||||
|
// confirmed 0x9F8 in iidx27, iidx32 aioNMgrIob2_Create
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0x9F8);
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_TDJ {
|
struct AIO_IOB2_BI2X_TDJ {
|
||||||
// who knows
|
// who knows
|
||||||
uint8_t data[0x13F8];
|
uint8_t data[0x13F8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// confirmed 0x13F8 in iidx27, iidx32 in aioIob2Bi2xTDJ_Create
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_TDJ) == 0x13F8);
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_TDJ__DEVSTATUS {
|
struct AIO_IOB2_BI2X_TDJ__DEVSTATUS {
|
||||||
// of course you could work with variables here
|
// of course you could work with variables here
|
||||||
uint8_t buffer[0xCA];
|
uint8_t buffer[0xCA];
|
||||||
@@ -78,7 +91,7 @@ namespace games::iidx {
|
|||||||
// libaio-iob.dll
|
// libaio-iob.dll
|
||||||
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
||||||
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
||||||
typedef AIO_NMGR_IOB2** (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
typedef AIO_NMGR_IOB2* (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* function pointers
|
* function pointers
|
||||||
@@ -443,7 +456,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static AC_HNDLIF* __fastcall aioIob2Bi2X_OpenSciUsbCdc(uint8_t device_num) {
|
static AC_HNDLIF* __fastcall aioIob2Bi2x_OpenSciUsbCdc(uint8_t device_num) {
|
||||||
if (acHndlif == nullptr) {
|
if (acHndlif == nullptr) {
|
||||||
acHndlif = new AC_HNDLIF;
|
acHndlif = new AC_HNDLIF;
|
||||||
memset(acHndlif->data, 0x0, sizeof(acHndlif->data));
|
memset(acHndlif->data, 0x0, sizeof(acHndlif->data));
|
||||||
@@ -456,14 +469,16 @@ namespace games::iidx {
|
|||||||
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
||||||
};
|
};
|
||||||
|
|
||||||
static AIO_NMGR_IOB2** __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
static AIO_NMGR_IOB2* __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
||||||
if (aioNmgrIob2 == nullptr) {
|
if (aioNmgrIob2 == nullptr) {
|
||||||
aioNmgrIob2 = new AIO_NMGR_IOB2;
|
aioNmgrIob2 = new AIO_NMGR_IOB2{};
|
||||||
memset(aioNmgrIob2, 0x0, sizeof(AIO_NMGR_IOB2));
|
aioNmgrIob2->vptr = new AIO_NMGR_IOB2_VTABLE{};
|
||||||
aioNmgrIob2->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
aioNmgrIob2->vptr->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||||
}
|
}
|
||||||
log_info("bi2x_hook", "aioNMgrIob2_Create");
|
log_info("bi2x_hook", "aioNMgrIob2_Create returned {}, size=0x{:x}, vptr @ {}",
|
||||||
return &aioNmgrIob2;
|
fmt::ptr(aioNmgrIob2), sizeof(*aioNmgrIob2), fmt::ptr(aioNmgrIob2->vptr));
|
||||||
|
|
||||||
|
return aioNmgrIob2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
||||||
@@ -520,10 +535,12 @@ namespace games::iidx {
|
|||||||
// hook IOB
|
// hook IOB
|
||||||
const auto libaioIobDll = "libaio-iob.dll";
|
const auto libaioIobDll = "libaio-iob.dll";
|
||||||
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_OpenSciUsbCdc",
|
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_OpenSciUsbCdc",
|
||||||
aioIob2Bi2X_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
|
aioIob2Bi2x_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
|
||||||
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_WriteFirmGetState",
|
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_WriteFirmGetState",
|
||||||
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
||||||
detour::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
|
detour::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
|
||||||
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
namespace games::iidx {
|
namespace games::iidx {
|
||||||
|
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/memutils.h"
|
#include "util/memutils.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "launcher/signal.h"
|
#include "launcher/signal.h"
|
||||||
|
|
||||||
@@ -80,6 +82,7 @@ namespace games::iidx {
|
|||||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||||
std::mutex IIDX_LED_TICKER_LOCK;
|
std::mutex IIDX_LED_TICKER_LOCK;
|
||||||
bool IIDX_TDJ_MONITOR_WARNING = false;
|
bool IIDX_TDJ_MONITOR_WARNING = false;
|
||||||
|
iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE = iidx_aio_emulation_state::unknown;
|
||||||
|
|
||||||
// io
|
// io
|
||||||
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
||||||
@@ -251,6 +254,8 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
typedef void* (*aioIob2Bi2x_CreateWriteFirmContext_t)(unsigned int, int);
|
typedef void* (*aioIob2Bi2x_CreateWriteFirmContext_t)(unsigned int, int);
|
||||||
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
|
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
|
||||||
|
|
||||||
@@ -269,6 +274,8 @@ namespace games::iidx {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
IIDXGame::IIDXGame() : Game("Beatmania IIDX") {
|
IIDXGame::IIDXGame() : Game("Beatmania IIDX") {
|
||||||
logger::hook_add(log_hook, this);
|
logger::hook_add(log_hook, this);
|
||||||
}
|
}
|
||||||
@@ -319,6 +326,8 @@ namespace games::iidx {
|
|||||||
devicehook_add(new IIDXFMSerialHandle());
|
devicehook_add(new IIDXFMSerialHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
// check for new I/O DLL
|
// check for new I/O DLL
|
||||||
auto aio = libutils::try_library("libaio.dll");
|
auto aio = libutils::try_library("libaio.dll");
|
||||||
if (aio != nullptr) {
|
if (aio != nullptr) {
|
||||||
@@ -384,6 +393,8 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
if (hooks::audio::ENABLED) {
|
if (hooks::audio::ENABLED) {
|
||||||
apply_audio_hacks();
|
apply_audio_hacks();
|
||||||
} else {
|
} else {
|
||||||
@@ -443,7 +454,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// windowed subscreen, enabled by default, unless turned off by user
|
// windowed subscreen, enabled by default, unless turned off by user
|
||||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
|
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool() && TDJ_MODE) {
|
||||||
GRAPHICS_IIDX_WSUB = true;
|
GRAPHICS_IIDX_WSUB = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,7 +473,7 @@ namespace games::iidx {
|
|||||||
"!!! please do the following instead: !!!\n"
|
"!!! please do the following instead: !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! Revert your changes to XML file so it says !!!\n"
|
"!!! Revert your changes to XML file so it says !!!\n"
|
||||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
||||||
"!!! in command line !!!\n"
|
"!!! in command line !!!\n"
|
||||||
@@ -499,6 +510,16 @@ namespace games::iidx {
|
|||||||
" monitor in Windows settings before launching the game"
|
" monitor in Windows settings before launching the game"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// socd
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::Neutral;
|
||||||
|
if (options->at(launcher::Options::IIDXDigitalTTSocd).is_active()) {
|
||||||
|
if (options->at(launcher::Options::IIDXDigitalTTSocd).value_text() == "last") {
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||||
|
} else if (options->at(launcher::Options::IIDXDigitalTTSocd).value_text() == "first") {
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferFirst;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IIDXGame::detach() {
|
void IIDXGame::detach() {
|
||||||
@@ -722,13 +743,12 @@ namespace games::iidx {
|
|||||||
bool ttpm_alt = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
bool ttpm_alt = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
||||||
player != 0 ? Buttons::P2_TTPlusMinusAlt : Buttons::P1_TTPlusMinusAlt));
|
player != 0 ? Buttons::P2_TTPlusMinusAlt : Buttons::P1_TTPlusMinusAlt));
|
||||||
|
|
||||||
// TT+
|
const auto tt_socd = socd::socd_clean(player, ttm, ttp, get_performance_milliseconds());
|
||||||
if (ttp)
|
if (tt_socd == socd::SocdCW) {
|
||||||
IIDXIO_TT_STATE[player] += change;
|
IIDXIO_TT_STATE[player] += change;
|
||||||
|
} else if (tt_socd == socd::SocdCCW) {
|
||||||
// TT-
|
|
||||||
if (ttm)
|
|
||||||
IIDXIO_TT_STATE[player] -= change;
|
IIDXIO_TT_STATE[player] -= change;
|
||||||
|
}
|
||||||
|
|
||||||
// TT+/-
|
// TT+/-
|
||||||
bool ttpm_rising_edge = !IIDXIO_TT_PRESSED[player] && ttpm;
|
bool ttpm_rising_edge = !IIDXIO_TT_PRESSED[player] && ttpm;
|
||||||
@@ -798,7 +818,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_tdj_fhd() {
|
bool is_tdj_fhd() {
|
||||||
return TDJ_MODE && avs::game::is_ext(2022101900, MAXINT);
|
return TDJ_MODE && avs::game::is_ext(2022101900, INT_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_audio_hacks() {
|
void apply_audio_hacks() {
|
||||||
@@ -864,12 +884,12 @@ namespace games::iidx {
|
|||||||
|
|
||||||
// patch iidx32+ for asio compatibility
|
// patch iidx32+ for asio compatibility
|
||||||
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
||||||
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
||||||
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
||||||
if (avs::game::is_ext(2024090100, MAXINT) &&
|
if (avs::game::is_ext(2024090100, INT_MAX) &&
|
||||||
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
||||||
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
||||||
|
|
||||||
// in iidx32 final:
|
// in iidx32 final:
|
||||||
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
||||||
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
|
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
|
||||||
@@ -915,6 +935,8 @@ namespace games::iidx {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CURRENT_IO_EMULATION_STATE = state;
|
||||||
|
|
||||||
// log_warning below could be turned into log_fatal in the future once we gain confidence
|
// log_warning below could be turned into log_fatal in the future once we gain confidence
|
||||||
// that this isn't triggering when it's not supposed to
|
// that this isn't triggering when it's not supposed to
|
||||||
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
||||||
|
|||||||
@@ -11,6 +11,12 @@
|
|||||||
|
|
||||||
namespace games::iidx {
|
namespace games::iidx {
|
||||||
|
|
||||||
|
enum class iidx_aio_emulation_state {
|
||||||
|
unknown,
|
||||||
|
bi2a_com2,
|
||||||
|
bi2x_hook
|
||||||
|
};
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
|
|
||||||
extern bool FLIP_CAMS;
|
extern bool FLIP_CAMS;
|
||||||
@@ -38,6 +44,7 @@ namespace games::iidx {
|
|||||||
constexpr int IIDX_TAPELED_TOTAL = 17;
|
constexpr int IIDX_TAPELED_TOTAL = 17;
|
||||||
// data mapping
|
// data mapping
|
||||||
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
||||||
|
extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE;
|
||||||
|
|
||||||
class IIDXGame : public games::Game {
|
class IIDXGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
@@ -64,9 +71,5 @@ namespace games::iidx {
|
|||||||
bool is_tdj_fhd();
|
bool is_tdj_fhd();
|
||||||
void apply_audio_hacks();
|
void apply_audio_hacks();
|
||||||
|
|
||||||
enum class iidx_aio_emulation_state {
|
|
||||||
bi2a_com2,
|
|
||||||
bi2x_hook
|
|
||||||
};
|
|
||||||
void update_io_emulation_state(iidx_aio_emulation_state state);
|
void update_io_emulation_state(iidx_aio_emulation_state state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,103 +83,105 @@ std::vector<Light> &games::iidx::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"P1 1",
|
{
|
||||||
"P1 2",
|
{"Common", "P1 1"},
|
||||||
"P1 3",
|
{"Common", "P1 2"},
|
||||||
"P1 4",
|
{"Common", "P1 3"},
|
||||||
"P1 5",
|
{"Common", "P1 4"},
|
||||||
"P1 6",
|
{"Common", "P1 5"},
|
||||||
"P1 7",
|
{"Common", "P1 6"},
|
||||||
"P2 1",
|
{"Common", "P1 7"},
|
||||||
"P2 2",
|
{"Common", "P2 1"},
|
||||||
"P2 3",
|
{"Common", "P2 2"},
|
||||||
"P2 4",
|
{"Common", "P2 3"},
|
||||||
"P2 5",
|
{"Common", "P2 4"},
|
||||||
"P2 6",
|
{"Common", "P2 5"},
|
||||||
"P2 7",
|
{"Common", "P2 6"},
|
||||||
"P1 Start",
|
{"Common", "P2 7"},
|
||||||
"P2 Start",
|
{"Common", "P1 Start"},
|
||||||
"VEFX",
|
{"Common", "P2 Start"},
|
||||||
"Effect",
|
{"Common", "VEFX"},
|
||||||
"Spot Light 1",
|
{"Common", "Effect"},
|
||||||
"Spot Light 2",
|
{"DX", "Spot Light 1"},
|
||||||
"Spot Light 3",
|
{"DX", "Spot Light 2"},
|
||||||
"Spot Light 4",
|
{"DX", "Spot Light 3"},
|
||||||
"Spot Light 5",
|
{"DX", "Spot Light 4"},
|
||||||
"Spot Light 6",
|
{"DX", "Spot Light 5"},
|
||||||
"Spot Light 7",
|
{"DX", "Spot Light 6"},
|
||||||
"Spot Light 8",
|
{"DX", "Spot Light 7"},
|
||||||
"Neon Lamp",
|
{"DX", "Spot Light 8"},
|
||||||
"Woofer R",
|
{"DX", "Neon Lamp"},
|
||||||
"Woofer G",
|
{"Lightning Model", "Woofer R"},
|
||||||
"Woofer B",
|
{"Lightning Model", "Woofer G"},
|
||||||
"IC Card Reader P1 R",
|
{"Lightning Model", "Woofer B"},
|
||||||
"IC Card Reader P1 G",
|
{"Lightning Model", "IC Card Reader P1 R"},
|
||||||
"IC Card Reader P1 B",
|
{"Lightning Model", "IC Card Reader P1 G"},
|
||||||
"IC Card Reader P2 R",
|
{"Lightning Model", "IC Card Reader P1 B"},
|
||||||
"IC Card Reader P2 G",
|
{"Lightning Model", "IC Card Reader P2 R"},
|
||||||
"IC Card Reader P2 B",
|
{"Lightning Model", "IC Card Reader P2 G"},
|
||||||
"Turntable P1 R",
|
{"Lightning Model", "IC Card Reader P2 B"},
|
||||||
"Turntable P1 G",
|
{"Lightning Model", "Turntable P1 R"},
|
||||||
"Turntable P1 B",
|
{"Lightning Model", "Turntable P1 G"},
|
||||||
"Turntable P2 R",
|
{"Lightning Model", "Turntable P1 B"},
|
||||||
"Turntable P2 G",
|
{"Lightning Model", "Turntable P2 R"},
|
||||||
"Turntable P2 B",
|
{"Lightning Model", "Turntable P2 G"},
|
||||||
"Turntable P1 Resistance",
|
{"Lightning Model", "Turntable P2 B"},
|
||||||
"Turntable P2 Resistance",
|
{"Lightning Model", "Turntable P1 Resistance"},
|
||||||
"Stage Left Avg R",
|
{"Lightning Model", "Turntable P2 Resistance"},
|
||||||
"Stage Left Avg G",
|
{"Lightning Model", "Stage Left Avg R"},
|
||||||
"Stage Left Avg B",
|
{"Lightning Model", "Stage Left Avg G"},
|
||||||
"Stage Right Avg R",
|
{"Lightning Model", "Stage Left Avg B"},
|
||||||
"Stage Right Avg G",
|
{"Lightning Model", "Stage Right Avg R"},
|
||||||
"Stage Right Avg B",
|
{"Lightning Model", "Stage Right Avg G"},
|
||||||
"Cabinet Left Avg R",
|
{"Lightning Model", "Stage Right Avg B"},
|
||||||
"Cabinet Left Avg G",
|
{"Lightning Model", "Cabinet Left Avg R"},
|
||||||
"Cabinet Left Avg B",
|
{"Lightning Model", "Cabinet Left Avg G"},
|
||||||
"Cabinet Right Avg R",
|
{"Lightning Model", "Cabinet Left Avg B"},
|
||||||
"Cabinet Right Avg G",
|
{"Lightning Model", "Cabinet Right Avg R"},
|
||||||
"Cabinet Right Avg B",
|
{"Lightning Model", "Cabinet Right Avg G"},
|
||||||
"Control Panel Under Avg R",
|
{"Lightning Model", "Cabinet Right Avg B"},
|
||||||
"Control Panel Under Avg G",
|
{"Lightning Model", "Control Panel Under Avg R"},
|
||||||
"Control Panel Under Avg B",
|
{"Lightning Model", "Control Panel Under Avg G"},
|
||||||
"Ceiling Left Avg R",
|
{"Lightning Model", "Control Panel Under Avg B"},
|
||||||
"Ceiling Left Avg G",
|
{"Lightning Model", "Ceiling Left Avg R"},
|
||||||
"Ceiling Left Avg B",
|
{"Lightning Model", "Ceiling Left Avg G"},
|
||||||
"Title Left Avg R",
|
{"Lightning Model", "Ceiling Left Avg B"},
|
||||||
"Title Left Avg G",
|
{"Lightning Model", "Title Left Avg R"},
|
||||||
"Title Left Avg B",
|
{"Lightning Model", "Title Left Avg G"},
|
||||||
"Title Right Avg R",
|
{"Lightning Model", "Title Left Avg B"},
|
||||||
"Title Right Avg G",
|
{"Lightning Model", "Title Right Avg R"},
|
||||||
"Title Right Avg B",
|
{"Lightning Model", "Title Right Avg G"},
|
||||||
"Ceiling Right Avg R",
|
{"Lightning Model", "Title Right Avg B"},
|
||||||
"Ceiling Right Avg G",
|
{"Lightning Model", "Ceiling Right Avg R"},
|
||||||
"Ceiling Right Avg B",
|
{"Lightning Model", "Ceiling Right Avg G"},
|
||||||
"Touch Panel Left Avg R",
|
{"Lightning Model", "Ceiling Right Avg B"},
|
||||||
"Touch Panel Left Avg G",
|
{"Lightning Model", "Touch Panel Left Avg R"},
|
||||||
"Touch Panel Left Avg B",
|
{"Lightning Model", "Touch Panel Left Avg G"},
|
||||||
"Touch Panel Right Avg R",
|
{"Lightning Model", "Touch Panel Left Avg B"},
|
||||||
"Touch Panel Right Avg G",
|
{"Lightning Model", "Touch Panel Right Avg R"},
|
||||||
"Touch Panel Right Avg B",
|
{"Lightning Model", "Touch Panel Right Avg G"},
|
||||||
"Side Panel Left Inner Avg R",
|
{"Lightning Model", "Touch Panel Right Avg B"},
|
||||||
"Side Panel Left Inner Avg G",
|
{"Lightning Model", "Side Panel Left Inner Avg R"},
|
||||||
"Side Panel Left Inner Avg B",
|
{"Lightning Model", "Side Panel Left Inner Avg G"},
|
||||||
"Side Panel Left Outer Avg R",
|
{"Lightning Model", "Side Panel Left Inner Avg B"},
|
||||||
"Side Panel Left Outer Avg G",
|
{"Lightning Model", "Side Panel Left Outer Avg R"},
|
||||||
"Side Panel Left Outer Avg B",
|
{"Lightning Model", "Side Panel Left Outer Avg G"},
|
||||||
"Side Panel Left Avg R",
|
{"Lightning Model", "Side Panel Left Outer Avg B"},
|
||||||
"Side Panel Left Avg G",
|
{"Lightning Model", "Side Panel Left Avg R"},
|
||||||
"Side Panel Left Avg B",
|
{"Lightning Model", "Side Panel Left Avg G"},
|
||||||
"Side Panel Right Outer Avg R",
|
{"Lightning Model", "Side Panel Left Avg B"},
|
||||||
"Side Panel Right Outer Avg G",
|
{"Lightning Model", "Side Panel Right Outer Avg R"},
|
||||||
"Side Panel Right Outer Avg B",
|
{"Lightning Model", "Side Panel Right Outer Avg G"},
|
||||||
"Side Panel Right Inner Avg R",
|
{"Lightning Model", "Side Panel Right Outer Avg B"},
|
||||||
"Side Panel Right Inner Avg G",
|
{"Lightning Model", "Side Panel Right Inner Avg R"},
|
||||||
"Side Panel Right Inner Avg B",
|
{"Lightning Model", "Side Panel Right Inner Avg G"},
|
||||||
"Side Panel Right Avg R",
|
{"Lightning Model", "Side Panel Right Inner Avg B"},
|
||||||
"Side Panel Right Avg G",
|
{"Lightning Model", "Side Panel Right Avg R"},
|
||||||
"Side Panel Right Avg B"
|
{"Lightning Model", "Side Panel Right Avg G"},
|
||||||
|
{"Lightning Model", "Side Panel Right Avg B"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace games::iidx {
|
|||||||
protected:
|
protected:
|
||||||
virtual ~IIDXLocalCamera() {};
|
virtual ~IIDXLocalCamera() {};
|
||||||
|
|
||||||
ULONG m_nRefCount;
|
LONG m_nRefCount;
|
||||||
CRITICAL_SECTION m_critsec;
|
CRITICAL_SECTION m_critsec;
|
||||||
|
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
|||||||
@@ -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");
|
||||||
@@ -254,6 +256,7 @@ namespace games {
|
|||||||
const std::string otoca("Otoca D'or");
|
const std::string otoca("Otoca D'or");
|
||||||
games.push_back(otoca);
|
games.push_back(otoca);
|
||||||
buttons.insert({ otoca, otoca::get_buttons() });
|
buttons.insert({ otoca, otoca::get_buttons() });
|
||||||
|
buttons_help.insert({ otoca, otoca::get_buttons_help() });
|
||||||
file_hints[otoca].emplace_back("arkkep.dll");
|
file_hints[otoca].emplace_back("arkkep.dll");
|
||||||
|
|
||||||
// winning eleven
|
// winning eleven
|
||||||
@@ -322,7 +325,7 @@ namespace games {
|
|||||||
buttons_help.insert({ pc, pc::get_buttons_help() });
|
buttons_help.insert({ pc, pc::get_buttons_help() });
|
||||||
analogs.insert({ pc, pc::get_analogs() });
|
analogs.insert({ pc, pc::get_analogs() });
|
||||||
lights.insert({ pc, pc::get_lights() });
|
lights.insert({ pc, pc::get_lights() });
|
||||||
file_hints[pc].emplace_back("game/svm_Data");
|
file_hints[pc].emplace_back("game/svm.exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> &get_games() {
|
const std::vector<std::string> &get_games() {
|
||||||
@@ -349,6 +352,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;
|
||||||
@@ -389,7 +401,8 @@ namespace games {
|
|||||||
vkey_defaults.push_back(unit == 0 ? VK_NUMPAD9 : 0xFF);
|
vkey_defaults.push_back(unit == 0 ? VK_NUMPAD9 : 0xFF);
|
||||||
|
|
||||||
names.emplace_back(prefix + "00");
|
names.emplace_back(prefix + "00");
|
||||||
vkey_defaults.push_back(unit == 0 ? VK_RETURN : 0xFF);
|
// this used to be VK_RETURN, but that shares a value with normal Enter key
|
||||||
|
vkey_defaults.push_back(unit == 0 ? VK_SUBTRACT : 0xFF);
|
||||||
|
|
||||||
names.emplace_back(prefix + "Decimal");
|
names.emplace_back(prefix + "Decimal");
|
||||||
vkey_defaults.push_back(unit == 0 ? VK_DECIMAL : 0xFF);
|
vkey_defaults.push_back(unit == 0 ? VK_DECIMAL : 0xFF);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ std::vector<Button> &games::otoca::get_buttons() {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string games::otoca::get_buttons_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return " BT_Left Lever BT_Right";
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Light> &games::otoca::get_lights() {
|
std::vector<Light> &games::otoca::get_lights() {
|
||||||
static std::vector<Light> lights;
|
static std::vector<Light> lights;
|
||||||
|
|
||||||
|
|||||||
@@ -30,5 +30,6 @@ namespace games::otoca {
|
|||||||
|
|
||||||
// getters
|
// getters
|
||||||
std::vector<Button> &get_buttons();
|
std::vector<Button> &get_buttons();
|
||||||
|
std::string get_buttons_help();
|
||||||
std::vector<Light> &get_lights();
|
std::vector<Light> &get_lights();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,121 @@
|
|||||||
#include "otoca.h"
|
#include "otoca.h"
|
||||||
#include "p4io.h"
|
#include "p4io.h"
|
||||||
#include "games/shared/printer.h"
|
#include "games/shared/printer.h"
|
||||||
|
#include "util/detour.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
#define OTOCA_DEBUG_VERBOSE 0
|
||||||
|
#if OTOCA_DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace games::otoca {
|
namespace games::otoca {
|
||||||
|
|
||||||
|
bool BYPASS_CAMERA = false;
|
||||||
|
|
||||||
|
// for dev only
|
||||||
|
static bool CAMLIB_CALL_ORIGINAL = false;
|
||||||
|
|
||||||
OtocaGame::OtocaGame() : Game("Otoca D'or") {
|
OtocaGame::OtocaGame() : Game("Otoca D'or") {
|
||||||
}
|
}
|
||||||
|
|
||||||
OtocaGame::~OtocaGame() {
|
OtocaGame::~OtocaGame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef void (__cdecl *LibCameraInit_t)();
|
||||||
|
static LibCameraInit_t LibCameraInit_orig = nullptr;
|
||||||
|
static void __cdecl LibCameraInit_hook() {
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
LibCameraInit_orig();
|
||||||
|
log_debug("otoca::cam", "LibCameraInit returned");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraOpen_t)(double, int, int);
|
||||||
|
static LibCameraOpen_t LibCameraOpen_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraOpen_hook(double a1, int a2, int a3) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (15, 2, 0) returned 0
|
||||||
|
ret = LibCameraOpen_orig(a1, a2, a3);
|
||||||
|
log_debug("otoca::cam", "LibCameraOpen({}, {}, {}) returned {}", a1, a2, a3, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraStop_t)(int);
|
||||||
|
static LibCameraStop_t LibCameraStop_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraStop_hook(int a1) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam,
|
||||||
|
ret = LibCameraStop_orig(a1);
|
||||||
|
log_debug("otoca::cam", "LibCameraStop({}) returned {}", a1, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (__cdecl *LibCameraGetCameraNr_t)();
|
||||||
|
static LibCameraGetCameraNr_t LibCameraGetCameraNr_orig = nullptr;
|
||||||
|
static void __cdecl LibCameraGetCameraNr_hook() {
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
LibCameraGetCameraNr_orig();
|
||||||
|
log_debug("otoca::cam", "LibCameraGetCameraNr returned");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraRun_t)(int);
|
||||||
|
static LibCameraRun_t LibCameraRun_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraRun_hook(int a1) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (0) returned 0
|
||||||
|
ret = LibCameraRun_orig(a1);
|
||||||
|
log_debug("otoca::cam", "LibCameraRun({}) returned {}", a1, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraGetImage_t)(int, void *);
|
||||||
|
static LibCameraGetImage_t LibCameraGetImage_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraGetImage_hook(int a1, void *a2) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// not sure if this is called
|
||||||
|
ret = LibCameraGetImage_orig(a1, a2);
|
||||||
|
log_debug("otoca::cam", "LibCameraGetImage({}, {}) returned {}", a1, fmt::ptr(a2), ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraGetBrightness_t)(int *, int);
|
||||||
|
static LibCameraGetBrightness_t LibCameraGetBrightness_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraGetBrightness_hook(int *a1, int a2) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (ptr,) returned
|
||||||
|
ret = LibCameraGetBrightness_orig(a1, a2);
|
||||||
|
}
|
||||||
|
log_debug("otoca::cam", "LibCameraGetBrightness({}, {}) returned {}", fmt::ptr(a1), a2, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraGetBrightnessRange_t)(int *, int *, int);
|
||||||
|
static LibCameraGetBrightnessRange_t LibCameraGetBrightnessRange_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraGetBrightnessRange_hook(int *a1, int *a2, int a3) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (ptr, ptr, 0) returned 0
|
||||||
|
ret = LibCameraGetBrightnessRange_orig(a1, a2, a3);
|
||||||
|
log_debug("otoca::cam", "LibCameraGetBrightnessRange({}, {}, {}) returned {}", fmt::ptr(a1), fmt::ptr(a2), a3, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void OtocaGame::attach() {
|
void OtocaGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
@@ -23,5 +128,42 @@ namespace games::otoca {
|
|||||||
// initialize IO hooks
|
// initialize IO hooks
|
||||||
p4io_hook();
|
p4io_hook();
|
||||||
games::shared::printer_attach();
|
games::shared::printer_attach();
|
||||||
|
|
||||||
|
if (BYPASS_CAMERA) {
|
||||||
|
libutils::try_library("libcamera.dll");
|
||||||
|
const auto libcamera = "libcamera.dll";
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraInit@@YAXXZ",
|
||||||
|
LibCameraInit_hook, &LibCameraInit_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraOpen@@YA?AW4LIBCAMERA_STATUS@@NHPAUt_libcamera_open_param@@@Z",
|
||||||
|
LibCameraOpen_hook, &LibCameraOpen_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraStop@@YA?AW4LIBCAMERA_STATUS@@H@Z",
|
||||||
|
LibCameraStop_hook, &LibCameraStop_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetCameraNr@@YAHXZ",
|
||||||
|
LibCameraGetCameraNr_hook, &LibCameraGetCameraNr_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraRun@@YA?AW4LIBCAMERA_STATUS@@H@Z",
|
||||||
|
LibCameraRun_hook, &LibCameraRun_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetImage@@YA?AW4LIBCAMERA_STATUS@@HPAX@Z",
|
||||||
|
LibCameraGetImage_hook, &LibCameraGetImage_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetBrightness@@YA?AW4LIBCAMERA_STATUS@@PAJH@Z",
|
||||||
|
LibCameraGetBrightness_hook, &LibCameraGetBrightness_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetBrightnessRange@@YA?AW4LIBCAMERA_STATUS@@PAJ0H@Z",
|
||||||
|
LibCameraGetBrightnessRange_hook, &LibCameraGetBrightnessRange_orig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
namespace games::otoca {
|
namespace games::otoca {
|
||||||
|
|
||||||
|
extern bool BYPASS_CAMERA;
|
||||||
|
|
||||||
class OtocaGame : public games::Game {
|
class OtocaGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
OtocaGame();
|
OtocaGame();
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include "util/execexe.h"
|
#include "util/execexe.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
@@ -9,9 +11,10 @@
|
|||||||
#include "util/tapeled.h"
|
#include "util/tapeled.h"
|
||||||
#include "pc.h"
|
#include "pc.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "util/time.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
|
||||||
namespace games::pc {
|
namespace games::pc {
|
||||||
bool PC_KNOB_MODE = false;
|
|
||||||
|
|
||||||
constexpr float RELATIVE_DECAY = 0.02f;
|
constexpr float RELATIVE_DECAY = 0.02f;
|
||||||
struct FADER_RELATIVE {
|
struct FADER_RELATIVE {
|
||||||
@@ -75,18 +78,22 @@ namespace games::pc {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct AIO_SCI_COMM {
|
struct AIO_SCI_COMM {
|
||||||
|
uint8_t data[0xf8];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB2 {
|
struct AIO_NMGR_IOB2 {
|
||||||
|
uint8_t data[0xe30];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_AC1 {
|
struct AIO_IOB2_BI2X_AC1 {
|
||||||
|
uint8_t data[0x4538];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_WRFIRM {
|
struct AIO_IOB2_BI2X_WRFIRM {
|
||||||
|
uint8_t data[0x20f48];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_AC1__INPUT
|
struct AIO_IOB2_BI2X_AC1__INPUT
|
||||||
@@ -191,6 +198,12 @@ namespace games::pc {
|
|||||||
AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20];
|
AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// verified with XIF-2024122300
|
||||||
|
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4538);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* typedefs
|
* typedefs
|
||||||
*/
|
*/
|
||||||
@@ -338,14 +351,21 @@ namespace games::pc {
|
|||||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2;
|
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]) &&
|
|
||||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
const auto now = get_performance_milliseconds();
|
||||||
val = 0.f;
|
|
||||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left])) {
|
const auto fader_l_socd = socd::socd_clean(
|
||||||
|
0, // Fader-L
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]),
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right]),
|
||||||
|
now);
|
||||||
|
|
||||||
|
if (fader_l_socd == socd::SocdCCW) {
|
||||||
val = -1.0f;
|
val = -1.0f;
|
||||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
} else if (fader_l_socd == socd::SocdCW) {
|
||||||
val = +1.0f;
|
val = +1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
o_DevStatus->Input.CN15_7 = (val < 0.2f) ? 0xFF : 0;
|
o_DevStatus->Input.CN15_7 = (val < 0.2f) ? 0xFF : 0;
|
||||||
o_DevStatus->Input.CN15_6 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
o_DevStatus->Input.CN15_6 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||||
o_DevStatus->Input.CN15_5 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
o_DevStatus->Input.CN15_5 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||||
@@ -363,14 +383,19 @@ namespace games::pc {
|
|||||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2;
|
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]) &&
|
|
||||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
const auto fader_r_socd = socd::socd_clean(
|
||||||
val = 0.f;
|
1, // Fader-R
|
||||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left])) {
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]),
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right]),
|
||||||
|
now);
|
||||||
|
|
||||||
|
if (fader_r_socd == socd::SocdCCW) {
|
||||||
val = -1.0f;
|
val = -1.0f;
|
||||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
} else if (fader_r_socd == socd::SocdCW) {
|
||||||
val = +1.0f;
|
val = +1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
o_DevStatus->Input.CN11_20 = (val < 0.2f) ? 0xFF : 0;
|
o_DevStatus->Input.CN11_20 = (val < 0.2f) ? 0xFF : 0;
|
||||||
o_DevStatus->Input.CN11_19 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
o_DevStatus->Input.CN11_19 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||||
o_DevStatus->Input.CN9_10 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
o_DevStatus->Input.CN9_10 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||||
@@ -737,3 +762,5 @@ namespace games::pc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
namespace games::pc {
|
namespace games::pc {
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// QueryDisplayConfig
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
#include "pc.h"
|
#include "pc.h"
|
||||||
|
|
||||||
#include <format>
|
#include <format>
|
||||||
@@ -12,10 +15,12 @@
|
|||||||
#include "misc/wintouchemu.h"
|
#include "misc/wintouchemu.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
|
||||||
namespace games::pc {
|
namespace games::pc {
|
||||||
std::string PC_INJECT_ARGS = "";
|
std::string PC_INJECT_ARGS = "";
|
||||||
bool PC_NO_IO = false;
|
bool PC_NO_IO = false;
|
||||||
|
bool PC_KNOB_MODE = false;
|
||||||
|
|
||||||
static acioemu::ACIOHandle *acioHandle = nullptr;
|
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||||
static std::wstring portName = L"COM1";
|
static std::wstring portName = L"COM1";
|
||||||
@@ -30,10 +35,10 @@ namespace games::pc {
|
|||||||
if (pRawInputDevices &&
|
if (pRawInputDevices &&
|
||||||
(uiNumDevices > 0) &&
|
(uiNumDevices > 0) &&
|
||||||
(pRawInputDevices[0].hwndTarget == RI_MGR->input_hwnd)) {
|
(pRawInputDevices[0].hwndTarget == RI_MGR->input_hwnd)) {
|
||||||
|
|
||||||
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
|
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, it must be the game; prevent the game from registering for raw input
|
// otherwise, it must be the game; prevent the game from registering for raw input
|
||||||
// and hijacking WM_INPUT messages.
|
// and hijacking WM_INPUT messages.
|
||||||
SetLastError(0xDEADBEEF);
|
SetLastError(0xDEADBEEF);
|
||||||
@@ -56,11 +61,16 @@ namespace games::pc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LONG WINAPI QueryDisplayConfig_hook(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*, DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*) {
|
LONG WINAPI QueryDisplayConfig_hook(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*, DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*) {
|
||||||
// make unity fallback to EnumDisplaySettingsW as I don't
|
// make unity fallback to EnumDisplaySettingsW as I don't
|
||||||
// want to deal with this api which is way more complex
|
// want to deal with this api which is way more complex
|
||||||
return ERROR_NOT_SUPPORTED;
|
return ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PCGame::pre_attach() {
|
||||||
|
// SOCD
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||||
|
}
|
||||||
|
|
||||||
void PCGame::attach() {
|
void PCGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
@@ -78,17 +88,22 @@ namespace games::pc {
|
|||||||
execexe::load_library("libaio-iob2_video.dll");
|
execexe::load_library("libaio-iob2_video.dll");
|
||||||
execexe::load_library("win10actlog.dll");
|
execexe::load_library("win10actlog.dll");
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
if (!PC_NO_IO) {
|
if (!PC_NO_IO) {
|
||||||
bi2x_hook_init();
|
bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto user32Dll = "user32.dll";
|
const auto user32Dll = "user32.dll";
|
||||||
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
|
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
|
||||||
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
|
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
|
||||||
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
||||||
QueryDisplayConfig_hook, &QueryDisplayConfig_orig);
|
QueryDisplayConfig_hook, &QueryDisplayConfig_orig);
|
||||||
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
||||||
EnumDisplaySettingsW_hook, &EnumDisplaySettingsW_orig);
|
EnumDisplaySettingsW_hook, &EnumDisplaySettingsW_orig);
|
||||||
|
|
||||||
if (GRAPHICS_SHOW_CURSOR) {
|
if (GRAPHICS_SHOW_CURSOR) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace games::pc {
|
|||||||
public:
|
public:
|
||||||
PCGame() : Game("Polaris Chord") {}
|
PCGame() : Game("Polaris Chord") {}
|
||||||
|
|
||||||
|
virtual void pre_attach() override;
|
||||||
virtual void attach() override;
|
virtual void attach() override;
|
||||||
virtual void detach() override;
|
virtual void detach() override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
@@ -15,15 +17,19 @@ namespace games::qks {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct AIO_SCI_COMM {
|
struct AIO_SCI_COMM {
|
||||||
|
uint8_t data[0xf8];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB {
|
struct AIO_NMGR_IOB {
|
||||||
|
uint8_t data[0xa00];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_AC1 {
|
struct AIO_IOB2_BI2X_AC1 {
|
||||||
|
uint8_t data[0x3908];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_WRFIRM {
|
struct AIO_IOB2_BI2X_WRFIRM {
|
||||||
|
uint8_t data[0x20f48];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB__NODEINFO {
|
struct AIO_NMGR_IOB__NODEINFO {
|
||||||
@@ -174,6 +180,12 @@ namespace games::qks {
|
|||||||
AIO_IOB2_BI2X_AC1__SETTING Setting;
|
AIO_IOB2_BI2X_AC1__SETTING Setting;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// verified with UKS-001-2024030600
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x3908);
|
||||||
|
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB) == 0xa00);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* typedefs
|
* typedefs
|
||||||
*/
|
*/
|
||||||
@@ -602,4 +614,6 @@ namespace games::qks {
|
|||||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace games::qks {
|
namespace games::qks {
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -86,9 +86,13 @@ namespace games::qks {
|
|||||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
|
||||||
execexe_CreateFileA_hook,&execexe_CreateFileA_orig);
|
execexe_CreateFileA_hook,&execexe_CreateFileA_orig);
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
// insert BI2X hooks
|
// insert BI2X hooks
|
||||||
bi2x_hook_init();
|
bi2x_hook_init();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// add card reader
|
// add card reader
|
||||||
acioHandle = new acioemu::ACIOHandle(portName.c_str());
|
acioHandle = new acioemu::ACIOHandle(portName.c_str());
|
||||||
devicehook_init_trampoline();
|
devicehook_init_trampoline();
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ namespace games::qma {
|
|||||||
// create quiz directory
|
// create quiz directory
|
||||||
static bool dirs_created = false;
|
static bool dirs_created = false;
|
||||||
if (!dirs_created && _wcsnicmp(lpFileName, L"E:\\LMA", 6) == 0) {
|
if (!dirs_created && _wcsnicmp(lpFileName, L"E:\\LMA", 6) == 0) {
|
||||||
|
dirs_created = true;
|
||||||
fileutils::dir_create_recursive("E:\\LMA\\quiz11");
|
fileutils::dir_create_recursive("E:\\LMA\\quiz11");
|
||||||
fileutils::dir_create_recursive("E:\\LMA\\quiz13");
|
fileutils::dir_create_recursive("E:\\LMA\\quiz13");
|
||||||
fileutils::dir_create_recursive("E:\\LMA\\quiz15");
|
fileutils::dir_create_recursive("E:\\LMA\\quiz15");
|
||||||
dirs_created = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return result
|
// return result
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -10,12 +12,22 @@
|
|||||||
#include "launcher/options.h"
|
#include "launcher/options.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "games/sdvx/sdvx.h"
|
#include "games/sdvx/sdvx.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
#include "util/tapeled.h"
|
#include "util/tapeled.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "acioemu/icca.h"
|
#include "acioemu/icca.h"
|
||||||
|
|
||||||
|
#define DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace games::sdvx {
|
namespace games::sdvx {
|
||||||
constexpr bool BI2X_PASSTHROUGH = false;
|
constexpr bool BI2X_PASSTHROUGH = false;
|
||||||
bool BI2X_INITIALIZED = false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* class definitions
|
* class definitions
|
||||||
@@ -26,20 +38,54 @@ namespace games::sdvx {
|
|||||||
uint8_t data[0x10];
|
uint8_t data[0x10];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB2 {
|
struct AIO_NMGR_IOB2_VTABLE {
|
||||||
uint8_t dummy0[0x50];
|
// other functions here, but they never get called
|
||||||
|
uint8_t dummy[0x50];
|
||||||
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
||||||
uint8_t dummy1[0x9A0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR_IOB2 {
|
||||||
|
AIO_NMGR_IOB2_VTABLE *vptr;
|
||||||
|
uint8_t dummy[0x9F0];
|
||||||
|
};
|
||||||
|
|
||||||
|
// confirmed in EG final in aioNMgrIob2_Create
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0x9F8);
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_UFC {
|
struct AIO_IOB2_BI2X_UFC {
|
||||||
// who knows
|
// who knows
|
||||||
uint8_t data[0x13F8];
|
uint8_t data[0x39D8];
|
||||||
|
};
|
||||||
|
|
||||||
|
// confirmed in EG final in aioIob2Bi2xUFC_Create
|
||||||
|
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];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -66,7 +112,7 @@ namespace games::sdvx {
|
|||||||
// libaio-iob.dll
|
// libaio-iob.dll
|
||||||
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
||||||
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
||||||
typedef AIO_NMGR_IOB2** (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
typedef AIO_NMGR_IOB2* (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* function pointers
|
* function pointers
|
||||||
@@ -133,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
|
||||||
@@ -142,41 +188,51 @@ 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();
|
||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Left])) {
|
const auto vol_l_state = socd::socd_clean(0,
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Left]),
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Right]),
|
||||||
|
now);
|
||||||
|
if (vol_l_state == socd::SocdCCW) {
|
||||||
VOL_L -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
VOL_L -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
} else if (vol_l_state == socd::SocdCW) {
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Right])) {
|
|
||||||
VOL_L += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
VOL_L += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Left])) {
|
const auto vol_r_state = socd::socd_clean(1,
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Left]),
|
||||||
|
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Right]),
|
||||||
|
now);
|
||||||
|
if (vol_r_state == socd::SocdCCW) {
|
||||||
VOL_R -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
VOL_R -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
} else if (vol_r_state == socd::SocdCW) {
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Right])) {
|
|
||||||
VOL_R += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
VOL_R += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,8 +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(
|
||||||
|
"bi2x_hook",
|
||||||
|
"knobs = {} {}",
|
||||||
|
status->AnalogLeft,
|
||||||
|
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,
|
||||||
@@ -345,21 +407,21 @@ namespace games::sdvx {
|
|||||||
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
||||||
}
|
}
|
||||||
|
|
||||||
static AIO_NMGR_IOB2** __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
static AIO_NMGR_IOB2* __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
||||||
if (aioNmgrIob2 == nullptr) {
|
if (aioNmgrIob2 == nullptr) {
|
||||||
aioNmgrIob2 = new AIO_NMGR_IOB2;
|
aioNmgrIob2 = new AIO_NMGR_IOB2{};
|
||||||
memset(aioNmgrIob2, 0x0, sizeof(AIO_NMGR_IOB2));
|
aioNmgrIob2->vptr = new AIO_NMGR_IOB2_VTABLE{};
|
||||||
aioNmgrIob2->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
aioNmgrIob2->vptr->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||||
}
|
}
|
||||||
log_info("bi2x_hook", "aioNMgrIob2_Create");
|
log_info("bi2x_hook", "aioNMgrIob2_Create returned {}, size=0x{:x}, vptr @ {}",
|
||||||
BI2X_INITIALIZED = true;
|
fmt::ptr(aioNmgrIob2), sizeof(*aioNmgrIob2), fmt::ptr(aioNmgrIob2->vptr));
|
||||||
|
|
||||||
// enable hack to make PIN pad work for KFC in BI2X mode
|
// enable hack to make PIN pad work for KFC in BI2X mode
|
||||||
// this explicit check in the I/O init path is necessary
|
// this explicit check in the I/O init path is necessary
|
||||||
// (as opposed to just doing a check for "isValkyrieCabMode?")
|
// (as opposed to just doing a check for "isValkyrieCabMode?")
|
||||||
// because there are hex edits that allow you to use legacy (KFC/BIO2) IO while in Valk mode
|
// because there are hex edits that allow you to use legacy (KFC/BIO2) IO while in Valk mode
|
||||||
acioemu::ICCA_DEVICE_HACK = true;
|
acioemu::ICCA_DEVICE_HACK = true;
|
||||||
return &aioNmgrIob2;
|
return aioNmgrIob2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
||||||
@@ -411,3 +473,5 @@ namespace games::sdvx {
|
|||||||
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
namespace games::sdvx {
|
namespace games::sdvx {
|
||||||
|
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -65,76 +65,78 @@ std::vector<Light> &games::sdvx::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Sound Voltex");
|
lights = GameAPI::Lights::getLights("Sound Voltex");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"BT-A",
|
{
|
||||||
"BT-B",
|
{"Common", "BT-A"},
|
||||||
"BT-C",
|
{"Common", "BT-B"},
|
||||||
"BT-D",
|
{"Common", "BT-C"},
|
||||||
"FX-L",
|
{"Common", "BT-D"},
|
||||||
"FX-R",
|
{"Common", "FX-L"},
|
||||||
"Start",
|
{"Common", "FX-R"},
|
||||||
"Wing Left Up R",
|
{"Common", "Start"},
|
||||||
"Wing Left Up G",
|
{"Nemsys", "Wing Left Up R"},
|
||||||
"Wing Left Up B",
|
{"Nemsys", "Wing Left Up G"},
|
||||||
"Wing Right Up R",
|
{"Nemsys", "Wing Left Up B"},
|
||||||
"Wing Right Up G",
|
{"Nemsys", "Wing Right Up R"},
|
||||||
"Wing Right Up B",
|
{"Nemsys", "Wing Right Up G"},
|
||||||
"Wing Left Low R",
|
{"Nemsys", "Wing Right Up B"},
|
||||||
"Wing Left Low G",
|
{"Nemsys", "Wing Left Low R"},
|
||||||
"Wing Left Low B",
|
{"Nemsys", "Wing Left Low G"},
|
||||||
"Wing Right Low R",
|
{"Nemsys", "Wing Left Low B"},
|
||||||
"Wing Right Low G",
|
{"Nemsys", "Wing Right Low R"},
|
||||||
"Wing Right Low B",
|
{"Nemsys", "Wing Right Low G"},
|
||||||
"Woofer R",
|
{"Nemsys", "Wing Right Low B"},
|
||||||
"Woofer G",
|
{"Nemsys", "Woofer R"},
|
||||||
"Woofer B",
|
{"Nemsys", "Woofer G"},
|
||||||
"Controller R",
|
{"Nemsys", "Woofer B"},
|
||||||
"Controller G",
|
{"Nemsys", "Controller R"},
|
||||||
"Controller B",
|
{"Nemsys", "Controller G"},
|
||||||
"Generator R",
|
{"Nemsys", "Controller B"},
|
||||||
"Generator G",
|
{"Nemsys", "Generator R"},
|
||||||
"Generator B",
|
{"Nemsys", "Generator G"},
|
||||||
"Pop",
|
{"Nemsys", "Generator B"},
|
||||||
"Title Left",
|
{"Nemsys", "Pop"},
|
||||||
"Title Right",
|
{"Nemsys", "Title Left"},
|
||||||
"Volume Sound",
|
{"Nemsys", "Title Right"},
|
||||||
"Volume Headphone",
|
{"System", "Volume Sound"},
|
||||||
"Volume External",
|
{"System", "Volume Headphone"},
|
||||||
"Volume Woofer",
|
{"System", "Volume External"},
|
||||||
"IC Card Reader R",
|
{"System", "Volume Woofer"},
|
||||||
"IC Card Reader G",
|
{"Valkyrie", "IC Card Reader R"},
|
||||||
"IC Card Reader B",
|
{"Valkyrie", "IC Card Reader G"},
|
||||||
"Title Avg R",
|
{"Valkyrie", "IC Card Reader B"},
|
||||||
"Title Avg G",
|
{"Valkyrie", "Title Avg R"},
|
||||||
"Title Avg B",
|
{"Valkyrie", "Title Avg G"},
|
||||||
"Upper Left Speaker Avg R",
|
{"Valkyrie", "Title Avg B"},
|
||||||
"Upper Left Speaker Avg G",
|
{"Valkyrie", "Upper Left Speaker Avg R"},
|
||||||
"Upper Left Speaker Avg B",
|
{"Valkyrie", "Upper Left Speaker Avg G"},
|
||||||
"Upper Right Speaker Avg R",
|
{"Valkyrie", "Upper Left Speaker Avg B"},
|
||||||
"Upper Right Speaker Avg G",
|
{"Valkyrie", "Upper Right Speaker Avg R"},
|
||||||
"Upper Right Speaker Avg B",
|
{"Valkyrie", "Upper Right Speaker Avg G"},
|
||||||
"Left Wing Avg R",
|
{"Valkyrie", "Upper Right Speaker Avg B"},
|
||||||
"Left Wing Avg G",
|
{"Valkyrie", "Left Wing Avg R"},
|
||||||
"Left Wing Avg B",
|
{"Valkyrie", "Left Wing Avg G"},
|
||||||
"Right Wing Avg R",
|
{"Valkyrie", "Left Wing Avg B"},
|
||||||
"Right Wing Avg G",
|
{"Valkyrie", "Right Wing Avg R"},
|
||||||
"Right Wing Avg B",
|
{"Valkyrie", "Right Wing Avg G"},
|
||||||
"Lower Left Speaker Avg R",
|
{"Valkyrie", "Right Wing Avg B"},
|
||||||
"Lower Left Speaker Avg G",
|
{"Valkyrie", "Lower Left Speaker Avg R"},
|
||||||
"Lower Left Speaker Avg B",
|
{"Valkyrie", "Lower Left Speaker Avg G"},
|
||||||
"Lower Right Speaker Avg R",
|
{"Valkyrie", "Lower Left Speaker Avg B"},
|
||||||
"Lower Right Speaker Avg G",
|
{"Valkyrie", "Lower Right Speaker Avg R"},
|
||||||
"Lower Right Speaker Avg B",
|
{"Valkyrie", "Lower Right Speaker Avg G"},
|
||||||
"Control Panel Avg R",
|
{"Valkyrie", "Lower Right Speaker Avg B"},
|
||||||
"Control Panel Avg G",
|
{"Valkyrie", "Control Panel Avg R"},
|
||||||
"Control Panel Avg B",
|
{"Valkyrie", "Control Panel Avg G"},
|
||||||
"Woofer Avg R",
|
{"Valkyrie", "Control Panel Avg B"},
|
||||||
"Woofer Avg G",
|
{"Valkyrie", "Woofer Avg R"},
|
||||||
"Woofer Avg B",
|
{"Valkyrie", "Woofer Avg G"},
|
||||||
"V Unit Avg R",
|
{"Valkyrie", "Woofer Avg B"},
|
||||||
"V Unit Avg G",
|
{"Valkyrie", "V Unit Avg R"},
|
||||||
"V Unit Avg B"
|
{"Valkyrie", "V Unit Avg G"},
|
||||||
|
{"Valkyrie", "V Unit Avg B"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <external/robin_hood.h>
|
#include <external/robin_hood.h>
|
||||||
|
|
||||||
#include "avs/game.h"
|
|
||||||
#include "games/shared/lcdhandle.h"
|
#include "games/shared/lcdhandle.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "hooks/audio/audio.h"
|
#include "hooks/audio/audio.h"
|
||||||
@@ -19,9 +18,12 @@
|
|||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "misc/wintouchemu.h"
|
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "misc/nativetouchhook.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
@@ -64,10 +66,6 @@ namespace games::sdvx {
|
|||||||
|
|
||||||
// prevent the game from randomly picking an ASIO device
|
// prevent the game from randomly picking an ASIO device
|
||||||
if (!USE_ASIO) {
|
if (!USE_ASIO) {
|
||||||
log_info(
|
|
||||||
"sdvx::asio",
|
|
||||||
"game tried to access HKLM\\SOFTWARE\\ASIO but ASIO is disabled; "
|
|
||||||
"access prevented, ASIO init will fail");
|
|
||||||
return ERROR_FILE_NOT_FOUND;
|
return ERROR_FILE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
// convince the game to use our hook (so we can swap in the preferred device)
|
// convince the game to use our hook (so we can swap in the preferred device)
|
||||||
@@ -236,6 +234,27 @@ namespace games::sdvx {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool logged_missing_layer_error = false;
|
||||||
|
// this is raw SHIFT-JIS text for
|
||||||
|
// W:BM2D: CreateLayer() 指定したレイヤーは存在しません
|
||||||
|
if (!logged_missing_layer_error &&
|
||||||
|
data.find(
|
||||||
|
"\x57\x3A\x42\x4D\x32\x44\x3A\x20\x43\x72\x65\x61\x74\x65\x4C\x61\x79\x65\x72\x28\x29\x20"
|
||||||
|
"\x8E\x77\x92\xE8\x82\xB5\x82\xBD\x83\x8C\x83\x43\x83\x84\x81\x5B\x82\xCD\x91\xB6\x8D\xDD"
|
||||||
|
"\x82\xB5\x82\xDC\x82\xB9\x82\xF1") != std::string::npos) {
|
||||||
|
|
||||||
|
logged_missing_layer_error = true;
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"sdvx game engine detected missing or corrupt file(s); raw SHIFT-JIS error was:",
|
||||||
|
std::string(" ") + data,
|
||||||
|
" this will almost certainly cause your game to crash",
|
||||||
|
" this is often caused by mismatched game DLLs or missing resources from game updates",
|
||||||
|
" double check the integrity of your game data and try again"
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,9 +350,8 @@ namespace games::sdvx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SPICE64 // SDVX5+ specific code
|
#ifdef SPICE64 // SDVX5+ specific code
|
||||||
this->VALKYRIE_MODEL = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
|
||||||
// check -monitor + UFC mode
|
// check -monitor + UFC mode
|
||||||
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && this->VALKYRIE_MODEL) {
|
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && is_valkyrie_model()) {
|
||||||
SHOW_VM_MONITOR_WARNING = true;
|
SHOW_VM_MONITOR_WARNING = true;
|
||||||
log_warning(
|
log_warning(
|
||||||
"sdvx",
|
"sdvx",
|
||||||
@@ -362,6 +380,16 @@ namespace games::sdvx {
|
|||||||
this->detect_sound_output_device();
|
this->detect_sound_output_device();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// SOCD
|
||||||
|
auto options = games::get_options(eamuse_get_game());
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||||
|
if (options->at(launcher::Options::SDVXDigitalKnobSocd).is_active()) {
|
||||||
|
if (options->at(launcher::Options::SDVXDigitalKnobSocd).value_text() == "neutral") {
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::Neutral;
|
||||||
|
} else if (options->at(launcher::Options::SDVXDigitalKnobSocd).value_text() == "last") {
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDVXGame::attach() {
|
void SDVXGame::attach() {
|
||||||
@@ -370,7 +398,7 @@ namespace games::sdvx {
|
|||||||
#ifdef SPICE64 // SDVX5+ specific code
|
#ifdef SPICE64 // SDVX5+ specific code
|
||||||
|
|
||||||
// LCD handle
|
// LCD handle
|
||||||
if (!this->VALKYRIE_MODEL) {
|
if (!is_valkyrie_model()) {
|
||||||
devicehook_init();
|
devicehook_init();
|
||||||
devicehook_add(new games::shared::LCDHandle());
|
devicehook_add(new games::shared::LCDHandle());
|
||||||
}
|
}
|
||||||
@@ -409,10 +437,12 @@ namespace games::sdvx {
|
|||||||
nvapi_hook::initialize(avs::game::DLL_INSTANCE);
|
nvapi_hook::initialize(avs::game::DLL_INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->VALKYRIE_MODEL) {
|
if (is_valkyrie_model()) {
|
||||||
// hook touch window
|
if (NATIVETOUCH) {
|
||||||
// in windowed mode, game can accept mouse input on the second screen
|
nativetouchhook::hook(avs::game::DLL_INSTANCE);
|
||||||
if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
} else if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||||
|
// hook touch window
|
||||||
|
// in windowed mode, game can accept mouse input on the second screen
|
||||||
wintouchemu::FORCE = true;
|
wintouchemu::FORCE = true;
|
||||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
wintouchemu::hook_title_ends(
|
wintouchemu::hook_title_ends(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
|
|
||||||
namespace games::sdvx {
|
namespace games::sdvx {
|
||||||
@@ -20,12 +21,18 @@ namespace games::sdvx {
|
|||||||
extern bool NATIVETOUCH;
|
extern bool NATIVETOUCH;
|
||||||
extern uint8_t DIGITAL_KNOB_SENS;
|
extern uint8_t DIGITAL_KNOB_SENS;
|
||||||
extern std::optional<std::string> ASIO_DRIVER;
|
extern std::optional<std::string> ASIO_DRIVER;
|
||||||
extern bool BI2X_INITIALIZED;
|
|
||||||
extern SdvxOverlayPosition OVERLAY_POS;
|
extern SdvxOverlayPosition OVERLAY_POS;
|
||||||
|
|
||||||
// states
|
// states
|
||||||
extern bool SHOW_VM_MONITOR_WARNING;
|
extern bool SHOW_VM_MONITOR_WARNING;
|
||||||
|
|
||||||
|
static inline bool is_valkyrie_model() {
|
||||||
|
return (
|
||||||
|
avs::game::is_model("KFC") &&
|
||||||
|
(avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class SDVXGame : public games::Game {
|
class SDVXGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
SDVXGame();
|
SDVXGame();
|
||||||
@@ -34,11 +41,6 @@ namespace games::sdvx {
|
|||||||
virtual void post_attach() override;
|
virtual void post_attach() override;
|
||||||
virtual void detach() override;
|
virtual void detach() override;
|
||||||
private:
|
private:
|
||||||
// don't be tempted to make this into a public or a global variable
|
|
||||||
// various modules need to check if VM mode is active and they should
|
|
||||||
// not depend on the fact that SDVX module is active (since it can be
|
|
||||||
// inactive on cabs or partial I/O setup)
|
|
||||||
bool VALKYRIE_MODEL = false;
|
|
||||||
void detect_sound_output_device();
|
void detect_sound_output_device();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ namespace games::shared {
|
|||||||
image_data[index + 2] = tmp;
|
image_data[index + 2] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avs::game::is_model({"KLP", "KFC"})) {
|
if (avs::game::is_model({"KLP", "KFC", "NCG"})) {
|
||||||
// rotate image clockwise
|
// rotate image clockwise
|
||||||
log_misc("printer", "rotate clockwise...");
|
log_misc("printer", "rotate clockwise...");
|
||||||
auto rotated = new uint8_t[image_width * image_height * 3];
|
auto rotated = new uint8_t[image_width * image_height * 3];
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ namespace hooks::audio {
|
|||||||
bool USE_DUMMY = false;
|
bool USE_DUMMY = false;
|
||||||
WAVEFORMATEXTENSIBLE FORMAT {};
|
WAVEFORMATEXTENSIBLE FORMAT {};
|
||||||
std::optional<Backend> BACKEND = std::nullopt;
|
std::optional<Backend> BACKEND = std::nullopt;
|
||||||
size_t ASIO_DRIVER_ID = 0;
|
std::optional<size_t> ASIO_DRIVER_ID = std::nullopt;
|
||||||
|
std::string ASIO_DRIVER_NAME = "";
|
||||||
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
||||||
|
|
||||||
// private globals
|
// private globals
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmreg.h>
|
#include <mmreg.h>
|
||||||
@@ -20,7 +21,8 @@ namespace hooks::audio {
|
|||||||
extern bool USE_DUMMY;
|
extern bool USE_DUMMY;
|
||||||
extern WAVEFORMATEXTENSIBLE FORMAT;
|
extern WAVEFORMATEXTENSIBLE FORMAT;
|
||||||
extern std::optional<Backend> BACKEND;
|
extern std::optional<Backend> BACKEND;
|
||||||
extern size_t ASIO_DRIVER_ID;
|
extern std::optional<size_t> ASIO_DRIVER_ID;
|
||||||
|
extern std::string ASIO_DRIVER_NAME;
|
||||||
extern bool ASIO_FORCE_UNLOAD_ON_STOP;
|
extern bool ASIO_FORCE_UNLOAD_ON_STOP;
|
||||||
extern bool LOW_LATENCY_SHARED_WASAPI;
|
extern bool LOW_LATENCY_SHARED_WASAPI;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,13 +218,40 @@ void AsioBackend::set_thread_state(AsioThreadState state) {
|
|||||||
bool AsioBackend::load_driver() {
|
bool AsioBackend::load_driver() {
|
||||||
AsioDriverList asio_driver_list;
|
AsioDriverList asio_driver_list;
|
||||||
|
|
||||||
for (const auto &driver : asio_driver_list.driver_list) {
|
size_t driver_id = 0;
|
||||||
log_info("audio::asio", "Driver {}", driver.id);
|
std::string driver_name = "";
|
||||||
log_info("audio::asio", "... Name : {}", driver.name);
|
if (hooks::audio::ASIO_DRIVER_ID.has_value()) {
|
||||||
log_info("audio::asio", "... Path : {}", driver.dll_path);
|
driver_id = hooks::audio::ASIO_DRIVER_ID.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto driver_id = hooks::audio::ASIO_DRIVER_ID;
|
for (const auto &driver : asio_driver_list.driver_list) {
|
||||||
|
log_info("audio::asio", " Driver ID : {}", driver.id);
|
||||||
|
log_info("audio::asio", " ... Name : {}", driver.name);
|
||||||
|
log_info("audio::asio", " ... Path : {}", driver.dll_path);
|
||||||
|
|
||||||
|
if (!hooks::audio::ASIO_DRIVER_NAME.empty()) {
|
||||||
|
// match on driver name
|
||||||
|
if (driver.name == hooks::audio::ASIO_DRIVER_NAME) {
|
||||||
|
driver_id = driver.id;
|
||||||
|
driver_name = driver.name;
|
||||||
|
}
|
||||||
|
} else if (driver.id == driver_id) {
|
||||||
|
// match on driver index (0 for default or user-specified)
|
||||||
|
driver_name = driver.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver_name.empty()) {
|
||||||
|
if (!hooks::audio::ASIO_DRIVER_NAME.empty()) {
|
||||||
|
log_warning(
|
||||||
|
"audio::asio",
|
||||||
|
"tried to look for ASIO driver but failed: {}", hooks::audio::ASIO_DRIVER_NAME);
|
||||||
|
}
|
||||||
|
log_fatal(
|
||||||
|
"audio::asio",
|
||||||
|
"could not find ASIO driver to use for conversion, fix your audio options and try again");
|
||||||
|
}
|
||||||
|
log_info("audio::asio", "will attempt to use the following ASIO driver: ID = {}, Name = {}", driver_id, driver_name);
|
||||||
|
|
||||||
IAsio *driver = nullptr;
|
IAsio *driver = nullptr;
|
||||||
auto ret = asio_driver_list.open_driver(driver_id, reinterpret_cast<void **>(&driver));
|
auto ret = asio_driver_list.open_driver(driver_id, reinterpret_cast<void **>(&driver));
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
#include "games/sdvx/sdvx.h"
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "games/mfc/mfc.h"
|
#include "games/mfc/mfc.h"
|
||||||
@@ -41,6 +42,15 @@
|
|||||||
#undef min
|
#undef min
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define D3D9_BACKEND_DEBUG 0
|
||||||
|
|
||||||
|
#if D3D9_BACKEND_DEBUG
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CHECK_RESULT(x) \
|
#define CHECK_RESULT(x) \
|
||||||
do { \
|
do { \
|
||||||
HRESULT __ret = (x); \
|
HRESULT __ret = (x); \
|
||||||
@@ -268,7 +278,7 @@ static bool is_dx9_on_12_enabled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
|
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
|
||||||
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, MAXINT)) {
|
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, INT_MAX)) {
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
"dx9on12 was force enabled by user (-dx9on12)",
|
"dx9on12 was force enabled by user (-dx9on12)",
|
||||||
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
|
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
|
||||||
@@ -514,7 +524,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
|||||||
if (!modified && games::iidx::is_tdj_fhd() && refresh < 110) {
|
if (!modified && games::iidx::is_tdj_fhd() && refresh < 110) {
|
||||||
if ((width == 1920 && height == 1080) ||
|
if ((width == 1920 && height == 1080) ||
|
||||||
(width == 1280 && height == 720)){
|
(width == 1280 && height == 720)){
|
||||||
log_misc(
|
log_debug(
|
||||||
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for TDJ FHD)",
|
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for TDJ FHD)",
|
||||||
Mode, width, height, refresh);
|
Mode, width, height, refresh);
|
||||||
memset(pMode, 0, sizeof(*pMode));
|
memset(pMode, 0, sizeof(*pMode));
|
||||||
@@ -527,7 +537,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
|||||||
// - skip 75 and 90 Hz entries because LDJ game timing is messed up with it
|
// - skip 75 and 90 Hz entries because LDJ game timing is messed up with it
|
||||||
// (TDJ should be fine as it assumes a fixed 60 Hz timing)
|
// (TDJ should be fine as it assumes a fixed 60 Hz timing)
|
||||||
if (!modified && (width == 1360 || width == 1366 || refresh == 75 || refresh == 90)) {
|
if (!modified && (width == 1360 || width == 1366 || refresh == 75 || refresh == 90)) {
|
||||||
log_misc(
|
log_debug(
|
||||||
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for LDJ/TDJ)",
|
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (for LDJ/TDJ)",
|
||||||
Mode, width, height, refresh);
|
Mode, width, height, refresh);
|
||||||
memset(pMode, 0, sizeof(*pMode));
|
memset(pMode, 0, sizeof(*pMode));
|
||||||
@@ -535,7 +545,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!modified && !games::iidx::TDJ_MODE && games::iidx::FORCE_720P && (height > 720)) {
|
if (!modified && !games::iidx::TDJ_MODE && games::iidx::FORCE_720P && (height > 720)) {
|
||||||
log_misc(
|
log_debug(
|
||||||
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (-iidxforce720p)",
|
"graphics::d3d9", "removing mode {}, {}x{} @ {}Hz (-iidxforce720p)",
|
||||||
Mode, width, height, refresh);
|
Mode, width, height, refresh);
|
||||||
memset(pMode, 0, sizeof(*pMode));
|
memset(pMode, 0, sizeof(*pMode));
|
||||||
@@ -803,6 +813,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");
|
||||||
@@ -927,26 +945,37 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
|
|
||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
auto *params = &pPresentationParameters[i];
|
auto *params = &pPresentationParameters[i];
|
||||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
if (!GRAPHICS_WINDOWED){
|
||||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
if (i == 0) {
|
||||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||||
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||||
|
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"use custom resolution {}x{} => {}x{}",
|
||||||
|
params->BackBufferWidth, params->BackBufferHeight,
|
||||||
|
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
|
||||||
|
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
|
||||||
|
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
|
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||||
|
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"swap full orientation {}x{} => {}x{}",
|
||||||
|
params->BackBufferWidth, params->BackBufferHeight,
|
||||||
|
params->BackBufferHeight, params->BackBufferWidth);
|
||||||
|
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
||||||
|
}
|
||||||
|
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
|
||||||
log_misc(
|
log_misc(
|
||||||
"graphics::d3d9",
|
"graphics::d3d9",
|
||||||
"use custom resolution {}x{} => {}x{}",
|
"use custom sub resolution {}x{} => {}x{}",
|
||||||
params->BackBufferWidth, params->BackBufferHeight,
|
params->BackBufferWidth, params->BackBufferHeight,
|
||||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
|
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first,
|
||||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
|
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second);
|
||||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
|
||||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
|
||||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
|
||||||
log_misc(
|
|
||||||
"graphics::d3d9",
|
|
||||||
"swap full orientation {}x{} => {}x{}",
|
|
||||||
params->BackBufferWidth, params->BackBufferHeight,
|
|
||||||
params->BackBufferHeight, params->BackBufferWidth);
|
|
||||||
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -974,12 +1003,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||||
|
|
||||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
if (!GRAPHICS_WINDOWED) {
|
||||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
if (i == 0) {
|
||||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||||
|
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||||
|
}
|
||||||
|
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
|
||||||
|
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
|
||||||
|
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1022,6 +1056,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");
|
||||||
@@ -1142,9 +1187,15 @@ IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
||||||
|
// iidx/sdvx
|
||||||
|
int swapchain = 1;
|
||||||
|
if (games::gitadora::is_arena_model()) {
|
||||||
|
swapchain = 3;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE && SUB_SWAP_CHAIN == nullptr) {
|
if (!ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE && SUB_SWAP_CHAIN == nullptr) {
|
||||||
ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = true;
|
ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = true;
|
||||||
HRESULT hr = wrapped_device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
HRESULT hr = wrapped_device->GetSwapChain(swapchain, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning(
|
log_warning(
|
||||||
"graphics::d3d9",
|
"graphics::d3d9",
|
||||||
@@ -1353,11 +1404,11 @@ void graphics_d3d9_on_present(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for IIDX TDJ / SDVX UFC, handle subscreen
|
// for IIDX TDJ / SDVX UFC, handle subscreen
|
||||||
const bool is_vm =
|
const bool is_vm = games::sdvx::is_valkyrie_model();
|
||||||
avs::game::is_model("KFC") &&
|
|
||||||
(avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H');
|
|
||||||
const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE;
|
const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE;
|
||||||
if (is_vm || is_tdj) {
|
const bool is_gfdm_arena =
|
||||||
|
games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW;
|
||||||
|
if (is_vm || is_tdj || is_gfdm_arena) {
|
||||||
graphics_d3d9_ldj_on_present(wrapped_device);
|
graphics_d3d9_ldj_on_present(wrapped_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1519,4 +1570,4 @@ void update_backbuffer_dimensions(D3DPRESENT_PARAMETERS *params) {
|
|||||||
params->BackBufferHeight);
|
params->BackBufferHeight);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
#include "util/flags_helper.h"
|
#include "util/flags_helper.h"
|
||||||
@@ -249,8 +250,16 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain(
|
|||||||
WRAP_VERBOSE;
|
WRAP_VERBOSE;
|
||||||
|
|
||||||
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
||||||
|
bool create_swap_chain = false;
|
||||||
if (avs::game::is_model({"LDJ", "KFC"})) {
|
if (avs::game::is_model({"LDJ", "KFC"})) {
|
||||||
|
create_swap_chain = true;
|
||||||
|
} else if (games::gitadora::is_arena_model() &&
|
||||||
|
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||||
|
pPresentationParameters->BackBufferWidth == 800) {
|
||||||
|
create_swap_chain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create_swap_chain) {
|
||||||
if (SUCCEEDED(hr) && !sub_swapchain) {
|
if (SUCCEEDED(hr) && !sub_swapchain) {
|
||||||
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
||||||
sub_swapchain->should_run_hooks = false;
|
sub_swapchain->should_run_hooks = false;
|
||||||
@@ -295,7 +304,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
|
|||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool swap = false;
|
||||||
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) {
|
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) {
|
||||||
|
swap = true;
|
||||||
|
}
|
||||||
|
if (games::gitadora::is_arena_model() &&
|
||||||
|
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||||
|
iSwapChain == 3) {
|
||||||
|
swap = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swap) {
|
||||||
if (sub_swapchain) {
|
if (sub_swapchain) {
|
||||||
sub_swapchain->AddRef();
|
sub_swapchain->AddRef();
|
||||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain);
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain);
|
||||||
@@ -574,7 +593,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::UpdateTexture(
|
|||||||
WRAP_DEBUG;
|
WRAP_DEBUG;
|
||||||
|
|
||||||
if (CUSTOM_RESET) {
|
if (CUSTOM_RESET) {
|
||||||
#ifdef __GNUC__
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
|
||||||
// Do a pointer compare rather than `QueryInterface` because it is a lot cheaper than incrementing
|
// Do a pointer compare rather than `QueryInterface` because it is a lot cheaper than incrementing
|
||||||
// the reference count
|
// the reference count
|
||||||
@@ -742,16 +761,14 @@ 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;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ graphics_orientation GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_NORMAL;
|
|||||||
bool GRAPHICS_WINDOWED = false;
|
bool GRAPHICS_WINDOWED = false;
|
||||||
std::vector<HWND> GRAPHICS_WINDOWS;
|
std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
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;
|
||||||
@@ -70,6 +71,7 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
|
|||||||
bool SUBSCREEN_FORCE_REDRAW = false;
|
bool SUBSCREEN_FORCE_REDRAW = false;
|
||||||
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
||||||
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||||
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
||||||
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
||||||
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
||||||
@@ -96,6 +98,7 @@ static decltype(SetCursor) *SetCursor_orig = nullptr;
|
|||||||
static decltype(SetWindowLongA) *SetWindowLongA_orig = nullptr;
|
static decltype(SetWindowLongA) *SetWindowLongA_orig = nullptr;
|
||||||
static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr;
|
static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr;
|
||||||
static decltype(SetWindowPos) *SetWindowPos_orig = nullptr;
|
static decltype(SetWindowPos) *SetWindowPos_orig = nullptr;
|
||||||
|
static decltype(ShowWindow) *ShowWindow_orig = nullptr;
|
||||||
|
|
||||||
static void reset_window_hook(HWND hWnd) {
|
static void reset_window_hook(HWND hWnd) {
|
||||||
overlay::destroy(hWnd);
|
overlay::destroy(hWnd);
|
||||||
@@ -298,6 +301,30 @@ static BOOL WINAPI ClipCursor_hook(const RECT *lpRect) {
|
|||||||
return ClipCursor_orig(lpRect);
|
return ClipCursor_orig(lpRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fix_up_rb_auto_rotate_dimensions(DWORD &w, DWORD &h) {
|
||||||
|
// reflec beat
|
||||||
|
// for whatever reason, rb creates the first window it weird dimensions (774x1389 for reflesia, for example)
|
||||||
|
// so doing ChangeDisplaySettings as-is ends up with DISP_CHANGE_BADMODE
|
||||||
|
// while a second window (with title D3DProxyWindow) does get created with correct dimensions, it's
|
||||||
|
// too late to rotate the window then; as a workaround force the display change now with patched up dimensions
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
if (w == 774) {
|
||||||
|
// LBR, MBR reflesia
|
||||||
|
w = 768;
|
||||||
|
h = 1360;
|
||||||
|
changed = true;
|
||||||
|
} else if (w == 1086) {
|
||||||
|
// MBR (before reflesia)
|
||||||
|
w = 1080;
|
||||||
|
h = 1920;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
log_misc("graphics", "fix auto-rotate dimensions for reflec beat: w={}, h={}", w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName,
|
static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName,
|
||||||
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
|
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
|
||||||
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
|
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
|
||||||
@@ -322,10 +349,19 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
// set display orientation and/or refresh rate
|
// set display orientation and/or refresh rate
|
||||||
// only set orientation when the target window is portrait
|
// only set orientation when the target window is portrait
|
||||||
// (avoid doing this for SDVX subscreen which is in landscape, for example)
|
// (avoid doing this for SDVX subscreen which is in landscape, for example)
|
||||||
const auto adjust_orientation =
|
auto adjust_orientation =
|
||||||
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
||||||
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW);
|
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW);
|
||||||
|
|
||||||
|
// reflec beat
|
||||||
|
if (avs::game::is_model("KBR")) {
|
||||||
|
// KBR (rb1) launches landscape and draws rotated by itself, don't do any rotation
|
||||||
|
adjust_orientation = false;
|
||||||
|
} else if (avs::game::is_model({"LBR", "MBR"}) && window_name == "D3DProxyWindow") {
|
||||||
|
// do not auto-rotate for second window (D3DProxyWindow)
|
||||||
|
adjust_orientation = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ((nHeight > nWidth && adjust_orientation) || GRAPHICS_FORCE_REFRESH > 0) {
|
if ((nHeight > nWidth && adjust_orientation) || GRAPHICS_FORCE_REFRESH > 0) {
|
||||||
DEVMODE mode {};
|
DEVMODE mode {};
|
||||||
|
|
||||||
@@ -335,11 +371,16 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
DWORD orientation = GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ? DMDO_90 : DMDO_270;
|
DWORD orientation = GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ? DMDO_90 : DMDO_270;
|
||||||
log_misc(
|
log_misc(
|
||||||
"graphics",
|
"graphics",
|
||||||
"auto-rotate: call ChangeDisplaySettings and rotate display to DMDO_xx mode {}",
|
"auto-rotate: call ChangeDisplaySettings and rotate display to DMDO_xx mode {}, w={}, h={}",
|
||||||
orientation);
|
orientation, nWidth, nHeight);
|
||||||
mode.dmPelsWidth = nWidth;
|
mode.dmPelsWidth = nWidth;
|
||||||
mode.dmPelsHeight = nHeight;
|
mode.dmPelsHeight = nHeight;
|
||||||
mode.dmDisplayOrientation = orientation;
|
mode.dmDisplayOrientation = orientation;
|
||||||
|
|
||||||
|
// reflec beat
|
||||||
|
if (avs::game::is_model({"LBR", "MBR"})) {
|
||||||
|
fix_up_rb_auto_rotate_dimensions(mode.dmPelsWidth, mode.dmPelsHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GRAPHICS_FORCE_REFRESH > 0) {
|
if (GRAPHICS_FORCE_REFRESH > 0) {
|
||||||
@@ -354,7 +395,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
|
|
||||||
const auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
const auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||||
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
||||||
log_warning("graphics", "failed to change display settings: {}", disp_res);
|
log_warning("graphics", "ChangeDisplaySettings failed: {}", disp_res);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics", "failed to get display settings");
|
log_warning("graphics", "failed to get display settings");
|
||||||
@@ -373,7 +414,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
||||||
bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL");
|
bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL");
|
||||||
|
|
||||||
// TDJ windowed mode with subscreen: hide maximize button
|
// hide maximize button (prevent misaligned touches)
|
||||||
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) {
|
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) {
|
||||||
dwStyle &= ~(WS_MAXIMIZEBOX);
|
dwStyle &= ~(WS_MAXIMIZEBOX);
|
||||||
}
|
}
|
||||||
@@ -381,7 +422,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
if (is_gfdm_sub_window) {
|
if (is_gfdm_sub_window) {
|
||||||
dwStyle &= ~(WS_SIZEBOX);
|
dwStyle &= ~(WS_SIZEBOX);
|
||||||
}
|
}
|
||||||
if ((is_tdj_sub_window || is_sdvx_sub_window || is_gfdm_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
||||||
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,12 +465,19 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW);
|
graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_gfdm_sub_window) {
|
// only hook touch window if multiple windows are allowed
|
||||||
|
if (is_gfdm_sub_window && !games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||||
GFDM_SUBSCREEN_WINDOW = result;
|
GFDM_SUBSCREEN_WINDOW = result;
|
||||||
graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW);
|
graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_touch_indicators(result);
|
disable_touch_indicators(result);
|
||||||
|
log_misc(
|
||||||
|
"graphics",
|
||||||
|
"CreateWindowExA returned {}, {}",
|
||||||
|
fmt::ptr(result),
|
||||||
|
lpWindowName ? lpWindowName : "(null)");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +509,8 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// windowed mode adjustments
|
// windowed mode adjustments
|
||||||
if (GRAPHICS_WINDOWED) {
|
// check the width to filter out invisible system-created windows
|
||||||
|
if (GRAPHICS_WINDOWED && nWidth > 0) {
|
||||||
|
|
||||||
// change window style
|
// change window style
|
||||||
dwExStyle = 0;
|
dwExStyle = 0;
|
||||||
@@ -496,6 +545,12 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
|
|||||||
hWndParent, hMenu, hInstance, lpParam);
|
hWndParent, hMenu, hInstance, lpParam);
|
||||||
GRAPHICS_WINDOWS.push_back(result);
|
GRAPHICS_WINDOWS.push_back(result);
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"graphics",
|
||||||
|
"CreateWindowExW returned {}, {}",
|
||||||
|
fmt::ptr(result),
|
||||||
|
lpWindowName ? ws2s(lpWindowName) : "(null)");
|
||||||
|
|
||||||
disable_touch_indicators(result);
|
disable_touch_indicators(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -660,10 +715,29 @@ static BOOL WINAPI SetWindowPos_hook(HWND hWnd, HWND hWndInsertAfter,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prevent gitadora arena model from shifting windows around if the user has preferences
|
||||||
|
if (GRAPHICS_WINDOWED && games::gitadora::is_arena_model() &&
|
||||||
|
GRAPHICS_HOOKED_WINDOW.has_value() && hWnd == GRAPHICS_HOOKED_WINDOW.value() &&
|
||||||
|
cfg::SCREENRESIZE->enable_window_resize) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
// call original
|
// call original
|
||||||
return SetWindowPos_orig(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
|
return SetWindowPos_orig(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) {
|
||||||
|
if (games::gitadora::is_arena_model() &&
|
||||||
|
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||||
|
hWnd != GRAPHICS_HOOKED_WINDOW) {
|
||||||
|
log_info("graphics", "ShowWindow_hook - hiding sub window {}", fmt::ptr(hWnd));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call original
|
||||||
|
return ShowWindow_orig(hWnd, nCmdShow);
|
||||||
|
}
|
||||||
|
|
||||||
static ATOM WINAPI RegisterClassA_hook(const WNDCLASSA *lpWndClass) {
|
static ATOM WINAPI RegisterClassA_hook(const WNDCLASSA *lpWndClass) {
|
||||||
|
|
||||||
// check for null
|
// check for null
|
||||||
@@ -806,6 +880,7 @@ void graphics_init() {
|
|||||||
SetWindowLongA_orig = detour::iat_try("SetWindowLongA", SetWindowLongA_hook);
|
SetWindowLongA_orig = detour::iat_try("SetWindowLongA", SetWindowLongA_hook);
|
||||||
SetWindowLongW_orig = detour::iat_try("SetWindowLongW", SetWindowLongW_hook);
|
SetWindowLongW_orig = detour::iat_try("SetWindowLongW", SetWindowLongW_hook);
|
||||||
SetWindowPos_orig = detour::iat_try("SetWindowPos", SetWindowPos_hook);
|
SetWindowPos_orig = detour::iat_try("SetWindowPos", SetWindowPos_hook);
|
||||||
|
ShowWindow_orig = detour::iat_try("ShowWindow", ShowWindow_hook);
|
||||||
|
|
||||||
detour::iat_try("MessageBoxA", MessageBoxA_hook);
|
detour::iat_try("MessageBoxA", MessageBoxA_hook);
|
||||||
detour::iat_try("MessageBoxExA", MessageBoxExA_hook);
|
detour::iat_try("MessageBoxExA", MessageBoxExA_hook);
|
||||||
@@ -977,6 +1052,7 @@ bool graphics_capture_receive_jpeg(int screen, TooJpeg::WRITE_ONE_BYTE receiver,
|
|||||||
auto capture_width = capture.width;
|
auto capture_width = capture.width;
|
||||||
auto capture_height = capture.height;
|
auto capture_height = capture.height;
|
||||||
auto capture_timestamp = capture.timestamp;
|
auto capture_timestamp = capture.timestamp;
|
||||||
|
capture.data = nullptr;
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// validate data
|
// validate data
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ extern bool GRAPHICS_WINDOWED;
|
|||||||
extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
||||||
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
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;
|
||||||
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||||
|
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||||
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
||||||
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
||||||
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||||
@@ -48,6 +50,7 @@ extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
|||||||
extern std::optional<std::string> GRAPHICS_WINDOW_POS;
|
extern std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||||
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
||||||
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
|
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
|
||||||
|
extern std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||||
|
|
||||||
extern bool GRAPHICS_IIDX_WSUB;
|
extern bool GRAPHICS_IIDX_WSUB;
|
||||||
extern std::optional<std::string> GRAPHICS_WSUB_SIZE;
|
extern std::optional<std::string> GRAPHICS_WSUB_SIZE;
|
||||||
@@ -99,5 +102,8 @@ void graphics_capture_initial_window(HWND hWnd);
|
|||||||
void graphics_update_window_style(HWND hWnd);
|
void graphics_update_window_style(HWND hWnd);
|
||||||
void graphics_update_z_order(HWND hWnd, bool always_on_top);
|
void graphics_update_z_order(HWND hWnd, bool always_on_top);
|
||||||
void graphics_move_resize_window(HWND hWnd);
|
void graphics_move_resize_window(HWND hWnd);
|
||||||
bool graphics_window_change_crashes_game();
|
bool graphics_window_options_breaks_game();
|
||||||
|
bool graphics_window_decoration_change_crashes_game();
|
||||||
|
bool graphics_window_resize_breaks_game();
|
||||||
|
bool graphics_window_move_and_resize_breaks_game();
|
||||||
void graphics_load_windowed_subscreen_parameters();
|
void graphics_load_windowed_subscreen_parameters();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -23,6 +24,7 @@ std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
|||||||
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||||
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
||||||
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
|
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
|
||||||
|
std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||||
|
|
||||||
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
|
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
|
||||||
bool GRAPHICS_IIDX_WSUB = false;
|
bool GRAPHICS_IIDX_WSUB = false;
|
||||||
@@ -55,15 +57,22 @@ void graphics_capture_initial_window(HWND hWnd) {
|
|||||||
|
|
||||||
graphics_load_windowed_parameters();
|
graphics_load_windowed_parameters();
|
||||||
|
|
||||||
cfg::SCREENRESIZE->init_window_style = GetWindowLong(hWnd, GWL_STYLE);
|
GRAPHICS_HOOKED_WINDOW = hWnd;
|
||||||
|
|
||||||
log_debug("graphics-windowed", "graphics_capture_initial_window called");
|
cfg::SCREENRESIZE->init_window_style = GetWindowLong(hWnd, GWL_STYLE);
|
||||||
|
cfg::SCREENRESIZE->init_window_style_ex = GetWindowLong(hWnd, GWL_EXSTYLE);
|
||||||
|
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_capture_initial_window called, hWnd={}",
|
||||||
|
fmt::ptr(hWnd));
|
||||||
|
|
||||||
RECT rect;
|
RECT rect;
|
||||||
if (!GetClientRect(hWnd, &rect)) {
|
if (!GetClientRect(hWnd, &rect)) {
|
||||||
log_warning(
|
log_warning(
|
||||||
"graphics",
|
"graphics-windowed",
|
||||||
"graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
"[{}] graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
||||||
|
fmt::ptr(hWnd),
|
||||||
GetLastError());
|
GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -75,12 +84,22 @@ void graphics_capture_initial_window(HWND hWnd) {
|
|||||||
cfg::SCREENRESIZE->init_client_aspect_ratio = (float)client_w / (float)client_h;
|
cfg::SCREENRESIZE->init_client_aspect_ratio = (float)client_w / (float)client_h;
|
||||||
log_debug(
|
log_debug(
|
||||||
"graphics-windowed",
|
"graphics-windowed",
|
||||||
"graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
"[{}] graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
||||||
|
fmt::ptr(hWnd),
|
||||||
client_w, client_h, cfg::SCREENRESIZE->init_client_aspect_ratio);
|
client_w, client_h, cfg::SCREENRESIZE->init_client_aspect_ratio);
|
||||||
|
|
||||||
// ensure frame size is captured
|
// ensure frame size is captured
|
||||||
graphics_wm_style_changed(hWnd, false);
|
graphics_wm_style_changed(hWnd, false);
|
||||||
|
|
||||||
|
// if the game is not compatible, ignore user-supplied values and keep the current size
|
||||||
|
if (graphics_window_resize_breaks_game()) {
|
||||||
|
cfg::SCREENRESIZE->client_width = client_w;
|
||||||
|
cfg::SCREENRESIZE->client_height = client_h;
|
||||||
|
}
|
||||||
|
if (graphics_window_move_and_resize_breaks_game()) {
|
||||||
|
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||||
|
}
|
||||||
|
|
||||||
// if there was no user-supplied dimension, seed it with the current size
|
// if there was no user-supplied dimension, seed it with the current size
|
||||||
// so that the next resize operation will work
|
// so that the next resize operation will work
|
||||||
if (cfg::SCREENRESIZE->client_width == 0) {
|
if (cfg::SCREENRESIZE->client_width == 0) {
|
||||||
@@ -94,7 +113,8 @@ void graphics_capture_initial_window(HWND hWnd) {
|
|||||||
// resize must be done before applying the border
|
// resize must be done before applying the border
|
||||||
if (cfg::SCREENRESIZE->enable_window_resize) {
|
if (cfg::SCREENRESIZE->enable_window_resize) {
|
||||||
log_info(
|
log_info(
|
||||||
"graphics-windowed", "change window rect - window offset: {}x{}, client size: {}x{}",
|
"graphics-windowed", "[{}] change window rect - window offset: {}x{}, client size: {}x{}",
|
||||||
|
fmt::ptr(hWnd),
|
||||||
cfg::SCREENRESIZE->window_offset_x,
|
cfg::SCREENRESIZE->window_offset_x,
|
||||||
cfg::SCREENRESIZE->window_offset_y,
|
cfg::SCREENRESIZE->window_offset_y,
|
||||||
cfg::SCREENRESIZE->client_width,
|
cfg::SCREENRESIZE->client_width,
|
||||||
@@ -104,12 +124,13 @@ void graphics_capture_initial_window(HWND hWnd) {
|
|||||||
// ddr hangs when window frame doesn't have overlapped
|
// ddr hangs when window frame doesn't have overlapped
|
||||||
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
||||||
log_info(
|
log_info(
|
||||||
"graphics-windowed", "change window style - decoration: {}",
|
"graphics-windowed", "[{}] change window style - decoration: {}",
|
||||||
|
fmt::ptr(hWnd),
|
||||||
cfg::SCREENRESIZE->window_decoration);
|
cfg::SCREENRESIZE->window_decoration);
|
||||||
graphics_update_window_style(hWnd);
|
graphics_update_window_style(hWnd);
|
||||||
}
|
}
|
||||||
if (cfg::SCREENRESIZE->window_always_on_top) {
|
if (cfg::SCREENRESIZE->window_always_on_top) {
|
||||||
log_info("graphics-windowed", "change window z-order - always on top");
|
log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd));
|
||||||
graphics_update_z_order(hWnd, true);
|
graphics_update_z_order(hWnd, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +173,7 @@ void graphics_load_windowed_parameters() {
|
|||||||
cfg::SCREENRESIZE->window_offset_x = result.first;
|
cfg::SCREENRESIZE->window_offset_x = result.first;
|
||||||
cfg::SCREENRESIZE->window_offset_y = result.second;
|
cfg::SCREENRESIZE->window_offset_y = result.second;
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -windowpos");
|
log_fatal("graphics-windowed", "failed to parse -windowpos");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +202,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
|||||||
GRAPHICS_WSUB_WIDTH = result.first;
|
GRAPHICS_WSUB_WIDTH = result.first;
|
||||||
GRAPHICS_WSUB_HEIGHT = result.second;
|
GRAPHICS_WSUB_HEIGHT = result.second;
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
log_fatal("graphics-windowed", "failed to parse -iidxwsubsize / -sdvxwsubsize");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +216,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
|||||||
GRAPHICS_WSUB_X = result.first;
|
GRAPHICS_WSUB_X = result.first;
|
||||||
GRAPHICS_WSUB_Y = result.second;
|
GRAPHICS_WSUB_Y = result.second;
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
log_fatal("graphics-windowed", "failed to parse -iidxwsubpos / -sdvxwsubpos");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,20 +389,29 @@ void graphics_update_window_style(HWND hWnd) {
|
|||||||
if (!GRAPHICS_WINDOWED) {
|
if (!GRAPHICS_WINDOWED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (graphics_window_change_crashes_game()) {
|
if (graphics_window_decoration_change_crashes_game()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("graphics-windowed", "graphics_update_window_style called");
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_update_window_style called for hWnd={}",
|
||||||
|
fmt::ptr(hWnd));
|
||||||
|
|
||||||
// update frame style
|
// update frame style
|
||||||
auto style = cfg::SCREENRESIZE->init_window_style;
|
auto style = cfg::SCREENRESIZE->init_window_style;
|
||||||
|
auto style_ex = cfg::SCREENRESIZE->init_window_style_ex;
|
||||||
switch (cfg::SCREENRESIZE->window_decoration) {
|
switch (cfg::SCREENRESIZE->window_decoration) {
|
||||||
case cfg::WindowDecorationMode::Borderless:
|
case cfg::WindowDecorationMode::Borderless:
|
||||||
style &= ~WS_OVERLAPPEDWINDOW;
|
style &= ~WS_OVERLAPPEDWINDOW;
|
||||||
|
style_ex &= ~WS_EX_CLIENTEDGE;
|
||||||
|
style_ex &= ~WS_EX_WINDOWEDGE;
|
||||||
break;
|
break;
|
||||||
case cfg::WindowDecorationMode::ResizableFrame:
|
case cfg::WindowDecorationMode::ResizableFrame:
|
||||||
style |= WS_OVERLAPPEDWINDOW;
|
style |= WS_OVERLAPPEDWINDOW;
|
||||||
|
if (graphics_window_resize_breaks_game()) {
|
||||||
|
style &= ~WS_SIZEBOX;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case cfg::WindowDecorationMode::Default:
|
case cfg::WindowDecorationMode::Default:
|
||||||
default:
|
default:
|
||||||
@@ -393,11 +423,26 @@ void graphics_update_window_style(HWND hWnd) {
|
|||||||
"graphics_update_window_style - calling SetWindowLong with Mode {}, style 0x{:x}",
|
"graphics_update_window_style - calling SetWindowLong with Mode {}, style 0x{:x}",
|
||||||
static_cast<int>(cfg::SCREENRESIZE->window_decoration),
|
static_cast<int>(cfg::SCREENRESIZE->window_decoration),
|
||||||
style);
|
style);
|
||||||
SetWindowLong(hWnd, GWL_STYLE, style);
|
|
||||||
|
const auto ret = SetWindowLong(hWnd, GWL_STYLE, style);
|
||||||
|
(void)ret;
|
||||||
|
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_update_window_style - SetWindowLong(GWL_STYLE) returned {:x}, GLE={}",
|
||||||
|
ret,
|
||||||
|
GetLastError());
|
||||||
|
|
||||||
|
const auto ret2 = SetWindowLong(hWnd, GWL_EXSTYLE, style_ex);
|
||||||
|
(void)ret2;
|
||||||
|
log_debug(
|
||||||
|
"graphics-windowed",
|
||||||
|
"graphics_update_window_style SetWindowLong(GWL_EXSTYLE) returned {:x}, GLE={}",
|
||||||
|
ret2,
|
||||||
|
GetLastError());
|
||||||
|
|
||||||
// SetWindowPos must be called after SetWindowLong if the frame style changed
|
// SetWindowPos must be called after SetWindowLong if the frame style changed
|
||||||
// this will be done in WM_STYLECHANGED handler
|
// this will be done in WM_STYLECHANGED handler
|
||||||
log_debug("graphics-windowed", "graphics_update_window_style returned");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void graphics_update_z_order(HWND hWnd, bool always_on_top) {
|
void graphics_update_z_order(HWND hWnd, bool always_on_top) {
|
||||||
@@ -454,12 +499,19 @@ void graphics_move_resize_window(HWND hWnd) {
|
|||||||
log_debug("graphics-windowed", "graphics_move_resize_window returned");
|
log_debug("graphics-windowed", "graphics_move_resize_window returned");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool graphics_window_change_crashes_game() {
|
bool graphics_window_decoration_change_crashes_game() {
|
||||||
static std::once_flag flag;
|
static std::once_flag flag;
|
||||||
static bool result = false;
|
static bool result = false;
|
||||||
std::call_once(flag, []() {
|
std::call_once(flag, []() {
|
||||||
// ddr crashes when frame style changes
|
// ddr crashes when frame style changes
|
||||||
result = avs::game::is_model("MDX");
|
result = avs::game::is_model("MDX");
|
||||||
|
|
||||||
|
// changing window decoration also changes dimensions and position
|
||||||
|
// so this must be prevented
|
||||||
|
if (graphics_window_move_and_resize_breaks_game()) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
log_warning(
|
log_warning(
|
||||||
"graphics-windowed",
|
"graphics-windowed",
|
||||||
@@ -468,3 +520,43 @@ bool graphics_window_change_crashes_game() {
|
|||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool graphics_window_move_and_resize_breaks_game() {
|
||||||
|
static std::once_flag flag;
|
||||||
|
static bool result = false;
|
||||||
|
|
||||||
|
std::call_once(flag, []() {
|
||||||
|
// games like reflec beat in use spice touch overlay for windowed mode
|
||||||
|
// and mouse input breaks if window is moved as the overlay does not
|
||||||
|
// currently support window moves/resizes
|
||||||
|
result = avs::game::is_model({"KBR", "LBR", "MBR"});
|
||||||
|
if (result) {
|
||||||
|
log_warning(
|
||||||
|
"graphics-windowed",
|
||||||
|
"ignoring changes to window properties due to incompatibility with this game");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool graphics_window_resize_breaks_game() {
|
||||||
|
// for whatever reason, gitadora games on dx/sd/sd2 cabs behave poorly when window is resized
|
||||||
|
// (eihter at launch or while it's running)
|
||||||
|
// usually results in: soft lock during scene transtions or really long transitions,
|
||||||
|
// backgrounds of menus not rendering, etc.
|
||||||
|
bool is_gitadora = avs::game::is_model({ "J32", "J33", "K32", "K33", "L32", "L33", "M32" });
|
||||||
|
bool is_arena = games::gitadora::is_arena_model();
|
||||||
|
|
||||||
|
bool result = graphics_window_move_and_resize_breaks_game() || (is_gitadora && !is_arena);
|
||||||
|
|
||||||
|
static std::once_flag flag;
|
||||||
|
std::call_once(flag, [&result]() {
|
||||||
|
if (result) {
|
||||||
|
log_warning(
|
||||||
|
"graphics-windowed",
|
||||||
|
"ignoring changes to window size due to incompatibility with this game");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// GetLocaleInfoEx
|
||||||
|
#define _WIN32_WINNT 0x0600
|
||||||
|
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
|
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
@@ -10,6 +13,7 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
#include "games/gitadora/gitadora.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -220,8 +224,7 @@ void hooks::lang::early_init() {
|
|||||||
detour::trampoline_try("kernel32.dll", "GetOEMCP", GetOEMCP_hook, &GetOEMCP_orig);
|
detour::trampoline_try("kernel32.dll", "GetOEMCP", GetOEMCP_hook, &GetOEMCP_orig);
|
||||||
|
|
||||||
#ifdef SPICE64 // SDVX5+ specific code
|
#ifdef SPICE64 // SDVX5+ specific code
|
||||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
if (games::sdvx::is_valkyrie_model()) {
|
||||||
if (avs::game::is_model("KFC") && isValkyrieCabinetMode) {
|
|
||||||
log_info("hooks::lang", "hooking GetSystemDefaultLCID");
|
log_info("hooks::lang", "hooking GetSystemDefaultLCID");
|
||||||
detour::trampoline_try(
|
detour::trampoline_try(
|
||||||
"kernel32.dll",
|
"kernel32.dll",
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// DisplayConfigSetDeviceInfo
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
#include "winuser.h"
|
#include "winuser.h"
|
||||||
|
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -554,6 +559,10 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
nvenc_hook::FORCE_DISABLE = true;
|
nvenc_hook::FORCE_DISABLE = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (options[launcher::Options::OtocaCamHook].value_bool()) {
|
||||||
|
games::otoca::BYPASS_CAMERA = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
|
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
|
||||||
attach_jb = true;
|
attach_jb = true;
|
||||||
}
|
}
|
||||||
@@ -584,6 +593,25 @@ 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::GitaDoraArenaSingleWindow].value_bool() && GRAPHICS_WINDOWED) {
|
||||||
|
games::gitadora::ARENA_SINGLE_WINDOW = true;
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::GitaDoraWailHold].is_active()) {
|
||||||
|
socd::TILT_HOLD_MS = options[launcher::Options::GitaDoraWailHold].value_uint32();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::GitaDoraPickAlgo].is_active()) {
|
||||||
|
const auto text = options[launcher::Options::GitaDoraPickAlgo].value_text();
|
||||||
|
if (text == "legacy") {
|
||||||
|
games::gitadora::PICK_ALGO.reset();
|
||||||
|
} else if (text == "neutral") {
|
||||||
|
games::gitadora::PICK_ALGO = socd::SocdAlgorithm::Neutral;
|
||||||
|
} else if (text == "raw") {
|
||||||
|
games::gitadora::PICK_ALGO = socd::SocdAlgorithm::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::GitaDoraSubOverlaySize].is_active()) {
|
||||||
|
games::gitadora::SUBSCREEN_OVERLAY_SIZE = options[launcher::Options::GitaDoraSubOverlaySize].value_text();
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
|
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
|
||||||
attach_nostalgia = true;
|
attach_nostalgia = true;
|
||||||
}
|
}
|
||||||
@@ -605,6 +633,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;
|
||||||
}
|
}
|
||||||
@@ -959,6 +998,12 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::DisableOverlay].value_bool()) {
|
if (options[launcher::Options::DisableOverlay].value_bool()) {
|
||||||
overlay::ENABLED = false;
|
overlay::ENABLED = false;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::OverlayScaling].is_active() && !cfg::CONFIGURATOR_STANDALONE && !cfg_run) {
|
||||||
|
const auto val = options[launcher::Options::OverlayScaling].value_uint32();
|
||||||
|
if (10 <= val && val <= 400 && val != 100) {
|
||||||
|
overlay::UI_SCALE_PERCENT = options[launcher::Options::OverlayScaling].value_uint32();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (options[launcher::Options::DisableAudioHooks].value_bool()) {
|
if (options[launcher::Options::DisableAudioHooks].value_bool()) {
|
||||||
hooks::audio::ENABLED = false;
|
hooks::audio::ENABLED = false;
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
@@ -970,18 +1015,26 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
|
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
|
||||||
hooks::audio::VOLUME_HOOK_ENABLED = false;
|
hooks::audio::VOLUME_HOOK_ENABLED = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::AudioBackend].is_active()) {
|
if (options[launcher::Options::AudioBackend].is_active()) {
|
||||||
auto &name = options[launcher::Options::AudioBackend].value_text();
|
auto &name = options[launcher::Options::AudioBackend].value_text();
|
||||||
auto backend = hooks::audio::name_to_backend(name.c_str());
|
auto backend = hooks::audio::name_to_backend(name.c_str());
|
||||||
if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) {
|
if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_fatal("launcher", "invalid audio backend: {}", name);
|
log_fatal("launcher", "invalid audio backend: {}", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks::audio::BACKEND = backend;
|
hooks::audio::BACKEND = backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::AsioDriverId].is_active()) {
|
if (options[launcher::Options::AsioDriverId].is_active()) {
|
||||||
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32();
|
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::AsioDriverName].is_active()) {
|
||||||
|
hooks::audio::BACKEND = hooks::audio::name_to_backend("asio");
|
||||||
|
hooks::audio::ASIO_DRIVER_NAME = options[launcher::Options::AsioDriverName].value_text();
|
||||||
|
// this new option overrides the old one
|
||||||
|
hooks::audio::ASIO_DRIVER_ID.reset();
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::AudioDummy].value_bool()) {
|
if (options[launcher::Options::AudioDummy].value_bool()) {
|
||||||
hooks::audio::USE_DUMMY = true;
|
hooks::audio::USE_DUMMY = true;
|
||||||
}
|
}
|
||||||
@@ -1042,8 +1095,8 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
std::pair<uint32_t, uint32_t> result;
|
std::pair<uint32_t, uint32_t> result;
|
||||||
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
||||||
GRAPHICS_WINDOW_SIZE = result;
|
GRAPHICS_WINDOW_SIZE = result;
|
||||||
} else {
|
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_warning("launcher", "failed to parse -windowsize");
|
log_fatal("launcher", "failed to parse -windowsize");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
|
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
|
||||||
@@ -1087,6 +1140,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::spice2x_DRSTransposeTouch].value_bool()) {
|
if (options[launcher::Options::spice2x_DRSTransposeTouch].value_bool()) {
|
||||||
games::drs::TRANSPOSE_TOUCH = true;
|
games::drs::TRANSPOSE_TOUCH = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::DRSRGBCameraHook].value_bool()) {
|
||||||
|
games::drs::RGB_CAMERA_HOOK = true;
|
||||||
|
}
|
||||||
if (options[launcher::Options::spice2x_AutoCard].is_active()) {
|
if (options[launcher::Options::spice2x_AutoCard].is_active()) {
|
||||||
const auto text = options[launcher::Options::spice2x_AutoCard].value_text();
|
const auto text = options[launcher::Options::spice2x_AutoCard].value_text();
|
||||||
if (text == "p1") {
|
if (text == "p1") {
|
||||||
@@ -1341,12 +1397,33 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (launcher::signal::DISABLE && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
log_warning(
|
||||||
|
"launcher",
|
||||||
|
"WARNING - user specified -signaldisable option\n\n\n"
|
||||||
|
"!!! !!!\n"
|
||||||
|
"!!! Using -signaldisable option disables all exception handling, !!!\n"
|
||||||
|
"!!! which means when the game crashes it will likely just vanish !!!\n"
|
||||||
|
"!!! without troubleshooting information in the logs. !!!\n"
|
||||||
|
"!!! !!!\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::FullscreenResolution].is_active()) {
|
if (options[launcher::Options::FullscreenResolution].is_active()) {
|
||||||
std::pair<uint32_t, uint32_t> result;
|
std::pair<uint32_t, uint32_t> result;
|
||||||
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
||||||
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
||||||
} else {
|
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_warning("launcher", "failed to parse -forceres");
|
log_fatal("launcher", "failed to parse -forceres");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options[launcher::Options::FullscreenSubResolution].is_active()) {
|
||||||
|
std::pair<uint32_t, uint32_t> result;
|
||||||
|
if (parse_width_height(options[launcher::Options::FullscreenSubResolution].value_text(), result)) {
|
||||||
|
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB = result;
|
||||||
|
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
log_fatal("launcher", "failed to parse -forceressub");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1823,7 +1900,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Polaris Chord
|
// Polaris Chord
|
||||||
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/svm_Data")) {
|
if (check_dll("kamunity.dll") && fileutils::file_exists("game/svm.exe")) {
|
||||||
avs::game::DLL_NAME = "kamunity.dll";
|
avs::game::DLL_NAME = "kamunity.dll";
|
||||||
attach_io = true;
|
attach_io = true;
|
||||||
attach_pc = true;
|
attach_pc = true;
|
||||||
@@ -1862,7 +1939,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
"This usually means one of the following:\n\n"
|
"This usually means one of the following:\n\n"
|
||||||
" 1. You put spice executables in the wrong place, or\n"
|
" 1. You put spice executables in the wrong place, or\n"
|
||||||
" 2. XML files in prop directory are malformed or missing, or\n"
|
" 2. XML files in prop directory are malformed or missing, or\n"
|
||||||
" 3. You passed in invalid options (usually, path overrides like -exec)\n\n"
|
" 3. You passed in invalid options (do not specify -exec or -modules if you don't know what you're doing)\n\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
log_fatal("launcher", "module auto detection failed, see log.txt for troubleshooting");
|
log_fatal("launcher", "module auto detection failed, see log.txt for troubleshooting");
|
||||||
|
|||||||
@@ -12,9 +12,11 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#define FOREGROUND_GREY (8)
|
#define FOREGROUND_GREY (8)
|
||||||
#define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN)
|
#define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN)
|
||||||
#define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN)
|
#define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN)
|
||||||
|
#define FOREGROUND_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE)
|
||||||
|
#define FOREGROUND_MAGENTA (FOREGROUND_RED | FOREGROUND_BLUE)
|
||||||
|
|
||||||
namespace logger {
|
namespace logger {
|
||||||
|
|
||||||
@@ -103,9 +105,6 @@ namespace logger {
|
|||||||
last_style = content.second;
|
last_style = content.second;
|
||||||
|
|
||||||
switch (content.second) {
|
switch (content.second) {
|
||||||
case Style::DEFAULT:
|
|
||||||
set_console_color(hTerminal, FOREGROUND_WHITE);
|
|
||||||
break;
|
|
||||||
case Style::GREY:
|
case Style::GREY:
|
||||||
set_console_color(hTerminal, FOREGROUND_GREY);
|
set_console_color(hTerminal, FOREGROUND_GREY);
|
||||||
break;
|
break;
|
||||||
@@ -115,6 +114,13 @@ namespace logger {
|
|||||||
case Style::RED:
|
case Style::RED:
|
||||||
set_console_color(hTerminal, FOREGROUND_RED);
|
set_console_color(hTerminal, FOREGROUND_RED);
|
||||||
break;
|
break;
|
||||||
|
case Style::SPECIAL:
|
||||||
|
set_console_color(hTerminal, FOREGROUND_CYAN);
|
||||||
|
break;
|
||||||
|
case Style::DEFAULT:
|
||||||
|
default:
|
||||||
|
set_console_color(hTerminal, FOREGROUND_WHITE);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ namespace logger {
|
|||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
GREY = 1,
|
GREY = 1,
|
||||||
YELLOW = 2,
|
YELLOW = 2,
|
||||||
RED = 3
|
RED = 3,
|
||||||
|
SPECIAL = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
|||||||
+452
-241
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,8 @@ namespace launcher {
|
|||||||
GraphicsForceRefresh,
|
GraphicsForceRefresh,
|
||||||
FullscreenResolution,
|
FullscreenResolution,
|
||||||
FullscreenOrientationFlip,
|
FullscreenOrientationFlip,
|
||||||
|
FullscreenSubResolution,
|
||||||
|
FullscreenSubRefreshRate,
|
||||||
Graphics9On12,
|
Graphics9On12,
|
||||||
spice2x_Dx9On12,
|
spice2x_Dx9On12,
|
||||||
NoLegacy,
|
NoLegacy,
|
||||||
@@ -47,6 +49,7 @@ namespace launcher {
|
|||||||
SOFTID,
|
SOFTID,
|
||||||
VREnable,
|
VREnable,
|
||||||
DisableOverlay,
|
DisableOverlay,
|
||||||
|
OverlayScaling,
|
||||||
spice2x_FpsAutoShow,
|
spice2x_FpsAutoShow,
|
||||||
spice2x_FpsOpposite,
|
spice2x_FpsOpposite,
|
||||||
spice2x_SubScreenAutoShow,
|
spice2x_SubScreenAutoShow,
|
||||||
@@ -66,6 +69,7 @@ namespace launcher {
|
|||||||
IIDXBIO2FW,
|
IIDXBIO2FW,
|
||||||
IIDXTDJMode,
|
IIDXTDJMode,
|
||||||
spice2x_IIDXDigitalTTSensitivity,
|
spice2x_IIDXDigitalTTSensitivity,
|
||||||
|
IIDXDigitalTTSocd,
|
||||||
spice2x_IIDXLDJForce720p,
|
spice2x_IIDXLDJForce720p,
|
||||||
spice2x_IIDXTDJSubSize,
|
spice2x_IIDXTDJSubSize,
|
||||||
spice2x_IIDXLEDFontSize,
|
spice2x_IIDXLEDFontSize,
|
||||||
@@ -82,10 +86,11 @@ namespace launcher {
|
|||||||
SDVXPrinterJPGQuality,
|
SDVXPrinterJPGQuality,
|
||||||
SDVXDisableCameras,
|
SDVXDisableCameras,
|
||||||
SDVXNativeTouch,
|
SDVXNativeTouch,
|
||||||
|
spice2x_SDVXSubRedraw,
|
||||||
spice2x_SDVXDigitalKnobSensitivity,
|
spice2x_SDVXDigitalKnobSensitivity,
|
||||||
|
SDVXDigitalKnobSocd,
|
||||||
spice2x_SDVXAsioDriver,
|
spice2x_SDVXAsioDriver,
|
||||||
spice2x_SDVXSubPos,
|
spice2x_SDVXSubPos,
|
||||||
spice2x_SDVXSubRedraw,
|
|
||||||
LoadDDRModule,
|
LoadDDRModule,
|
||||||
DDR43Mode,
|
DDR43Mode,
|
||||||
DDRSkipCodecRegisteration,
|
DDRSkipCodecRegisteration,
|
||||||
@@ -96,6 +101,11 @@ namespace launcher {
|
|||||||
LoadGitaDoraModule,
|
LoadGitaDoraModule,
|
||||||
GitaDoraTwoChannelAudio,
|
GitaDoraTwoChannelAudio,
|
||||||
GitaDoraCabinetType,
|
GitaDoraCabinetType,
|
||||||
|
GitaDoraArenaSingleWindow,
|
||||||
|
GitaDoraLefty,
|
||||||
|
GitaDoraWailHold,
|
||||||
|
GitaDoraPickAlgo,
|
||||||
|
GitaDoraSubOverlaySize,
|
||||||
LoadJubeatModule,
|
LoadJubeatModule,
|
||||||
LoadReflecBeatModule,
|
LoadReflecBeatModule,
|
||||||
LoadShogikaiModule,
|
LoadShogikaiModule,
|
||||||
@@ -177,6 +187,7 @@ namespace launcher {
|
|||||||
spice2x_DisableVolumeHook,
|
spice2x_DisableVolumeHook,
|
||||||
AudioBackend,
|
AudioBackend,
|
||||||
AsioDriverId,
|
AsioDriverId,
|
||||||
|
AsioDriverName,
|
||||||
AudioDummy,
|
AudioDummy,
|
||||||
DelayBy5Seconds,
|
DelayBy5Seconds,
|
||||||
spice2x_DelayByNSeconds,
|
spice2x_DelayByNSeconds,
|
||||||
@@ -228,6 +239,7 @@ namespace launcher {
|
|||||||
spice2x_IIDXWindowedTDJ,
|
spice2x_IIDXWindowedTDJ,
|
||||||
spice2x_DRSDisableTouch,
|
spice2x_DRSDisableTouch,
|
||||||
spice2x_DRSTransposeTouch,
|
spice2x_DRSTransposeTouch,
|
||||||
|
DRSRGBCameraHook,
|
||||||
spice2x_IIDXNativeTouch,
|
spice2x_IIDXNativeTouch,
|
||||||
spice2x_IIDXNoSub,
|
spice2x_IIDXNoSub,
|
||||||
spice2x_IIDXEmulateSubscreenKeypadTouch,
|
spice2x_IIDXEmulateSubscreenKeypadTouch,
|
||||||
@@ -259,6 +271,7 @@ namespace launcher {
|
|||||||
LovePlusPrinterOutputFormat,
|
LovePlusPrinterOutputFormat,
|
||||||
LovePlusPrinterJPGQuality,
|
LovePlusPrinterJPGQuality,
|
||||||
OptionConflictResolution,
|
OptionConflictResolution,
|
||||||
|
OtocaCamHook,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class OptionsCategory {
|
enum class OptionsCategory {
|
||||||
|
|||||||
@@ -185,21 +185,30 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
|
|||||||
}
|
}
|
||||||
|
|
||||||
static BOOL WINAPI SetConsoleCtrlHandler_hook(PHANDLER_ROUTINE pHandlerRoutine, BOOL Add) {
|
static BOOL WINAPI SetConsoleCtrlHandler_hook(PHANDLER_ROUTINE pHandlerRoutine, BOOL Add) {
|
||||||
log_misc("signal", "SetConsoleCtrlHandler hook hit");
|
static std::once_flag printed;
|
||||||
|
std::call_once(printed, []() {
|
||||||
|
log_misc("signal", "SetConsoleCtrlHandler hook hit (printing once)");
|
||||||
|
});
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
|
||||||
{
|
|
||||||
log_info("signal", "SetUnhandledExceptionFilter hook hit");
|
static std::once_flag printed;
|
||||||
|
std::call_once(printed, []() {
|
||||||
|
log_info("signal", "SetUnhandledExceptionFilter hook hit (printing once)");
|
||||||
|
});
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||||||
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
static std::once_flag printed;
|
||||||
|
std::call_once(printed, []() {
|
||||||
|
log_info("signal", "AddVectoredExceptionHandler hook hit (printing once)");
|
||||||
|
});
|
||||||
|
|
||||||
return launcher::signal::USE_VEH_WORKAROUND ? INVALID_HANDLE_VALUE : nullptr;
|
return launcher::signal::USE_VEH_WORKAROUND ? INVALID_HANDLE_VALUE : nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-20
@@ -119,6 +119,7 @@ static std::string EAMIO_DLL_NAME = "eamio.dll";
|
|||||||
static robin_hood::unordered_map<int, std::thread *> BT5API_THREADS;
|
static robin_hood::unordered_map<int, std::thread *> BT5API_THREADS;
|
||||||
static robin_hood::unordered_map<int, int> BT5API_THREAD_RESULTS;
|
static robin_hood::unordered_map<int, int> BT5API_THREAD_RESULTS;
|
||||||
static uint8_t BT5API_CARD_STATES[] = {0, 0};
|
static uint8_t BT5API_CARD_STATES[] = {0, 0};
|
||||||
|
static uint16_t BT5API_KEYPAD_STATES[] = {0, 0};
|
||||||
|
|
||||||
static void bt5api_log(const char *bt5_module, const char *fmt, ...) {
|
static void bt5api_log(const char *bt5_module, const char *fmt, ...) {
|
||||||
|
|
||||||
@@ -240,46 +241,77 @@ void bt5api_hook(HINSTANCE module) {
|
|||||||
|
|
||||||
void bt5api_poll_reader_card(uint8_t unit_no) {
|
void bt5api_poll_reader_card(uint8_t unit_no) {
|
||||||
|
|
||||||
// check if initialized
|
// check if initialized or out of bounds
|
||||||
if (!EAMIO_DLL) {
|
if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// poll
|
// poll
|
||||||
if (!eam_io_poll(unit_no)) {
|
if (!eam_io_poll(unit_no)) {
|
||||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
log_warning("bt5api", "polling bt5api reader card {} returned failure",
|
||||||
static_cast<int>(unit_no));
|
static_cast<int>(unit_no));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get sensor state
|
// get sensor state
|
||||||
uint8_t sensor_state = eam_io_get_sensor_state(unit_no);
|
uint8_t sensor_state = eam_io_get_sensor_state(unit_no);
|
||||||
|
|
||||||
|
// if we have a change in state.
|
||||||
|
if(sensor_state != BT5API_CARD_STATES[unit_no]) {
|
||||||
|
|
||||||
// check for card in
|
// card inserted into the back.
|
||||||
if (sensor_state > BT5API_CARD_STATES[unit_no]) {
|
if(sensor_state & (1 << EAM_IO_SENSOR_BACK)) {
|
||||||
uint8_t card_id[8];
|
uint8_t card_id[8];
|
||||||
eam_io_read_card(unit_no, card_id, 8);
|
|
||||||
eamuse_card_insert(unit_no, card_id);
|
log_info("bt5api", "card unit {} inserted", static_cast<int>(unit_no));
|
||||||
|
|
||||||
|
// do the bt5 dance
|
||||||
|
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_CLOSE);
|
||||||
|
eam_io_poll(unit_no);
|
||||||
|
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_READ);
|
||||||
|
eam_io_poll(unit_no);
|
||||||
|
|
||||||
|
// ask the api for the card, then insert it into our engine.
|
||||||
|
eam_io_read_card(unit_no, card_id, 8);
|
||||||
|
eamuse_card_insert(unit_no, card_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if card removed entirely, do another bt5 dance!
|
||||||
|
if(sensor_state == 0) {
|
||||||
|
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_CLOSE);
|
||||||
|
eam_io_poll(unit_no);
|
||||||
|
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_OPEN);
|
||||||
|
|
||||||
|
log_info("bt5api", "card unit {} removed", static_cast<int>(unit_no));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// save state
|
// save prev state
|
||||||
BT5API_CARD_STATES[unit_no] = sensor_state;
|
BT5API_CARD_STATES[unit_no] = sensor_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt5api_poll_reader_keypad(uint8_t unit_no) {
|
void bt5api_poll_reader_keypad(uint8_t unit_no) {
|
||||||
|
|
||||||
// check if initialized
|
// check if initialized or out of bounds
|
||||||
if (!EAMIO_DLL) {
|
if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// poll
|
uint16_t keypad_current = eam_io_get_keypad_state(unit_no);
|
||||||
if (!eam_io_poll(unit_no)) {
|
uint16_t keypad_changes = ~BT5API_KEYPAD_STATES[unit_no] & keypad_current;
|
||||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
|
||||||
static_cast<int>(unit_no));
|
// if user has pressed decimal for the first time, eject the slotted reader.
|
||||||
|
if(keypad_changes & (1 << EAM_IO_KEYPAD_DECIMAL)) {
|
||||||
|
log_info("bt5api", "card unit {} ejecting", static_cast<int>(unit_no));
|
||||||
|
|
||||||
|
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_EJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save the last pressed.
|
||||||
|
BT5API_KEYPAD_STATES[unit_no] = keypad_current;
|
||||||
|
|
||||||
// get keypad
|
// send off the state to the main engine.
|
||||||
eamuse_set_keypad_overrides_bt5(unit_no, eam_io_get_keypad_state(unit_no));
|
eamuse_set_keypad_overrides_bt5(unit_no, keypad_current);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt5api_dispose() {
|
void bt5api_dispose() {
|
||||||
|
|||||||
@@ -202,7 +202,14 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) {
|
|||||||
|
|
||||||
// bt5api
|
// bt5api
|
||||||
if (BT5API_ENABLED) {
|
if (BT5API_ENABLED) {
|
||||||
bt5api_poll_reader_card((uint8_t) index);
|
// some bt5api want to be polled in order.
|
||||||
|
if (eamuse_get_game_keypads() > 1) {
|
||||||
|
bt5api_poll_reader_card(1);
|
||||||
|
bt5api_poll_reader_card(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bt5api_poll_reader_card((uint8_t) index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// auto insert card
|
// auto insert card
|
||||||
|
|||||||
+120
-56
@@ -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;
|
||||||
@@ -487,24 +522,42 @@ static long __cdecl gfdm_unit_get_input_p(int device, size_t player) {
|
|||||||
if (games::gitadora::is_guitar()) {
|
if (games::gitadora::is_guitar()) {
|
||||||
auto offset = player * 11;
|
auto offset = player * 11;
|
||||||
|
|
||||||
// pick up
|
const auto pick_up = Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[3 + offset]));
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[3 + offset]))) {
|
const auto pick_down = Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[4 + offset]));
|
||||||
if (!GFDM_GF_PICK_STATE_UP[player]) {
|
if (!games::gitadora::PICK_ALGO.has_value()) {
|
||||||
GFDM_GF_PICK_STATE_UP[player] = true;
|
// legacy behavior (detect rising edges only, input for 1 frame)
|
||||||
ret |= 0x80 | 0x20;
|
if (pick_up) {
|
||||||
|
if (!GFDM_GF_PICK_STATE_UP[player]) {
|
||||||
|
GFDM_GF_PICK_STATE_UP[player] = true;
|
||||||
|
ret |= 0x80 | 0x20;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GFDM_GF_PICK_STATE_UP[player] = false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
GFDM_GF_PICK_STATE_UP[player] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pick down
|
if (pick_down) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[4 + offset]))) {
|
if (!GFDM_GF_PICK_STATE_DOWN[player]) {
|
||||||
if (!GFDM_GF_PICK_STATE_DOWN[player]) {
|
GFDM_GF_PICK_STATE_DOWN[player] = true;
|
||||||
GFDM_GF_PICK_STATE_DOWN[player] = true;
|
ret |= 0x100 | 0x20;
|
||||||
ret |= 0x100 | 0x20;
|
}
|
||||||
|
} else {
|
||||||
|
GFDM_GF_PICK_STATE_DOWN[player] = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GFDM_GF_PICK_STATE_DOWN[player] = false;
|
const auto socd = socd::socd_clean(
|
||||||
|
player,
|
||||||
|
pick_up,
|
||||||
|
pick_down,
|
||||||
|
get_performance_milliseconds(),
|
||||||
|
games::gitadora::PICK_ALGO.value()
|
||||||
|
);
|
||||||
|
if (socd == socd::SocdCCW) {
|
||||||
|
ret |= 0x80 | 0x20; // pick up
|
||||||
|
} else if (socd == socd::SocdCW) {
|
||||||
|
ret |= 0x100 | 0x20; // pick down
|
||||||
|
} else if (socd == socd::SocdBoth) {
|
||||||
|
ret |= 0x100 | 0x80 | 0x20; // up and down
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// button R
|
// button R
|
||||||
@@ -585,63 +638,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
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
// mingw otherwise doesn't load touch stuff
|
// mingw otherwise doesn't load touch stuff
|
||||||
#define _WIN32_WINNT 0x0601
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
#include "wintouchemu.h"
|
#include "wintouchemu.h"
|
||||||
|
#include "rawinput/touch.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
|
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -63,6 +66,11 @@ namespace nativetouchhook {
|
|||||||
point->cyContact = 0;
|
point->cyContact = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void flip_touch_points(PTOUCHINPUT point) {
|
||||||
|
point->x = rawinput::touch::DISPLAY_SIZE_X * 100 - point->x;
|
||||||
|
point->y = rawinput::touch::DISPLAY_SIZE_Y * 100 - point->y;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL WINAPI GetTouchInputInfoHook(
|
static BOOL WINAPI GetTouchInputInfoHook(
|
||||||
HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||||
|
|
||||||
@@ -72,9 +80,32 @@ namespace nativetouchhook {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool flip_values = false;
|
||||||
|
if (avs::game::is_model("KFC") && rawinput::touch::DISPLAY_INITIALIZED) {
|
||||||
|
log_debug(
|
||||||
|
"touch::native", "DISPLAY_ORIENTATION = {}, DISPLAY_SIZE_X = {}, DISPLAY_SIZE_Y = {}",
|
||||||
|
rawinput::touch::DISPLAY_ORIENTATION,
|
||||||
|
rawinput::touch::DISPLAY_SIZE_X,
|
||||||
|
rawinput::touch::DISPLAY_SIZE_Y);
|
||||||
|
if (rawinput::touch::DISPLAY_ORIENTATION == DMDO_270) {
|
||||||
|
flip_values = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawinput::touch::INVERTED) {
|
||||||
|
flip_values = !flip_values;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < cInputs; i++) {
|
for (size_t i = 0; i < cInputs; i++) {
|
||||||
PTOUCHINPUT point = &pInputs[i];
|
PTOUCHINPUT point = &pInputs[i];
|
||||||
strip_contact_size(point);
|
|
||||||
|
if (avs::game::is_model("LDJ")) {
|
||||||
|
strip_contact_size(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flip_values) {
|
||||||
|
flip_touch_points(point);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "rawinput/touch.h"
|
||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
|
||||||
@@ -125,6 +127,11 @@ namespace wintouchemu {
|
|||||||
WINDOW_TITLE_END = window_title_end;
|
WINDOW_TITLE_END = window_title_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void flip_touch_points(PTOUCHINPUT point) {
|
||||||
|
point->x = rawinput::touch::DISPLAY_SIZE_X * 100 - point->x;
|
||||||
|
point->y = rawinput::touch::DISPLAY_SIZE_Y * 100 - point->y;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL WINAPI GetTouchInputInfoHook(HANDLE hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
static BOOL WINAPI GetTouchInputInfoHook(HANDLE hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||||
|
|
||||||
// check if original should be called
|
// check if original should be called
|
||||||
@@ -219,6 +226,12 @@ namespace wintouchemu {
|
|||||||
touch_input->cxContact = 0;
|
touch_input->cxContact = 0;
|
||||||
touch_input->cyContact = 0;
|
touch_input->cyContact = 0;
|
||||||
|
|
||||||
|
if (avs::game::is_model("KFC") &&
|
||||||
|
rawinput::touch::DISPLAY_INITIALIZED &&
|
||||||
|
rawinput::touch::DISPLAY_ORIENTATION == DMDO_270) {
|
||||||
|
flip_touch_points(touch_input);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (USE_MOUSE && !mouse_used) {
|
} else if (USE_MOUSE && !mouse_used) {
|
||||||
|
|
||||||
// disable further mouse inputs this call
|
// disable further mouse inputs this call
|
||||||
@@ -390,7 +403,10 @@ namespace wintouchemu {
|
|||||||
} else if (avs::game::is_model("LDJ") && !GENERIC_SUB_WINDOW_FULLSIZE) {
|
} else if (avs::game::is_model("LDJ") && !GENERIC_SUB_WINDOW_FULLSIZE) {
|
||||||
// overlay subscreen in IIDX
|
// overlay subscreen in IIDX
|
||||||
// use mouse position as ImGui overlay will block the touch window
|
// use mouse position as ImGui overlay will block the touch window
|
||||||
log_info("wintouchemu", "use mouse cursor API for overlay subscreen");
|
log_info("wintouchemu", "use mouse cursor API for ldj overlay subscreen");
|
||||||
|
USE_MOUSE = true;
|
||||||
|
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||||
|
log_info("wintouchemu", "use mouse cursor API for gitadora overlay subscreen");
|
||||||
USE_MOUSE = true;
|
USE_MOUSE = true;
|
||||||
} else {
|
} else {
|
||||||
// create touch window - create overlay if not yet existing at this point
|
// create touch window - create overlay if not yet existing at this point
|
||||||
|
|||||||
@@ -236,6 +236,26 @@ namespace nvapi {
|
|||||||
return NvAPI_DRS_SetSetting(h_session, h_profile, &drs_setting);
|
return NvAPI_DRS_SetSetting(h_session, h_profile, &drs_setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NvAPI_Status set_refresh_rate(NvDRSSessionHandle h_session, NvDRSProfileHandle h_profile) {
|
||||||
|
auto drs_setting = NVDRS_SETTING {};
|
||||||
|
drs_setting.version = NVDRS_SETTING_VER;
|
||||||
|
drs_setting.settingId = REFRESH_RATE_OVERRIDE_ID;
|
||||||
|
drs_setting.settingType = NVDRS_DWORD_TYPE;
|
||||||
|
drs_setting.u32PredefinedValue = REFRESH_RATE_OVERRIDE_APPLICATION_CONTROLLED;
|
||||||
|
drs_setting.u32CurrentValue = REFRESH_RATE_OVERRIDE_APPLICATION_CONTROLLED;
|
||||||
|
return NvAPI_DRS_SetSetting(h_session, h_profile, &drs_setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
NvAPI_Status set_max_fps(NvDRSSessionHandle h_session, NvDRSProfileHandle h_profile) {
|
||||||
|
auto drs_setting = NVDRS_SETTING {};
|
||||||
|
drs_setting.version = NVDRS_SETTING_VER;
|
||||||
|
drs_setting.settingId = FRL_FPS_ID;
|
||||||
|
drs_setting.settingType = NVDRS_DWORD_TYPE;
|
||||||
|
drs_setting.u32PredefinedValue = FRL_FPS_DISABLED;
|
||||||
|
drs_setting.u32CurrentValue = FRL_FPS_DISABLED;
|
||||||
|
return NvAPI_DRS_SetSetting(h_session, h_profile, &drs_setting);
|
||||||
|
}
|
||||||
|
|
||||||
void set_profile_settings() {
|
void set_profile_settings() {
|
||||||
if (!NVAPI_MODULE_LOAD_SUCCEEDED) {
|
if (!NVAPI_MODULE_LOAD_SUCCEEDED) {
|
||||||
return;
|
return;
|
||||||
@@ -266,22 +286,26 @@ namespace nvapi {
|
|||||||
log_info("nvapi", "setting VSync to Application Controlled...");
|
log_info("nvapi", "setting VSync to Application Controlled...");
|
||||||
if ((status = set_vsync_mode(h_session, h_profile)) != NVAPI_OK) {
|
if ((status = set_vsync_mode(h_session, h_profile)) != NVAPI_OK) {
|
||||||
log_warning("nvapi", "could not set VSync mode: {}", get_error_message(status));
|
log_warning("nvapi", "could not set VSync mode: {}", get_error_message(status));
|
||||||
NvAPI_DRS_DestroySession(h_session);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("nvapi", "applying preferred PState to Maximum Performance...");
|
log_info("nvapi", "applying preferred PState to Maximum Performance...");
|
||||||
if ((status = set_gpu_power_state(h_session, h_profile)) != NVAPI_OK) {
|
if ((status = set_gpu_power_state(h_session, h_profile)) != NVAPI_OK) {
|
||||||
log_warning("nvapi", "could not set preferred PState: {}", get_error_message(status));
|
log_warning("nvapi", "could not set preferred PState: {}", get_error_message(status));
|
||||||
NvAPI_DRS_DestroySession(h_session);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("nvapi", "disabling G-SYNC...");
|
log_info("nvapi", "disabling G-SYNC...");
|
||||||
if ((status = set_gsync_mode(h_session, h_profile)) != NVAPI_OK) {
|
if ((status = set_gsync_mode(h_session, h_profile)) != NVAPI_OK) {
|
||||||
log_warning("nvapi", "could not set G-Sync mode: {}", get_error_message(status));
|
log_warning("nvapi", "could not set G-Sync mode: {}", get_error_message(status));
|
||||||
NvAPI_DRS_DestroySession(h_session);
|
}
|
||||||
return;
|
|
||||||
|
log_info("nvapi", "setting preferred refresh rate to Application Controlled...");
|
||||||
|
if ((status = set_refresh_rate(h_session, h_profile)) != NVAPI_OK) {
|
||||||
|
log_warning("nvapi", "could not set preferred refresh rate: {}", get_error_message(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("nvapi", "disabling max framerate...");
|
||||||
|
if ((status = set_max_fps(h_session, h_profile)) != NVAPI_OK) {
|
||||||
|
log_warning("nvapi", "could not disable max framerate: {}", get_error_message(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("nvapi", "saving settings for DRS session...");
|
log_info("nvapi", "saving settings for DRS session...");
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace ImGui {
|
|||||||
|
|
||||||
void HelpMarker(const char* desc) {
|
void HelpMarker(const char* desc) {
|
||||||
ImGui::TextDisabled("(?)");
|
ImGui::TextDisabled("(?)");
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||||
HelpTooltip(desc);
|
HelpTooltip(desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ namespace ImGui {
|
|||||||
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 1.f, 0.f, 1.f));
|
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||||||
ImGui::TextDisabled("(!)");
|
ImGui::TextDisabled("(!)");
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||||
WarnTooltip(desc, warn);
|
WarnTooltip(desc, warn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,4 +92,75 @@ namespace ImGui {
|
|||||||
ImGui::GetColorU32(ImGuiCol_PlotHistogram),
|
ImGui::GetColorU32(ImGuiCol_PlotHistogram),
|
||||||
thickness);
|
thickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TruncateText(const std::string& p_text, float p_truncated_width, bool &truncated) {
|
||||||
|
std::string truncated_text = p_text;
|
||||||
|
|
||||||
|
const float text_width =
|
||||||
|
ImGui::CalcTextSize(p_text.c_str(), nullptr, true).x;
|
||||||
|
|
||||||
|
if (text_width > p_truncated_width) {
|
||||||
|
truncated = true;
|
||||||
|
constexpr const char* ELLIPSIS = " ...";
|
||||||
|
const float ellipsis_size = ImGui::CalcTextSize(ELLIPSIS).x;
|
||||||
|
|
||||||
|
int visible_chars = 0;
|
||||||
|
for (size_t i = 0; i < p_text.size(); i++) {
|
||||||
|
const float current_width = ImGui::CalcTextSize(
|
||||||
|
p_text.substr(0, i).c_str(), nullptr, true)
|
||||||
|
.x;
|
||||||
|
if (current_width + ellipsis_size > p_truncated_width) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
visible_chars = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncated_text = (p_text.substr(0, visible_chars) + ELLIPSIS).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return truncated_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextTruncated(const std::string& p_text, float p_truncated_width) {
|
||||||
|
if (p_text.empty()) {
|
||||||
|
ImGui::TextDisabled("-");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool truncated = false;
|
||||||
|
ImGui::TextUnformatted(TruncateText(p_text, p_truncated_width, truncated).c_str());
|
||||||
|
if (truncated && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||||
|
ImGui::HelpTooltip(p_text.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeleteButton(const std::string& tooltip) {
|
||||||
|
ImGui::PushID(tooltip.c_str());
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_Button, 0.7f));
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(3.f, 2.f));
|
||||||
|
bool clicked = ImGui::SmallButton("\u00D7"); // multiplication sign (×)
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||||
|
ImGui::HelpTooltip(tooltip.c_str());
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClearButton(const std::string& tooltip) {
|
||||||
|
ImGui::PushID(tooltip.c_str());
|
||||||
|
// same colors as a checkbox
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(ImGuiCol_FrameBgHovered));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetColorU32(ImGuiCol_FrameBgActive));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.97f, 0.97f, 0.97f, 1.00f));
|
||||||
|
bool clicked = ImGui::Button("\u00D7"); // multiplication sign (×)
|
||||||
|
ImGui::PopStyleColor(4);
|
||||||
|
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||||
|
ImGui::HelpTooltip(tooltip.c_str());
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "external/imgui/imgui.h"
|
||||||
|
|
||||||
namespace ImGui {
|
namespace ImGui {
|
||||||
|
|
||||||
|
constexpr ImGuiHoveredFlags TOOLTIP_FLAGS =
|
||||||
|
(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_AllowWhenDisabled);
|
||||||
|
|
||||||
void HelpTooltip(const char* desc);
|
void HelpTooltip(const char* desc);
|
||||||
void HelpMarker(const char* desc);
|
void HelpMarker(const char* desc);
|
||||||
void WarnTooltip(const char* desc, const char* warn);
|
void WarnTooltip(const char* desc, const char* warn);
|
||||||
void WarnMarker(const char* desc, const char* warn);
|
void WarnMarker(const char* desc, const char* warn);
|
||||||
void DummyMarker();
|
void DummyMarker();
|
||||||
|
|
||||||
void Knob(float fraction, float size, float thickness = 2.f,
|
void Knob(float fraction, float size, float thickness = 2.f,
|
||||||
float pos_x = -1.f, float pos_y = -1.f);
|
float pos_x = -1.f, float pos_y = -1.f);
|
||||||
|
|
||||||
|
void TextTruncated(const std::string& p_text, float p_truncated_width);
|
||||||
|
bool DeleteButton(const std::string& tooltip);
|
||||||
|
bool ClearButton(const std::string& tooltip);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
|
|
||||||
@@ -29,6 +30,8 @@ static HWND g_hWnd = nullptr;
|
|||||||
static INT64 g_Time = 0;
|
static INT64 g_Time = 0;
|
||||||
static INT64 g_TicksPerSecond = 0;
|
static INT64 g_TicksPerSecond = 0;
|
||||||
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||||
|
static double g_LastMouseMovement = 0.f;
|
||||||
|
static bool g_MouseCursorAutoHide = false;
|
||||||
|
|
||||||
bool ImGui_ImplSpice_Init(HWND hWnd) {
|
bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||||
log_misc("imgui_impl_spice", "init");
|
log_misc("imgui_impl_spice", "init");
|
||||||
@@ -83,6 +86,12 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
|
|||||||
// get display size
|
// get display size
|
||||||
ImGui_ImplSpice_UpdateDisplaySize();
|
ImGui_ImplSpice_UpdateDisplaySize();
|
||||||
|
|
||||||
|
// only enable mouse cursor auto hiding if we want ImGui to draw the cursor
|
||||||
|
// (if OS is drawing it, we don't auto-hide)
|
||||||
|
if (io.MouseDrawCursor && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
g_MouseCursorAutoHide = true;
|
||||||
|
}
|
||||||
|
|
||||||
// return success
|
// return success
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -283,7 +292,7 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
memset(io.MouseDown, false, sizeof(io.MouseDown));
|
memset(io.MouseDown, false, sizeof(io.MouseDown));
|
||||||
|
|
||||||
// early quit if window not in focus
|
// early quit if window not in focus
|
||||||
if (!superexit::has_focus()) {
|
if (!superexit::has_focus() || rawinput::OS_WINDOW_ACTIVE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,12 +384,31 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set mouse wheel
|
// set mouse wheel
|
||||||
auto mouse_diff = mouse_wheel - mouse_wheel_last;
|
auto wheel_diff = mouse_wheel - mouse_wheel_last;
|
||||||
mouse_wheel_last = mouse_wheel;
|
mouse_wheel_last = mouse_wheel;
|
||||||
io.MouseWheel = mouse_diff;
|
io.MouseWheel = wheel_diff;
|
||||||
|
|
||||||
// update OS mouse position
|
// update OS mouse position
|
||||||
|
const auto old_mouse_pos = io.MousePos;
|
||||||
ImGui_ImplSpice_UpdateMousePos();
|
ImGui_ImplSpice_UpdateMousePos();
|
||||||
|
const auto new_mouse_pos = io.MousePos;
|
||||||
|
|
||||||
|
// automatically hide cursor
|
||||||
|
if (g_MouseCursorAutoHide) {
|
||||||
|
if (old_mouse_pos.x != new_mouse_pos.x ||
|
||||||
|
old_mouse_pos.y != new_mouse_pos.y ||
|
||||||
|
wheel_diff != 0 ||
|
||||||
|
io.MouseDown[0] || io.MouseDown[1] || io.MouseDown[2]) {
|
||||||
|
|
||||||
|
// mouse moved, update time and show the cursor
|
||||||
|
g_LastMouseMovement = get_performance_milliseconds();
|
||||||
|
io.MouseDrawCursor = true;
|
||||||
|
|
||||||
|
} else if ((get_performance_milliseconds() - g_LastMouseMovement) > 2000) {
|
||||||
|
// mouse idle for more than 2 seconds, hide the cursor
|
||||||
|
io.MouseDrawCursor = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||||
// if cursor is inside the client area, always set the OS cursor to what ImGui wants
|
// if cursor is inside the client area, always set the OS cursor to what ImGui wants
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#include "windows/generic_sub.h"
|
#include "windows/generic_sub.h"
|
||||||
#include "windows/iidx_seg.h"
|
#include "windows/iidx_seg.h"
|
||||||
#include "windows/iidx_sub.h"
|
#include "windows/iidx_sub.h"
|
||||||
|
#include "windows/gfdm_sub.h"
|
||||||
#include "windows/drs_dancefloor.h"
|
#include "windows/drs_dancefloor.h"
|
||||||
#include "windows/iopanel.h"
|
#include "windows/iopanel.h"
|
||||||
#include "windows/iopanel_ddr.h"
|
#include "windows/iopanel_ddr.h"
|
||||||
@@ -51,16 +53,26 @@ namespace overlay {
|
|||||||
bool AUTO_SHOW_IOPANEL = false;
|
bool AUTO_SHOW_IOPANEL = false;
|
||||||
bool AUTO_SHOW_KEYPAD_P1 = false;
|
bool AUTO_SHOW_KEYPAD_P1 = false;
|
||||||
bool AUTO_SHOW_KEYPAD_P2 = false;
|
bool AUTO_SHOW_KEYPAD_P2 = false;
|
||||||
|
|
||||||
bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = false;
|
bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = false;
|
||||||
|
|
||||||
bool FPS_SHOULD_FLIP = false;
|
bool FPS_SHOULD_FLIP = false;
|
||||||
|
std::optional<uint32_t> UI_SCALE_PERCENT;
|
||||||
|
|
||||||
// global
|
// global
|
||||||
std::mutex OVERLAY_MUTEX;
|
std::mutex OVERLAY_MUTEX;
|
||||||
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
|
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
|
||||||
ImFont* DSEG_FONT = nullptr;
|
ImFont* DSEG_FONT = nullptr;
|
||||||
bool SHOW_DEBUG_LOG_WINDOW = false;
|
bool SHOW_DEBUG_LOG_WINDOW = false;
|
||||||
|
|
||||||
|
ImVec2 apply_scaling_to_vector(float x, float y) {
|
||||||
|
if (!UI_SCALE_PERCENT.has_value()) {
|
||||||
|
return ImVec2(x, y);
|
||||||
|
}
|
||||||
|
return ImVec2(apply_scaling(x), apply_scaling(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 apply_scaling_to_vector(const ImVec2& input) {
|
||||||
|
return apply_scaling_to_vector(input.x, input.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *ImGui_Alloc(size_t sz, void *user_data) {
|
static void *ImGui_Alloc(size_t sz, void *user_data) {
|
||||||
@@ -170,71 +182,79 @@ void overlay::SpiceOverlay::init() {
|
|||||||
style.WindowRounding = 0;
|
style.WindowRounding = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// red theme based on:
|
if (UI_SCALE_PERCENT.has_value()) {
|
||||||
// https://github.com/ocornut/imgui/issues/707#issuecomment-760220280
|
const auto scale = static_cast<float>(UI_SCALE_PERCENT.value()) / 100.f;
|
||||||
// r, g, b, a
|
ImGui::GetStyle().ScaleAllSizes(scale);
|
||||||
|
ImGui::GetIO().FontGlobalScale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on CrimsonVesuvius theme from
|
||||||
|
// https://github.com/ocornut/imgui/issues/707#issuecomment-4107169777
|
||||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||||
|
|
||||||
// note: also see bg_alpha value in each overlay window
|
// Text
|
||||||
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 1.0f);
|
colors[ImGuiCol_Text] = ImVec4(1.00f, 0.90f, 0.90f, 1.00f); // Slight pinkish tint to off-white
|
||||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 1.0f);
|
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.40f, 0.40f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
|
// Backgrounds
|
||||||
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
colors[ImGuiCol_WindowBg] = ImVec4(0.08f, 0.07f, 0.07f, 1.00f); // Deep charcoal
|
||||||
|
colors[ImGuiCol_ChildBg] = ImVec4(0.10f, 0.09f, 0.09f, 1.00f);
|
||||||
|
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.07f, 0.07f, 0.96f);
|
||||||
|
|
||||||
colors[ImGuiCol_FrameBg] = ImVec4(0.18f, 0.18f, 0.18f, 0.54f);
|
// Borders
|
||||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.18f, 0.18f, 0.18f, 0.80f);
|
colors[ImGuiCol_Border] = ImVec4(0.25f, 0.15f, 0.15f, 0.80f);
|
||||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 0.80f);
|
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
|
// Frames
|
||||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
colors[ImGuiCol_FrameBg] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
||||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.25f, 0.15f, 0.15f, 1.00f);
|
||||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
colors[ImGuiCol_FrameBgActive] = ImVec4(0.35f, 0.20f, 0.20f, 1.00f);
|
||||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
|
||||||
colors[ImGuiCol_CheckMark] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
|
||||||
colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
|
||||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
|
||||||
|
|
||||||
colors[ImGuiCol_Button] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
// Title Bars
|
||||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.17f, 0.00f, 1.00f);
|
colors[ImGuiCol_TitleBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
||||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.17f, 0.00f, 0.90f);
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.25f, 0.10f, 0.10f, 1.00f);
|
||||||
|
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_Header] = ImVec4(0.33f, 0.35f, 0.36f, 0.53f);
|
// Menus
|
||||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.67f);
|
colors[ImGuiCol_MenuBarBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
||||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.47f, 0.47f, 0.47f, 0.67f);
|
|
||||||
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
|
||||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
|
||||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
|
||||||
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.85f);
|
|
||||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
|
|
||||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
|
|
||||||
|
|
||||||
colors[ImGuiCol_Tab] = colors[ImGuiCol_Button];
|
// Scrollbars
|
||||||
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_ButtonHovered];
|
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||||
colors[ImGuiCol_TabActive] = colors[ImGuiCol_ButtonActive];
|
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.25f, 0.12f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_TabUnfocused] = colors[ImGuiCol_Tab] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.35f, 0.15f, 0.15f, 1.00f);
|
||||||
colors[ImGuiCol_TabUnfocusedActive] = colors[ImGuiCol_TabActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.45f, 0.20f, 0.20f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_DockingPreview] = ImVec4(0.47f, 0.47f, 0.47f, 0.47f);
|
// Interactables (The High-Intensity Red)
|
||||||
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
colors[ImGuiCol_CheckMark] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f); // Sharp Red
|
||||||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
colors[ImGuiCol_SliderGrab] = ImVec4(0.60f, 0.12f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f);
|
||||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
colors[ImGuiCol_Button] = ImVec4(0.30f, 0.12f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
colors[ImGuiCol_ButtonHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
||||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
|
colors[ImGuiCol_ButtonActive] = ImVec4(0.70f, 0.25f, 0.25f, 1.00f);
|
||||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
|
colors[ImGuiCol_Header] = ImVec4(0.30f, 0.12f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
|
colors[ImGuiCol_HeaderHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
||||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
colors[ImGuiCol_HeaderActive] = ImVec4(0.70f, 0.25f, 0.25f, 1.00f);
|
||||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.02f);
|
|
||||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
// Tabs
|
||||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
colors[ImGuiCol_Tab] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
||||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
colors[ImGuiCol_TabHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
||||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
colors[ImGuiCol_TabActive] = ImVec4(0.35f, 0.12f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
|
||||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
// Misc
|
||||||
|
colors[ImGuiCol_PlotLines] = ImVec4(0.85f, 0.20f, 0.20f, 1.00f);
|
||||||
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.85f, 0.15f, 0.15f, 0.35f);
|
||||||
|
colors[ImGuiCol_NavHighlight] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f);
|
||||||
|
|
||||||
|
// spice custom
|
||||||
|
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.025f);
|
||||||
|
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.22f, 0.22f, 1.00f);
|
||||||
|
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.42f, 0.22f, 0.22f, 1.00f);
|
||||||
|
colors[ImGuiCol_SeparatorActive] = ImVec4(0.52f, 0.22f, 0.22f, 1.00f);
|
||||||
|
|
||||||
|
#ifdef IMGUI_HAS_DOCK
|
||||||
|
colors[ImGuiCol_DockingPreview] = ImVec4(0.85f, 0.15f, 0.15f, 0.40f);
|
||||||
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.08f, 0.07f, 0.07f, 1.00f);
|
||||||
|
#endif
|
||||||
|
|
||||||
// configure IO
|
// configure IO
|
||||||
auto &io = ImGui::GetIO();
|
auto &io = ImGui::GetIO();
|
||||||
@@ -245,7 +265,6 @@ void overlay::SpiceOverlay::init() {
|
|||||||
|
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
|
||||||
}
|
}
|
||||||
if (is_touch_available("SpiceOverlay::init")) {
|
if (is_touch_available("SpiceOverlay::init")) {
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
|
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
|
||||||
@@ -366,7 +385,8 @@ void overlay::SpiceOverlay::init() {
|
|||||||
window_iopanel = new overlay::windows::IIDXIOPanel(this);
|
window_iopanel = new overlay::windows::IIDXIOPanel(this);
|
||||||
} else if (avs::game::is_model("MDX")) {
|
} else if (avs::game::is_model("MDX")) {
|
||||||
window_iopanel = new overlay::windows::DDRIOPanel(this);
|
window_iopanel = new overlay::windows::DDRIOPanel(this);
|
||||||
} else if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
} else if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"}) &&
|
||||||
|
!games::gitadora::is_arena_model()) {
|
||||||
window_iopanel = new overlay::windows::GitadoraIOPanel(this);
|
window_iopanel = new overlay::windows::GitadoraIOPanel(this);
|
||||||
} else {
|
} else {
|
||||||
window_iopanel = new overlay::windows::IOPanel(this);
|
window_iopanel = new overlay::windows::IOPanel(this);
|
||||||
@@ -393,7 +413,10 @@ void overlay::SpiceOverlay::init() {
|
|||||||
window_sub = new overlay::windows::DRSDanceFloorDisplay(this);
|
window_sub = new overlay::windows::DRSDanceFloorDisplay(this);
|
||||||
} else if (avs::game::is_model("KFC")) {
|
} else if (avs::game::is_model("KFC")) {
|
||||||
window_sub = new overlay::windows::SDVXSubScreen(this);
|
window_sub = new overlay::windows::SDVXSubScreen(this);
|
||||||
|
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||||
|
window_sub = new overlay::windows::GitaDoraSubScreen(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window_sub) {
|
if (window_sub) {
|
||||||
this->window_add(window_sub);
|
this->window_add(window_sub);
|
||||||
if (AUTO_SHOW_SUBSCREEN) {
|
if (AUTO_SHOW_SUBSCREEN) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
@@ -27,6 +28,18 @@ namespace overlay {
|
|||||||
extern bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT;
|
extern bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT;
|
||||||
extern bool FPS_SHOULD_FLIP;
|
extern bool FPS_SHOULD_FLIP;
|
||||||
extern bool SHOW_DEBUG_LOG_WINDOW;
|
extern bool SHOW_DEBUG_LOG_WINDOW;
|
||||||
|
extern std::optional<uint32_t> UI_SCALE_PERCENT;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
float apply_scaling(T input) {
|
||||||
|
if (!UI_SCALE_PERCENT.has_value()) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
return static_cast<float>(input) * UI_SCALE_PERCENT.value() / 100.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 apply_scaling_to_vector(const ImVec2& input);
|
||||||
|
ImVec2 apply_scaling_to_vector(float x, float y);
|
||||||
|
|
||||||
class SpiceOverlay {
|
class SpiceOverlay {
|
||||||
public:
|
public:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user