mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
Compare commits
54 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 |
@@ -58,6 +58,7 @@ Watch out for legacy OS compatibility. Currently, the minimum support floor is W
|
||||
* Indents are four spaces.
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
OK:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
>
|
||||
> 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
|
||||
|
||||
## Description of change
|
||||
|
||||
@@ -109,6 +109,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# RapidJSON does this
|
||||
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
|
||||
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")
|
||||
|
||||
# file prefix map for relative working directory
|
||||
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_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}=.\"")
|
||||
endif()
|
||||
|
||||
# release with debug info flags
|
||||
@@ -346,6 +352,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
cfg/configurator.cpp
|
||||
cfg/configurator_wnd.cpp
|
||||
cfg/screen_resize.cpp
|
||||
cfg/controller_presets.cpp
|
||||
|
||||
# easrv
|
||||
easrv/easrv.cpp
|
||||
@@ -559,6 +566,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
overlay/windows/screen_resize.cpp
|
||||
overlay/windows/sdvx_sub.cpp
|
||||
overlay/windows/config.cpp
|
||||
overlay/windows/controller_presets.cpp
|
||||
overlay/windows/control.cpp
|
||||
overlay/windows/eadev.cpp
|
||||
overlay/windows/fps.cpp
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace api::modules {
|
||||
void DRS::touch_set(Request &req, Response &res) {
|
||||
|
||||
// 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;
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
@@ -86,6 +86,6 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
// apply touch points
|
||||
games::drs::fire_touches(touches, i);
|
||||
games::drs::fire_touches(touches.get(), i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
||||
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
||||
TOOLCHAIN_32="/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"
|
||||
TOOLCHAIN_64="/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"
|
||||
TOOLCHAIN_32="${TOOLCHAIN_32:-"/usr/share/mingw/toolchain-i686-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_DEBUG="./cmake-build-debug-32"
|
||||
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||
@@ -74,7 +74,7 @@ then
|
||||
BUILD_TYPE="Debug"
|
||||
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
||||
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
||||
DIST_NAME=$(echo ${DIST_NAME} | sed 's/\.[^.]*$/-dbg&/')
|
||||
DIST_NAME=$(echo "${DIST_NAME}" | sed 's/\.[^.]*$/-dbg&/')
|
||||
fi
|
||||
|
||||
# determine number of cores
|
||||
@@ -114,7 +114,7 @@ time (
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_32}
|
||||
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
|
||||
|
||||
# 64 bit
|
||||
@@ -127,7 +127,7 @@ time (
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_64}
|
||||
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
|
||||
|
||||
echo ""
|
||||
@@ -234,7 +234,7 @@ mkdir -p ${OUTDIR}/src
|
||||
if ((INCLUDE_SRC > 0))
|
||||
then
|
||||
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?"
|
||||
fi
|
||||
|
||||
@@ -249,11 +249,11 @@ if ((DIST_ENABLE > 0))
|
||||
then
|
||||
echo "Building dist..."
|
||||
mkdir -p ${DIST_FOLDER}
|
||||
rm -rf ${DIST_FOLDER}/${DIST_NAME}
|
||||
rm -rf "${DIST_FOLDER}/${DIST_NAME}"
|
||||
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..."
|
||||
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
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
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 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 build --pull "$PWD/external/docker" -t spicetools/deps --platform linux/x86_64
|
||||
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 "$@"
|
||||
|
||||
@@ -13,5 +13,6 @@ IDR_LICENSES RCDATA "../licenses.txt"
|
||||
IDR_PATCHES RCDATA "../build/patches.json"
|
||||
IDR_README RCDATA "../readme.txt"
|
||||
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
||||
IDR_CONTROLLER_PRESETS_BUILTIN RCDATA "../build/controller_presets.xml"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -91,6 +91,7 @@ public:
|
||||
setMultiplier(1);
|
||||
setRelativeMode(false);
|
||||
setDelayBufferDepth(0);
|
||||
setLastState(0.5f);
|
||||
}
|
||||
|
||||
inline void clearBindings() {
|
||||
|
||||
+50
-5
@@ -37,7 +37,11 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
||||
for (auto &bt : buttons) {
|
||||
if (name == bt.getName()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +51,7 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
||||
|
||||
if (vkey_defaults) {
|
||||
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: {
|
||||
|
||||
// get value
|
||||
if (inverted) {
|
||||
value = 1.f - device->hidInfo->value_states[index];
|
||||
} else {
|
||||
value = device->hidInfo->value_states[index];
|
||||
if (index < device->hidInfo->value_states.size()) {
|
||||
if (inverted) {
|
||||
value = 1.f - device->hidInfo->value_states[index];
|
||||
} else {
|
||||
value = device->hidInfo->value_states[index];
|
||||
}
|
||||
}
|
||||
|
||||
// deadzone
|
||||
@@ -859,6 +866,44 @@ std::vector<Light> GameAPI::Lights::sortLights(
|
||||
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) {
|
||||
|
||||
// check device
|
||||
|
||||
+15
-21
@@ -6,24 +6,20 @@
|
||||
#include <windows.h>
|
||||
#include "option.h"
|
||||
|
||||
#include "button.h"
|
||||
#include "analog.h"
|
||||
#include "light.h"
|
||||
|
||||
namespace rawinput {
|
||||
class RawInputManager;
|
||||
struct Device;
|
||||
}
|
||||
|
||||
class Button;
|
||||
class Analog;
|
||||
class Light;
|
||||
class Game;
|
||||
class Option;
|
||||
|
||||
namespace GameAPI {
|
||||
namespace Buttons {
|
||||
enum State {
|
||||
BUTTON_PRESSED = true,
|
||||
BUTTON_NOT_PRESSED = false
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the config and returns the buttons set by the user.
|
||||
*
|
||||
@@ -113,21 +109,13 @@ namespace GameAPI {
|
||||
}
|
||||
|
||||
namespace Lights {
|
||||
|
||||
std::vector<Light> getLights(const std::string &game_name);
|
||||
|
||||
std::vector<Light> sortLights(
|
||||
const std::vector<Light> &lights,
|
||||
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>
|
||||
void sortLights(std::vector<Light> *lights, T t, Rest... 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::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> &);
|
||||
}
|
||||
}
|
||||
|
||||
#include "button.h"
|
||||
#include "analog.h"
|
||||
#include "light.h"
|
||||
|
||||
+50
-30
@@ -44,7 +44,7 @@ std::string Button::getVKeyString() {
|
||||
case 0x0C:
|
||||
return "Clear";
|
||||
case 0x0D:
|
||||
return "Enter";
|
||||
return "Enter/Return";
|
||||
case 0x10:
|
||||
return "Shift";
|
||||
case 0x11:
|
||||
@@ -58,8 +58,28 @@ std::string Button::getVKeyString() {
|
||||
return "Pause";
|
||||
case 0x14:
|
||||
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:
|
||||
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:
|
||||
return "Space";
|
||||
case 0x21:
|
||||
@@ -163,37 +183,37 @@ std::string Button::getVKeyString() {
|
||||
case 0x5D:
|
||||
return "Apps";
|
||||
case 0x60:
|
||||
return "Num 0";
|
||||
return "Numpad 0";
|
||||
case 0x61:
|
||||
return "Num 1";
|
||||
return "Numpad 1";
|
||||
case 0x62:
|
||||
return "Num 2";
|
||||
return "Numpad 2";
|
||||
case 0x63:
|
||||
return "Num 3";
|
||||
return "Numpad 3";
|
||||
case 0x64:
|
||||
return "Num 4";
|
||||
return "Numpad 4";
|
||||
case 0x65:
|
||||
return "Num 5";
|
||||
return "Numpad 5";
|
||||
case 0x66:
|
||||
return "Num 6";
|
||||
return "Numpad 6";
|
||||
case 0x67:
|
||||
return "Num 7";
|
||||
return "Numpad 7";
|
||||
case 0x68:
|
||||
return "Num 8";
|
||||
return "Numpad 8";
|
||||
case 0x69:
|
||||
return "Num 9";
|
||||
return "Numpad 9";
|
||||
case 0x6A:
|
||||
return "*";
|
||||
return "Numpad *";
|
||||
case 0x6B:
|
||||
return "+";
|
||||
return "Numpad +";
|
||||
case 0x6C:
|
||||
return "Seperator";
|
||||
return "Numpad Seperator";
|
||||
case 0x6D:
|
||||
return "-";
|
||||
return "Numpad -";
|
||||
case 0x6E:
|
||||
return ".";
|
||||
return "Numpad .";
|
||||
case 0x6F:
|
||||
return "/";
|
||||
return "Numpad /";
|
||||
case 0x70:
|
||||
return "F1";
|
||||
case 0x71:
|
||||
@@ -255,9 +275,9 @@ std::string Button::getVKeyString() {
|
||||
case 0xA3:
|
||||
return "Right Control";
|
||||
case 0xA4:
|
||||
return "Left Menu";
|
||||
return "Left Alt";
|
||||
case 0xA5:
|
||||
return "Right Menu";
|
||||
return "Right Alt";
|
||||
default:
|
||||
|
||||
// check win API
|
||||
@@ -301,17 +321,17 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
||||
switch (device->type) {
|
||||
case rawinput::MOUSE: {
|
||||
const char *btn = "Unknown";
|
||||
static const char *MOUSE_NAMES[] = {
|
||||
"Left Mouse",
|
||||
"Right Mouse",
|
||||
"Middle Mouse",
|
||||
"Mouse 1",
|
||||
"Mouse 2",
|
||||
"Mouse 3",
|
||||
"Mouse 4",
|
||||
"Mouse 5",
|
||||
};
|
||||
if (vKey < sizeof(MOUSE_NAMES)) {
|
||||
static const std::array<const char *, 8>MOUSE_NAMES = {
|
||||
"Left Mouse",
|
||||
"Right Mouse",
|
||||
"Middle Mouse",
|
||||
"Mouse 1",
|
||||
"Mouse 2",
|
||||
"Mouse 3",
|
||||
"Mouse 4",
|
||||
"Mouse 5",
|
||||
};
|
||||
if (vKey < MOUSE_NAMES.size()) {
|
||||
btn = MOUSE_NAMES[vKey];
|
||||
}
|
||||
return fmt::format("{} ({})", btn, device->desc);
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api.h"
|
||||
|
||||
namespace rawinput {
|
||||
class RawInputManager;
|
||||
}
|
||||
|
||||
namespace GameAPI {
|
||||
namespace Buttons {
|
||||
enum State {
|
||||
BUTTON_PRESSED = true,
|
||||
BUTTON_NOT_PRESSED = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum ButtonAnalogType {
|
||||
BAT_NONE = 0,
|
||||
BAT_POSITIVE = 1,
|
||||
@@ -41,19 +49,24 @@ private:
|
||||
std::string name;
|
||||
std::string device_identifier = "";
|
||||
unsigned short vKey = INVALID_VKEY;
|
||||
|
||||
// default bindings are always naive
|
||||
unsigned short vKey_default = INVALID_VKEY;
|
||||
|
||||
ButtonAnalogType analog_type = BAT_NONE;
|
||||
double debounce_up = 0.0;
|
||||
double debounce_down = 0.0;
|
||||
bool invert = false;
|
||||
bool is_temporary = false;
|
||||
|
||||
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||
float last_velocity = 0.f;
|
||||
unsigned short velocity_threshold = 0;
|
||||
|
||||
std::string getVKeyString();
|
||||
std::string getMidiNoteString();
|
||||
|
||||
public:
|
||||
std::string getVKeyString();
|
||||
|
||||
// overrides
|
||||
bool override_enabled = false;
|
||||
@@ -116,6 +129,14 @@ public:
|
||||
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 {
|
||||
return this->analog_type;
|
||||
}
|
||||
@@ -164,6 +185,14 @@ public:
|
||||
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 {
|
||||
return this->velocity_threshold;
|
||||
}
|
||||
@@ -172,6 +201,20 @@ public:
|
||||
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 setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "config.h"
|
||||
#include "util/logging.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
/*
|
||||
* This code absolutely sucks.
|
||||
@@ -10,6 +11,14 @@
|
||||
// settings
|
||||
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 ///
|
||||
@@ -510,8 +519,10 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
||||
return false;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *gameButtonNode = nullptr;
|
||||
|
||||
// iterate button nodes
|
||||
tinyxml2::XMLElement *gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||
gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||
int button_count = 0;
|
||||
while (gameButtonNode != nullptr) {
|
||||
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
|
||||
if (button_count == 0) {
|
||||
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
|
||||
if (light_count == 0) {
|
||||
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:
|
||||
std::vector<Light> alternatives;
|
||||
std::string lightName;
|
||||
std::string lightCategory;
|
||||
std::string deviceIdentifier = "";
|
||||
unsigned int index = 0;
|
||||
bool is_temporary = false;
|
||||
|
||||
public:
|
||||
float last_state = 0.f;
|
||||
@@ -22,6 +24,8 @@ public:
|
||||
float override_state = 0.f;
|
||||
|
||||
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);
|
||||
|
||||
@@ -50,6 +54,14 @@ public:
|
||||
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 {
|
||||
return this->deviceIdentifier;
|
||||
}
|
||||
@@ -65,4 +77,26 @@ public:
|
||||
inline void setIndex(unsigned int 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,
|
||||
};
|
||||
|
||||
enum class OptionPickerType {
|
||||
None,
|
||||
AsioDriver,
|
||||
EACard,
|
||||
CpuAffinity,
|
||||
FilePath,
|
||||
DirectoryPath,
|
||||
};
|
||||
|
||||
struct OptionDefinition {
|
||||
std::string title;
|
||||
// unique identifier used for flag matching but also stored in config files
|
||||
@@ -32,6 +41,10 @@ struct OptionDefinition {
|
||||
bool sensitive = false;
|
||||
std::vector<std::pair<std::string, std::string>> elements = {};
|
||||
bool disabled = false;
|
||||
OptionPickerType picker = OptionPickerType::None;
|
||||
|
||||
// for OptionPickerType::FilePath
|
||||
std::string file_extension = "";
|
||||
};
|
||||
|
||||
class Option {
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
#define IDR_PATCHES 132
|
||||
#define IDR_README 133
|
||||
#define IDR_DSEGFONT 134
|
||||
#define IDR_CONTROLLER_PRESETS_BUILTIN 135
|
||||
|
||||
+114
-110
@@ -41,136 +41,140 @@ std::vector<Light> &games::ddr::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
&lights,
|
||||
"P1 Foot Left",
|
||||
"P1 Foot Up",
|
||||
"P1 Foot Right",
|
||||
"P1 Foot Down",
|
||||
"P2 Foot Left",
|
||||
"P2 Foot Up",
|
||||
"P2 Foot Right",
|
||||
"P2 Foot Down",
|
||||
"Spot Red",
|
||||
"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",
|
||||
GameAPI::Lights::sortLightsWithCategory(
|
||||
&lights,
|
||||
{
|
||||
{"General", "P1 Foot Left"},
|
||||
{"General", "P1 Foot Up"},
|
||||
{"General", "P1 Foot Right"},
|
||||
{"General", "P1 Foot Down"},
|
||||
{"General", "P2 Foot Left"},
|
||||
{"General", "P2 Foot Up"},
|
||||
{"General", "P2 Foot Right"},
|
||||
{"General", "P2 Foot Down"},
|
||||
|
||||
"WHITE Speaker Top R",
|
||||
"WHITE Speaker Top G",
|
||||
"WHITE Speaker Top B",
|
||||
"WHITE Speaker Bottom R",
|
||||
"WHITE Speaker Bottom G",
|
||||
"WHITE Speaker Bottom B",
|
||||
"WHITE Woofer R",
|
||||
"WHITE Woofer G",
|
||||
"WHITE Woofer B",
|
||||
{"SD", "Spot Red"},
|
||||
{"SD", "Spot Blue"},
|
||||
{"SD", "Top Spot Red"},
|
||||
{"SD", "Top Spot Blue"},
|
||||
{"SD", "P1 Halogen Upper"},
|
||||
{"SD", "P1 Halogen Lower"},
|
||||
{"SD", "P2 Halogen Upper"},
|
||||
{"SD", "P2 Halogen Lower"},
|
||||
{"SD", "P1 Button"},
|
||||
{"SD", "P2 Button"},
|
||||
{"SD", "Neon"},
|
||||
|
||||
"GOLD P1 Menu Start",
|
||||
"GOLD P1 Menu Up",
|
||||
"GOLD P1 Menu Down",
|
||||
"GOLD P1 Menu Left",
|
||||
"GOLD P1 Menu Right",
|
||||
{"HD", "HD P1 Start"},
|
||||
{"HD", "HD P1 Menu Left-Right"},
|
||||
{"HD", "HD P1 Menu Up-Down"},
|
||||
{"HD", "HD P2 Start"},
|
||||
{"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",
|
||||
"GOLD P2 Menu Up",
|
||||
"GOLD P2 Menu Down",
|
||||
"GOLD P2 Menu Left",
|
||||
"GOLD P2 Menu Right",
|
||||
{"WHITE", "WHITE Speaker Top R"},
|
||||
{"WHITE", "WHITE Speaker Top G"},
|
||||
{"WHITE", "WHITE Speaker Top B"},
|
||||
{"WHITE", "WHITE Speaker Bottom R"},
|
||||
{"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 Title Panel Center",
|
||||
"GOLD Title Panel Right",
|
||||
{"GOLD", "GOLD P1 Menu Start"},
|
||||
{"GOLD", "GOLD P1 Menu Up"},
|
||||
{"GOLD", "GOLD P1 Menu Down"},
|
||||
{"GOLD", "GOLD P1 Menu Left"},
|
||||
{"GOLD", "GOLD P1 Menu Right"},
|
||||
|
||||
"GOLD P1 Woofer Corner",
|
||||
"GOLD P2 Woofer Corner",
|
||||
{"GOLD", "GOLD P2 Menu Start"},
|
||||
{"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 P1 Card Unit G",
|
||||
"GOLD P1 Card Unit B",
|
||||
{"GOLD", "GOLD Title Panel Left"},
|
||||
{"GOLD", "GOLD Title Panel Center"},
|
||||
{"GOLD", "GOLD Title Panel Right"},
|
||||
|
||||
"GOLD P2 Card Unit R",
|
||||
"GOLD P2 Card Unit G",
|
||||
"GOLD P2 Card Unit B",
|
||||
{"GOLD", "GOLD P1 Woofer Corner"},
|
||||
{"GOLD", "GOLD P2 Woofer Corner"},
|
||||
|
||||
"GOLD Top Panel Avg R",
|
||||
"GOLD Top Panel Avg G",
|
||||
"GOLD Top Panel Avg B",
|
||||
{"GOLD", "GOLD P1 Card Unit R"},
|
||||
{"GOLD", "GOLD P1 Card Unit G"},
|
||||
{"GOLD", "GOLD P1 Card Unit B"},
|
||||
|
||||
"GOLD Monitor Side Left Avg R",
|
||||
"GOLD Monitor Side Left Avg G",
|
||||
"GOLD Monitor Side Left Avg B",
|
||||
{"GOLD", "GOLD P2 Card Unit R"},
|
||||
{"GOLD", "GOLD P2 Card Unit G"},
|
||||
{"GOLD", "GOLD P2 Card Unit B"},
|
||||
|
||||
"GOLD Monitor Side Right Avg R",
|
||||
"GOLD Monitor Side Right Avg G",
|
||||
"GOLD Monitor Side Right Avg B",
|
||||
{"GOLD", "GOLD Top Panel Avg R"},
|
||||
{"GOLD", "GOLD Top Panel Avg G"},
|
||||
{"GOLD", "GOLD Top Panel Avg B"},
|
||||
|
||||
"GOLD P1 Foot Up Avg R",
|
||||
"GOLD P1 Foot Up Avg G",
|
||||
"GOLD P1 Foot Up Avg B",
|
||||
{"GOLD", "GOLD Monitor Side Left Avg R"},
|
||||
{"GOLD", "GOLD Monitor Side Left Avg G"},
|
||||
{"GOLD", "GOLD Monitor Side Left Avg B"},
|
||||
|
||||
"GOLD P1 Foot Down Avg R",
|
||||
"GOLD P1 Foot Down Avg G",
|
||||
"GOLD P1 Foot Down Avg B",
|
||||
{"GOLD", "GOLD Monitor Side Right Avg R"},
|
||||
{"GOLD", "GOLD Monitor Side Right Avg G"},
|
||||
{"GOLD", "GOLD Monitor Side Right Avg B"},
|
||||
|
||||
"GOLD P1 Foot Left Avg R",
|
||||
"GOLD P1 Foot Left Avg G",
|
||||
"GOLD P1 Foot Left Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Up Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Up Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Up Avg B"},
|
||||
|
||||
"GOLD P1 Foot Right Avg R",
|
||||
"GOLD P1 Foot Right Avg G",
|
||||
"GOLD P1 Foot Right Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Down Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Down Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Down Avg B"},
|
||||
|
||||
"GOLD P2 Foot Up Avg R",
|
||||
"GOLD P2 Foot Up Avg G",
|
||||
"GOLD P2 Foot Up Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Left Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Left Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Left Avg B"},
|
||||
|
||||
"GOLD P2 Foot Down Avg R",
|
||||
"GOLD P2 Foot Down Avg G",
|
||||
"GOLD P2 Foot Down Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Right Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Right Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Right Avg B"},
|
||||
|
||||
"GOLD P2 Foot Left Avg R",
|
||||
"GOLD P2 Foot Left Avg G",
|
||||
"GOLD P2 Foot Left Avg B",
|
||||
{"GOLD", "GOLD P2 Foot Up Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Up Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Up Avg B"},
|
||||
|
||||
"GOLD P2 Foot Right Avg R",
|
||||
"GOLD P2 Foot Right Avg G",
|
||||
"GOLD P2 Foot Right Avg B",
|
||||
{"GOLD", "GOLD P2 Foot Down Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Down Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Down Avg B"},
|
||||
|
||||
"GOLD P1 Stage Corner Up-Left",
|
||||
"GOLD P1 Stage Corner Up-Right",
|
||||
"GOLD P1 Stage Corner Down-Left",
|
||||
"GOLD P1 Stage Corner Down-Right",
|
||||
{"GOLD", "GOLD P2 Foot Left Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Left Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Left Avg B"},
|
||||
|
||||
"GOLD P2 Stage Corner Up-Left",
|
||||
"GOLD P2 Stage Corner Up-Right",
|
||||
"GOLD P2 Stage Corner Down-Left",
|
||||
"GOLD P2 Stage Corner Down-Right"
|
||||
{"GOLD", "GOLD P2 Foot Right Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Right Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Right Avg B"},
|
||||
|
||||
{"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
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -158,100 +158,102 @@ std::vector<Light> &games::gitadora::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("GitaDora");
|
||||
|
||||
GameAPI::Lights::sortLights(&lights,
|
||||
"Guitar P1 Motor",
|
||||
"Guitar P2 Motor",
|
||||
GameAPI::Lights::sortLightsWithCategory(&lights,
|
||||
{
|
||||
{"Guitar", "Guitar P1 Motor"},
|
||||
{"Guitar", "Guitar P2 Motor"},
|
||||
|
||||
"P1 Start",
|
||||
"P1 Menu Up Down (DX)",
|
||||
"P1 Menu Left Right (DX)",
|
||||
"P1 Help (DX)",
|
||||
{"Menu", "P1 Start"},
|
||||
{"Menu", "P1 Menu Up Down (DX)"},
|
||||
{"Menu", "P1 Menu Left Right (DX)"},
|
||||
{"Menu", "P1 Help (DX)"},
|
||||
|
||||
"P2 Start",
|
||||
"P2 Menu Up Down (DX)",
|
||||
"P2 Menu Left Right (DX)",
|
||||
"P2 Help (DX)",
|
||||
{"Menu", "P2 Start"},
|
||||
{"Menu", "P2 Menu Up Down (DX)"},
|
||||
{"Menu", "P2 Menu Left Right (DX)"},
|
||||
{"Menu", "P2 Help (DX)"},
|
||||
|
||||
"Drum Left Cymbal",
|
||||
"Drum Hi-Hat",
|
||||
"Drum Snare",
|
||||
"Drum High Tom",
|
||||
"Drum Low Tom",
|
||||
"Drum Floor Tom",
|
||||
"Drum Right Cymbal",
|
||||
{"Drums", "Drum Left Cymbal"},
|
||||
{"Drums", "Drum Hi-Hat"},
|
||||
{"Drums", "Drum Snare"},
|
||||
{"Drums", "Drum High Tom"},
|
||||
{"Drums", "Drum Low Tom"},
|
||||
{"Drums", "Drum Floor Tom"},
|
||||
{"Drums", "Drum Right Cymbal"},
|
||||
|
||||
"Drum Woofer R",
|
||||
"Drum Woofer G",
|
||||
"Drum Woofer B",
|
||||
{"DX/SD", "Drum Woofer R"},
|
||||
{"DX/SD", "Drum Woofer G"},
|
||||
{"DX/SD", "Drum Woofer B"},
|
||||
|
||||
"Drum Stage R (DX)",
|
||||
"Drum Stage G (DX)",
|
||||
"Drum Stage B (DX)",
|
||||
{"DX/SD", "Drum Stage R (DX)"},
|
||||
{"DX/SD", "Drum Stage G (DX)"},
|
||||
{"DX/SD", "Drum Stage B (DX)"},
|
||||
|
||||
"Spot Left (DX)",
|
||||
"Spot Right (DX)",
|
||||
"Spot Center Left (DX)",
|
||||
"Spot Center Right (DX)",
|
||||
{"DX/SD", "Spot Left (DX)"},
|
||||
{"DX/SD", "Spot Right (DX)"},
|
||||
{"DX/SD", "Spot Center Left (DX)"},
|
||||
{"DX/SD", "Spot Center Right (DX)"},
|
||||
|
||||
"Drum Spot Rear Left (DX)",
|
||||
"Drum Spot Rear Right (DX)",
|
||||
{"DX/SD", "Drum Spot Rear Left (DX)"},
|
||||
{"DX/SD", "Drum Spot Rear Right (DX)"},
|
||||
|
||||
"Guitar Lower Left R (DX)",
|
||||
"Guitar Lower Left G (DX)",
|
||||
"Guitar Lower Left B (DX)",
|
||||
"Guitar Lower Right R (DX)",
|
||||
"Guitar Lower Right G (DX)",
|
||||
"Guitar Lower Right B (DX)",
|
||||
{"DX/SD", "Guitar Lower Left R (DX)"},
|
||||
{"DX/SD", "Guitar Lower Left G (DX)"},
|
||||
{"DX/SD", "Guitar Lower Left B (DX)"},
|
||||
{"DX/SD", "Guitar Lower Right R (DX)"},
|
||||
{"DX/SD", "Guitar Lower Right G (DX)"},
|
||||
{"DX/SD", "Guitar Lower Right B (DX)"},
|
||||
|
||||
"Guitar Left Speaker Upper R (DX)",
|
||||
"Guitar Left Speaker Upper G (DX)",
|
||||
"Guitar Left Speaker Upper B (DX)",
|
||||
"Guitar Left Speaker Mid Up Left R (DX)",
|
||||
"Guitar Left Speaker Mid Up Left G (DX)",
|
||||
"Guitar Left Speaker Mid Up Left B (DX)",
|
||||
"Guitar Left Speaker Mid Up Right R (DX)",
|
||||
"Guitar Left Speaker Mid Up Right G (DX)",
|
||||
"Guitar Left Speaker Mid Up Right B (DX)",
|
||||
"Guitar Left Speaker Mid Low Left R (DX)",
|
||||
"Guitar Left Speaker Mid Low Left G (DX)",
|
||||
"Guitar Left Speaker Mid Low Left B (DX)",
|
||||
"Guitar Left Speaker Mid Low Right R (DX)",
|
||||
"Guitar Left Speaker Mid Low Right G (DX)",
|
||||
"Guitar Left Speaker Mid Low Right B (DX)",
|
||||
"Guitar Left Speaker Lower R (DX)",
|
||||
"Guitar Left Speaker Lower G (DX)",
|
||||
"Guitar Left Speaker Lower B (DX)",
|
||||
{"DX/SD", "Guitar Left Speaker Upper R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Upper G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Upper B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Left R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Left G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Left B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Right R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Right G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Right B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Left R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Left G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Left B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Right R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Right G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Right B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Lower R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Lower G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Lower B (DX)"},
|
||||
|
||||
"Guitar Right Speaker Upper R (DX)",
|
||||
"Guitar Right Speaker Upper G (DX)",
|
||||
"Guitar Right Speaker Upper B (DX)",
|
||||
"Guitar Right Speaker Mid Up Left R (DX)",
|
||||
"Guitar Right Speaker Mid Up Left G (DX)",
|
||||
"Guitar Right Speaker Mid Up Left B (DX)",
|
||||
"Guitar Right Speaker Mid Up Right R (DX)",
|
||||
"Guitar Right Speaker Mid Up Right G (DX)",
|
||||
"Guitar Right Speaker Mid Up Right B (DX)",
|
||||
"Guitar Right Speaker Mid Low Left R (DX)",
|
||||
"Guitar Right Speaker Mid Low Left G (DX)",
|
||||
"Guitar Right Speaker Mid Low Left B (DX)",
|
||||
"Guitar Right Speaker Mid Low Right R (DX)",
|
||||
"Guitar Right Speaker Mid Low Right G (DX)",
|
||||
"Guitar Right Speaker Mid Low Right B (DX)",
|
||||
"Guitar Right Speaker Lower R (DX)",
|
||||
"Guitar Right Speaker Lower G (DX)",
|
||||
"Guitar Right Speaker Lower B (DX)",
|
||||
{"DX/SD", "Guitar Right Speaker Upper R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Upper G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Upper B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Left R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Left G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Left B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Right R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Right G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Right B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Left R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Left G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Left B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Right R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Right G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Right B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Lower R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Lower G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Lower B (DX)"},
|
||||
|
||||
"Title Average R (Arena)",
|
||||
"Title Average G (Arena)",
|
||||
"Title Average B (Arena)",
|
||||
{"Arena Model", "Title Average R (Arena)"},
|
||||
{"Arena Model", "Title Average G (Arena)"},
|
||||
{"Arena Model", "Title Average B (Arena)"},
|
||||
|
||||
"Woofer R (Arena)",
|
||||
"Woofer G (Arena)",
|
||||
"Woofer B (Arena)",
|
||||
{"Arena Model", "Woofer R (Arena)"},
|
||||
{"Arena Model", "Woofer G (Arena)"},
|
||||
{"Arena Model", "Woofer B (Arena)"},
|
||||
|
||||
"Card Reader R (Arena)",
|
||||
"Card Reader G (Arena)",
|
||||
"Card Reader B (Arena)"
|
||||
{"Arena Model", "Card Reader R (Arena)"},
|
||||
{"Arena Model", "Card Reader G (Arena)"},
|
||||
{"Arena Model", "Card Reader B (Arena)"}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ namespace games::iidx {
|
||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||
std::mutex IIDX_LED_TICKER_LOCK;
|
||||
bool IIDX_TDJ_MONITOR_WARNING = false;
|
||||
iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE = iidx_aio_emulation_state::unknown;
|
||||
|
||||
// io
|
||||
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
||||
@@ -453,7 +454,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -472,7 +473,7 @@ namespace games::iidx {
|
||||
"!!! please do the following instead: !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! Revert your changes to XML file so it says !!!\n"
|
||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
||||
"!!! in command line !!!\n"
|
||||
@@ -817,7 +818,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -883,12 +884,12 @@ namespace games::iidx {
|
||||
|
||||
// patch iidx32+ for asio compatibility
|
||||
// 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)
|
||||
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.value() == "wasapi")) {
|
||||
|
||||
|
||||
// in iidx32 final:
|
||||
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
||||
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
|
||||
@@ -934,6 +935,8 @@ namespace games::iidx {
|
||||
return;
|
||||
}
|
||||
|
||||
CURRENT_IO_EMULATION_STATE = state;
|
||||
|
||||
// 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
|
||||
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
namespace games::iidx {
|
||||
|
||||
enum class iidx_aio_emulation_state {
|
||||
unknown,
|
||||
bi2a_com2,
|
||||
bi2x_hook
|
||||
};
|
||||
|
||||
// settings
|
||||
|
||||
extern bool FLIP_CAMS;
|
||||
@@ -38,6 +44,7 @@ namespace games::iidx {
|
||||
constexpr int IIDX_TAPELED_TOTAL = 17;
|
||||
// data mapping
|
||||
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
||||
extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE;
|
||||
|
||||
class IIDXGame : public games::Game {
|
||||
public:
|
||||
@@ -64,9 +71,5 @@ namespace games::iidx {
|
||||
bool is_tdj_fhd();
|
||||
void apply_audio_hacks();
|
||||
|
||||
enum class iidx_aio_emulation_state {
|
||||
bi2a_com2,
|
||||
bi2x_hook
|
||||
};
|
||||
void update_io_emulation_state(iidx_aio_emulation_state state);
|
||||
}
|
||||
|
||||
@@ -83,103 +83,105 @@ std::vector<Light> &games::iidx::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
GameAPI::Lights::sortLightsWithCategory(
|
||||
&lights,
|
||||
"P1 1",
|
||||
"P1 2",
|
||||
"P1 3",
|
||||
"P1 4",
|
||||
"P1 5",
|
||||
"P1 6",
|
||||
"P1 7",
|
||||
"P2 1",
|
||||
"P2 2",
|
||||
"P2 3",
|
||||
"P2 4",
|
||||
"P2 5",
|
||||
"P2 6",
|
||||
"P2 7",
|
||||
"P1 Start",
|
||||
"P2 Start",
|
||||
"VEFX",
|
||||
"Effect",
|
||||
"Spot Light 1",
|
||||
"Spot Light 2",
|
||||
"Spot Light 3",
|
||||
"Spot Light 4",
|
||||
"Spot Light 5",
|
||||
"Spot Light 6",
|
||||
"Spot Light 7",
|
||||
"Spot Light 8",
|
||||
"Neon Lamp",
|
||||
"Woofer R",
|
||||
"Woofer G",
|
||||
"Woofer B",
|
||||
"IC Card Reader P1 R",
|
||||
"IC Card Reader P1 G",
|
||||
"IC Card Reader P1 B",
|
||||
"IC Card Reader P2 R",
|
||||
"IC Card Reader P2 G",
|
||||
"IC Card Reader P2 B",
|
||||
"Turntable P1 R",
|
||||
"Turntable P1 G",
|
||||
"Turntable P1 B",
|
||||
"Turntable P2 R",
|
||||
"Turntable P2 G",
|
||||
"Turntable P2 B",
|
||||
"Turntable P1 Resistance",
|
||||
"Turntable P2 Resistance",
|
||||
"Stage Left Avg R",
|
||||
"Stage Left Avg G",
|
||||
"Stage Left Avg B",
|
||||
"Stage Right Avg R",
|
||||
"Stage Right Avg G",
|
||||
"Stage Right Avg B",
|
||||
"Cabinet Left Avg R",
|
||||
"Cabinet Left Avg G",
|
||||
"Cabinet Left Avg B",
|
||||
"Cabinet Right Avg R",
|
||||
"Cabinet Right Avg G",
|
||||
"Cabinet Right Avg B",
|
||||
"Control Panel Under Avg R",
|
||||
"Control Panel Under Avg G",
|
||||
"Control Panel Under Avg B",
|
||||
"Ceiling Left Avg R",
|
||||
"Ceiling Left Avg G",
|
||||
"Ceiling Left Avg B",
|
||||
"Title Left Avg R",
|
||||
"Title Left Avg G",
|
||||
"Title Left Avg B",
|
||||
"Title Right Avg R",
|
||||
"Title Right Avg G",
|
||||
"Title Right Avg B",
|
||||
"Ceiling Right Avg R",
|
||||
"Ceiling Right Avg G",
|
||||
"Ceiling Right Avg B",
|
||||
"Touch Panel Left Avg R",
|
||||
"Touch Panel Left Avg G",
|
||||
"Touch Panel Left Avg B",
|
||||
"Touch Panel Right Avg R",
|
||||
"Touch Panel Right Avg G",
|
||||
"Touch Panel Right Avg B",
|
||||
"Side Panel Left Inner Avg R",
|
||||
"Side Panel Left Inner Avg G",
|
||||
"Side Panel Left Inner Avg B",
|
||||
"Side Panel Left Outer Avg R",
|
||||
"Side Panel Left Outer Avg G",
|
||||
"Side Panel Left Outer Avg B",
|
||||
"Side Panel Left Avg R",
|
||||
"Side Panel Left Avg G",
|
||||
"Side Panel Left Avg B",
|
||||
"Side Panel Right Outer Avg R",
|
||||
"Side Panel Right Outer Avg G",
|
||||
"Side Panel Right Outer Avg B",
|
||||
"Side Panel Right Inner Avg R",
|
||||
"Side Panel Right Inner Avg G",
|
||||
"Side Panel Right Inner Avg B",
|
||||
"Side Panel Right Avg R",
|
||||
"Side Panel Right Avg G",
|
||||
"Side Panel Right Avg B"
|
||||
{
|
||||
{"Common", "P1 1"},
|
||||
{"Common", "P1 2"},
|
||||
{"Common", "P1 3"},
|
||||
{"Common", "P1 4"},
|
||||
{"Common", "P1 5"},
|
||||
{"Common", "P1 6"},
|
||||
{"Common", "P1 7"},
|
||||
{"Common", "P2 1"},
|
||||
{"Common", "P2 2"},
|
||||
{"Common", "P2 3"},
|
||||
{"Common", "P2 4"},
|
||||
{"Common", "P2 5"},
|
||||
{"Common", "P2 6"},
|
||||
{"Common", "P2 7"},
|
||||
{"Common", "P1 Start"},
|
||||
{"Common", "P2 Start"},
|
||||
{"Common", "VEFX"},
|
||||
{"Common", "Effect"},
|
||||
{"DX", "Spot Light 1"},
|
||||
{"DX", "Spot Light 2"},
|
||||
{"DX", "Spot Light 3"},
|
||||
{"DX", "Spot Light 4"},
|
||||
{"DX", "Spot Light 5"},
|
||||
{"DX", "Spot Light 6"},
|
||||
{"DX", "Spot Light 7"},
|
||||
{"DX", "Spot Light 8"},
|
||||
{"DX", "Neon Lamp"},
|
||||
{"Lightning Model", "Woofer R"},
|
||||
{"Lightning Model", "Woofer G"},
|
||||
{"Lightning Model", "Woofer B"},
|
||||
{"Lightning Model", "IC Card Reader P1 R"},
|
||||
{"Lightning Model", "IC Card Reader P1 G"},
|
||||
{"Lightning Model", "IC Card Reader P1 B"},
|
||||
{"Lightning Model", "IC Card Reader P2 R"},
|
||||
{"Lightning Model", "IC Card Reader P2 G"},
|
||||
{"Lightning Model", "IC Card Reader P2 B"},
|
||||
{"Lightning Model", "Turntable P1 R"},
|
||||
{"Lightning Model", "Turntable P1 G"},
|
||||
{"Lightning Model", "Turntable P1 B"},
|
||||
{"Lightning Model", "Turntable P2 R"},
|
||||
{"Lightning Model", "Turntable P2 G"},
|
||||
{"Lightning Model", "Turntable P2 B"},
|
||||
{"Lightning Model", "Turntable P1 Resistance"},
|
||||
{"Lightning Model", "Turntable P2 Resistance"},
|
||||
{"Lightning Model", "Stage Left Avg R"},
|
||||
{"Lightning Model", "Stage Left Avg G"},
|
||||
{"Lightning Model", "Stage Left Avg B"},
|
||||
{"Lightning Model", "Stage Right Avg R"},
|
||||
{"Lightning Model", "Stage Right Avg G"},
|
||||
{"Lightning Model", "Stage Right Avg B"},
|
||||
{"Lightning Model", "Cabinet Left Avg R"},
|
||||
{"Lightning Model", "Cabinet Left Avg G"},
|
||||
{"Lightning Model", "Cabinet Left Avg B"},
|
||||
{"Lightning Model", "Cabinet Right Avg R"},
|
||||
{"Lightning Model", "Cabinet Right Avg G"},
|
||||
{"Lightning Model", "Cabinet Right Avg B"},
|
||||
{"Lightning Model", "Control Panel Under Avg R"},
|
||||
{"Lightning Model", "Control Panel Under Avg G"},
|
||||
{"Lightning Model", "Control Panel Under Avg B"},
|
||||
{"Lightning Model", "Ceiling Left Avg R"},
|
||||
{"Lightning Model", "Ceiling Left Avg G"},
|
||||
{"Lightning Model", "Ceiling Left Avg B"},
|
||||
{"Lightning Model", "Title Left Avg R"},
|
||||
{"Lightning Model", "Title Left Avg G"},
|
||||
{"Lightning Model", "Title Left Avg B"},
|
||||
{"Lightning Model", "Title Right Avg R"},
|
||||
{"Lightning Model", "Title Right Avg G"},
|
||||
{"Lightning Model", "Title Right Avg B"},
|
||||
{"Lightning Model", "Ceiling Right Avg R"},
|
||||
{"Lightning Model", "Ceiling Right Avg G"},
|
||||
{"Lightning Model", "Ceiling Right Avg B"},
|
||||
{"Lightning Model", "Touch Panel Left Avg R"},
|
||||
{"Lightning Model", "Touch Panel Left Avg G"},
|
||||
{"Lightning Model", "Touch Panel Left Avg B"},
|
||||
{"Lightning Model", "Touch Panel Right Avg R"},
|
||||
{"Lightning Model", "Touch Panel Right Avg G"},
|
||||
{"Lightning Model", "Touch Panel Right Avg B"},
|
||||
{"Lightning Model", "Side Panel Left Inner Avg R"},
|
||||
{"Lightning Model", "Side Panel Left Inner Avg G"},
|
||||
{"Lightning Model", "Side Panel Left Inner Avg B"},
|
||||
{"Lightning Model", "Side Panel Left Outer Avg R"},
|
||||
{"Lightning Model", "Side Panel Left Outer Avg G"},
|
||||
{"Lightning Model", "Side Panel Left Outer Avg B"},
|
||||
{"Lightning Model", "Side Panel Left Avg R"},
|
||||
{"Lightning Model", "Side Panel Left Avg G"},
|
||||
{"Lightning Model", "Side Panel Left Avg B"},
|
||||
{"Lightning Model", "Side Panel Right Outer Avg R"},
|
||||
{"Lightning Model", "Side Panel Right Outer Avg G"},
|
||||
{"Lightning Model", "Side Panel Right Outer Avg B"},
|
||||
{"Lightning Model", "Side Panel Right Inner Avg R"},
|
||||
{"Lightning Model", "Side Panel Right Inner Avg G"},
|
||||
{"Lightning Model", "Side Panel Right Inner Avg B"},
|
||||
{"Lightning Model", "Side Panel Right Avg R"},
|
||||
{"Lightning Model", "Side Panel Right Avg G"},
|
||||
{"Lightning Model", "Side Panel Right Avg B"}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace games::iidx {
|
||||
protected:
|
||||
virtual ~IIDXLocalCamera() {};
|
||||
|
||||
ULONG m_nRefCount;
|
||||
LONG m_nRefCount;
|
||||
CRITICAL_SECTION m_critsec;
|
||||
|
||||
std::string m_name;
|
||||
|
||||
@@ -256,6 +256,7 @@ namespace games {
|
||||
const std::string otoca("Otoca D'or");
|
||||
games.push_back(otoca);
|
||||
buttons.insert({ otoca, otoca::get_buttons() });
|
||||
buttons_help.insert({ otoca, otoca::get_buttons_help() });
|
||||
file_hints[otoca].emplace_back("arkkep.dll");
|
||||
|
||||
// winning eleven
|
||||
@@ -324,7 +325,7 @@ namespace games {
|
||||
buttons_help.insert({ pc, pc::get_buttons_help() });
|
||||
analogs.insert({ pc, pc::get_analogs() });
|
||||
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() {
|
||||
@@ -400,7 +401,8 @@ namespace games {
|
||||
vkey_defaults.push_back(unit == 0 ? VK_NUMPAD9 : 0xFF);
|
||||
|
||||
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");
|
||||
vkey_defaults.push_back(unit == 0 ? VK_DECIMAL : 0xFF);
|
||||
|
||||
@@ -23,6 +23,11 @@ std::vector<Button> &games::otoca::get_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() {
|
||||
static std::vector<Light> lights;
|
||||
|
||||
|
||||
@@ -30,5 +30,6 @@ namespace games::otoca {
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::string get_buttons_help();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,121 @@
|
||||
#include "otoca.h"
|
||||
#include "p4io.h"
|
||||
#include "games/shared/printer.h"
|
||||
#include "util/detour.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 {
|
||||
|
||||
bool BYPASS_CAMERA = false;
|
||||
|
||||
// for dev only
|
||||
static bool CAMLIB_CALL_ORIGINAL = false;
|
||||
|
||||
OtocaGame::OtocaGame() : Game("Otoca D'or") {
|
||||
}
|
||||
|
||||
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() {
|
||||
Game::attach();
|
||||
|
||||
@@ -23,5 +128,42 @@ namespace games::otoca {
|
||||
// initialize IO hooks
|
||||
p4io_hook();
|
||||
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 {
|
||||
|
||||
extern bool BYPASS_CAMERA;
|
||||
|
||||
class OtocaGame : public games::Game {
|
||||
public:
|
||||
OtocaGame();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// QueryDisplayConfig
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "pc.h"
|
||||
|
||||
#include <format>
|
||||
@@ -32,10 +35,10 @@ namespace games::pc {
|
||||
if (pRawInputDevices &&
|
||||
(uiNumDevices > 0) &&
|
||||
(pRawInputDevices[0].hwndTarget == RI_MGR->input_hwnd)) {
|
||||
|
||||
|
||||
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
|
||||
}
|
||||
|
||||
|
||||
// otherwise, it must be the game; prevent the game from registering for raw input
|
||||
// and hijacking WM_INPUT messages.
|
||||
SetLastError(0xDEADBEEF);
|
||||
@@ -58,7 +61,7 @@ namespace games::pc {
|
||||
}
|
||||
|
||||
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
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
@@ -98,9 +101,9 @@ namespace games::pc {
|
||||
const auto user32Dll = "user32.dll";
|
||||
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
|
||||
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
|
||||
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
||||
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
||||
QueryDisplayConfig_hook, &QueryDisplayConfig_orig);
|
||||
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
||||
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
||||
EnumDisplaySettingsW_hook, &EnumDisplaySettingsW_orig);
|
||||
|
||||
if (GRAPHICS_SHOW_CURSOR) {
|
||||
|
||||
@@ -88,10 +88,10 @@ namespace games::qma {
|
||||
// create quiz directory
|
||||
static bool dirs_created = false;
|
||||
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\\quiz13");
|
||||
fileutils::dir_create_recursive("E:\\LMA\\quiz15");
|
||||
dirs_created = true;
|
||||
}
|
||||
|
||||
// return result
|
||||
|
||||
@@ -65,76 +65,78 @@ std::vector<Light> &games::sdvx::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Sound Voltex");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
GameAPI::Lights::sortLightsWithCategory(
|
||||
&lights,
|
||||
"BT-A",
|
||||
"BT-B",
|
||||
"BT-C",
|
||||
"BT-D",
|
||||
"FX-L",
|
||||
"FX-R",
|
||||
"Start",
|
||||
"Wing Left Up R",
|
||||
"Wing Left Up G",
|
||||
"Wing Left Up B",
|
||||
"Wing Right Up R",
|
||||
"Wing Right Up G",
|
||||
"Wing Right Up B",
|
||||
"Wing Left Low R",
|
||||
"Wing Left Low G",
|
||||
"Wing Left Low B",
|
||||
"Wing Right Low R",
|
||||
"Wing Right Low G",
|
||||
"Wing Right Low B",
|
||||
"Woofer R",
|
||||
"Woofer G",
|
||||
"Woofer B",
|
||||
"Controller R",
|
||||
"Controller G",
|
||||
"Controller B",
|
||||
"Generator R",
|
||||
"Generator G",
|
||||
"Generator B",
|
||||
"Pop",
|
||||
"Title Left",
|
||||
"Title Right",
|
||||
"Volume Sound",
|
||||
"Volume Headphone",
|
||||
"Volume External",
|
||||
"Volume Woofer",
|
||||
"IC Card Reader R",
|
||||
"IC Card Reader G",
|
||||
"IC Card Reader B",
|
||||
"Title Avg R",
|
||||
"Title Avg G",
|
||||
"Title Avg B",
|
||||
"Upper Left Speaker Avg R",
|
||||
"Upper Left Speaker Avg G",
|
||||
"Upper Left Speaker Avg B",
|
||||
"Upper Right Speaker Avg R",
|
||||
"Upper Right Speaker Avg G",
|
||||
"Upper Right Speaker Avg B",
|
||||
"Left Wing Avg R",
|
||||
"Left Wing Avg G",
|
||||
"Left Wing Avg B",
|
||||
"Right Wing Avg R",
|
||||
"Right Wing Avg G",
|
||||
"Right Wing Avg B",
|
||||
"Lower Left Speaker Avg R",
|
||||
"Lower Left Speaker Avg G",
|
||||
"Lower Left Speaker Avg B",
|
||||
"Lower Right Speaker Avg R",
|
||||
"Lower Right Speaker Avg G",
|
||||
"Lower Right Speaker Avg B",
|
||||
"Control Panel Avg R",
|
||||
"Control Panel Avg G",
|
||||
"Control Panel Avg B",
|
||||
"Woofer Avg R",
|
||||
"Woofer Avg G",
|
||||
"Woofer Avg B",
|
||||
"V Unit Avg R",
|
||||
"V Unit Avg G",
|
||||
"V Unit Avg B"
|
||||
{
|
||||
{"Common", "BT-A"},
|
||||
{"Common", "BT-B"},
|
||||
{"Common", "BT-C"},
|
||||
{"Common", "BT-D"},
|
||||
{"Common", "FX-L"},
|
||||
{"Common", "FX-R"},
|
||||
{"Common", "Start"},
|
||||
{"Nemsys", "Wing Left Up R"},
|
||||
{"Nemsys", "Wing Left Up G"},
|
||||
{"Nemsys", "Wing Left Up B"},
|
||||
{"Nemsys", "Wing Right Up R"},
|
||||
{"Nemsys", "Wing Right Up G"},
|
||||
{"Nemsys", "Wing Right Up B"},
|
||||
{"Nemsys", "Wing Left Low R"},
|
||||
{"Nemsys", "Wing Left Low G"},
|
||||
{"Nemsys", "Wing Left Low B"},
|
||||
{"Nemsys", "Wing Right Low R"},
|
||||
{"Nemsys", "Wing Right Low G"},
|
||||
{"Nemsys", "Wing Right Low B"},
|
||||
{"Nemsys", "Woofer R"},
|
||||
{"Nemsys", "Woofer G"},
|
||||
{"Nemsys", "Woofer B"},
|
||||
{"Nemsys", "Controller R"},
|
||||
{"Nemsys", "Controller G"},
|
||||
{"Nemsys", "Controller B"},
|
||||
{"Nemsys", "Generator R"},
|
||||
{"Nemsys", "Generator G"},
|
||||
{"Nemsys", "Generator B"},
|
||||
{"Nemsys", "Pop"},
|
||||
{"Nemsys", "Title Left"},
|
||||
{"Nemsys", "Title Right"},
|
||||
{"System", "Volume Sound"},
|
||||
{"System", "Volume Headphone"},
|
||||
{"System", "Volume External"},
|
||||
{"System", "Volume Woofer"},
|
||||
{"Valkyrie", "IC Card Reader R"},
|
||||
{"Valkyrie", "IC Card Reader G"},
|
||||
{"Valkyrie", "IC Card Reader B"},
|
||||
{"Valkyrie", "Title Avg R"},
|
||||
{"Valkyrie", "Title Avg G"},
|
||||
{"Valkyrie", "Title Avg B"},
|
||||
{"Valkyrie", "Upper Left Speaker Avg R"},
|
||||
{"Valkyrie", "Upper Left Speaker Avg G"},
|
||||
{"Valkyrie", "Upper Left Speaker Avg B"},
|
||||
{"Valkyrie", "Upper Right Speaker Avg R"},
|
||||
{"Valkyrie", "Upper Right Speaker Avg G"},
|
||||
{"Valkyrie", "Upper Right Speaker Avg B"},
|
||||
{"Valkyrie", "Left Wing Avg R"},
|
||||
{"Valkyrie", "Left Wing Avg G"},
|
||||
{"Valkyrie", "Left Wing Avg B"},
|
||||
{"Valkyrie", "Right Wing Avg R"},
|
||||
{"Valkyrie", "Right Wing Avg G"},
|
||||
{"Valkyrie", "Right Wing Avg B"},
|
||||
{"Valkyrie", "Lower Left Speaker Avg R"},
|
||||
{"Valkyrie", "Lower Left Speaker Avg G"},
|
||||
{"Valkyrie", "Lower Left Speaker Avg B"},
|
||||
{"Valkyrie", "Lower Right Speaker Avg R"},
|
||||
{"Valkyrie", "Lower Right Speaker Avg G"},
|
||||
{"Valkyrie", "Lower Right Speaker Avg B"},
|
||||
{"Valkyrie", "Control Panel Avg R"},
|
||||
{"Valkyrie", "Control Panel Avg G"},
|
||||
{"Valkyrie", "Control Panel Avg B"},
|
||||
{"Valkyrie", "Woofer Avg R"},
|
||||
{"Valkyrie", "Woofer Avg G"},
|
||||
{"Valkyrie", "Woofer Avg B"},
|
||||
{"Valkyrie", "V Unit Avg R"},
|
||||
{"Valkyrie", "V Unit Avg G"},
|
||||
{"Valkyrie", "V Unit Avg B"}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/libutils.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/nativetouchhook.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "bi2x_hook.h"
|
||||
#include "camera.h"
|
||||
#include "io.h"
|
||||
@@ -233,6 +234,27 @@ namespace games::sdvx {
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -416,9 +438,11 @@ namespace games::sdvx {
|
||||
}
|
||||
|
||||
if (is_valkyrie_model()) {
|
||||
// hook touch window
|
||||
// in windowed mode, game can accept mouse input on the second screen
|
||||
if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||
if (NATIVETOUCH) {
|
||||
nativetouchhook::hook(avs::game::DLL_INSTANCE);
|
||||
} else if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||
// hook touch window
|
||||
// in windowed mode, game can accept mouse input on the second screen
|
||||
wintouchemu::FORCE = true;
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook_title_ends(
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace games::shared {
|
||||
image_data[index + 2] = tmp;
|
||||
}
|
||||
|
||||
if (avs::game::is_model({"KLP", "KFC"})) {
|
||||
if (avs::game::is_model({"KLP", "KFC", "NCG"})) {
|
||||
// rotate image clockwise
|
||||
log_misc("printer", "rotate clockwise...");
|
||||
auto rotated = new uint8_t[image_width * image_height * 3];
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace hooks::audio {
|
||||
bool USE_DUMMY = false;
|
||||
WAVEFORMATEXTENSIBLE FORMAT {};
|
||||
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;
|
||||
|
||||
// private globals
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
@@ -20,7 +21,8 @@ namespace hooks::audio {
|
||||
extern bool USE_DUMMY;
|
||||
extern WAVEFORMATEXTENSIBLE FORMAT;
|
||||
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 LOW_LATENCY_SHARED_WASAPI;
|
||||
|
||||
|
||||
@@ -218,13 +218,40 @@ void AsioBackend::set_thread_state(AsioThreadState state) {
|
||||
bool AsioBackend::load_driver() {
|
||||
AsioDriverList asio_driver_list;
|
||||
|
||||
for (const auto &driver : asio_driver_list.driver_list) {
|
||||
log_info("audio::asio", "Driver {}", driver.id);
|
||||
log_info("audio::asio", "... Name : {}", driver.name);
|
||||
log_info("audio::asio", "... Path : {}", driver.dll_path);
|
||||
size_t driver_id = 0;
|
||||
std::string driver_name = "";
|
||||
if (hooks::audio::ASIO_DRIVER_ID.has_value()) {
|
||||
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;
|
||||
auto ret = asio_driver_list.open_driver(driver_id, reinterpret_cast<void **>(&driver));
|
||||
|
||||
@@ -278,7 +278,7 @@ static bool is_dx9_on_12_enabled() {
|
||||
}
|
||||
|
||||
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({
|
||||
"dx9on12 was force enabled by user (-dx9on12)",
|
||||
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
|
||||
@@ -945,26 +945,37 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
auto *params = &pPresentationParameters[i];
|
||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
if (!GRAPHICS_WINDOWED){
|
||||
if (i == 0) {
|
||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||
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(
|
||||
"graphics::d3d9",
|
||||
"use custom resolution {}x{} => {}x{}",
|
||||
"use custom sub 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);
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second);
|
||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
|
||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,12 +1003,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||
|
||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||
if (!GRAPHICS_WINDOWED) {
|
||||
if (i == 0) {
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1554,4 +1570,4 @@ void update_backbuffer_dimensions(D3DPRESENT_PARAMETERS *params) {
|
||||
params->BackBufferHeight);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::UpdateTexture(
|
||||
WRAP_DEBUG;
|
||||
|
||||
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
|
||||
// the reference count
|
||||
@@ -768,7 +768,7 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
pReal->ColorFill(topSurface, nullptr, D3DCOLOR_XRGB(0, 0, 0));
|
||||
cfg::SCREENRESIZE->need_surface_clean = false;
|
||||
}
|
||||
|
||||
|
||||
D3DLOCKED_RECT rect;
|
||||
HRESULT hr;
|
||||
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||
|
||||
@@ -71,6 +71,7 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
|
||||
bool SUBSCREEN_FORCE_REDRAW = 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_SUB;
|
||||
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
||||
@@ -1051,6 +1052,7 @@ bool graphics_capture_receive_jpeg(int screen, TooJpeg::WRITE_ONE_BYTE receiver,
|
||||
auto capture_width = capture.width;
|
||||
auto capture_height = capture.height;
|
||||
auto capture_timestamp = capture.timestamp;
|
||||
capture.data = nullptr;
|
||||
lock.unlock();
|
||||
|
||||
// validate data
|
||||
|
||||
@@ -37,6 +37,7 @@ extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
||||
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
|
||||
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_SUB;
|
||||
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||
|
||||
@@ -173,7 +173,7 @@ void graphics_load_windowed_parameters() {
|
||||
cfg::SCREENRESIZE->window_offset_x = result.first;
|
||||
cfg::SCREENRESIZE->window_offset_y = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -windowpos");
|
||||
log_fatal("graphics-windowed", "failed to parse -windowpos");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
GRAPHICS_WSUB_WIDTH = result.first;
|
||||
GRAPHICS_WSUB_HEIGHT = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
||||
log_fatal("graphics-windowed", "failed to parse -iidxwsubsize / -sdvxwsubsize");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
GRAPHICS_WSUB_X = result.first;
|
||||
GRAPHICS_WSUB_Y = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
||||
log_fatal("graphics-windowed", "failed to parse -iidxwsubpos / -sdvxwsubpos");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// GetLocaleInfoEx
|
||||
#define _WIN32_WINNT 0x0600
|
||||
|
||||
#include "lang.h"
|
||||
|
||||
#define WIN32_NO_STATUS
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// DisplayConfigSetDeviceInfo
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "winuser.h"
|
||||
|
||||
#include "util/detour.h"
|
||||
|
||||
@@ -559,6 +559,10 @@ int main_implementation(int argc, char *argv[]) {
|
||||
nvenc_hook::FORCE_DISABLE = true;
|
||||
}
|
||||
#endif
|
||||
if (options[launcher::Options::OtocaCamHook].value_bool()) {
|
||||
games::otoca::BYPASS_CAMERA = true;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
|
||||
attach_jb = true;
|
||||
}
|
||||
@@ -1011,18 +1015,26 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
|
||||
hooks::audio::VOLUME_HOOK_ENABLED = false;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::AudioBackend].is_active()) {
|
||||
auto &name = options[launcher::Options::AudioBackend].value_text();
|
||||
auto backend = hooks::audio::name_to_backend(name.c_str());
|
||||
if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "invalid audio backend: {}", name);
|
||||
}
|
||||
|
||||
hooks::audio::BACKEND = backend;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::AsioDriverId].is_active()) {
|
||||
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()) {
|
||||
hooks::audio::USE_DUMMY = true;
|
||||
}
|
||||
@@ -1083,8 +1095,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
||||
GRAPHICS_WINDOW_SIZE = result;
|
||||
} else {
|
||||
log_warning("launcher", "failed to parse -windowsize");
|
||||
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "failed to parse -windowsize");
|
||||
}
|
||||
}
|
||||
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
|
||||
@@ -1385,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()) {
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
||||
} else {
|
||||
log_warning("launcher", "failed to parse -forceres");
|
||||
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1867,7 +1900,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// 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";
|
||||
attach_io = true;
|
||||
attach_pc = true;
|
||||
|
||||
@@ -71,6 +71,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "*.dll",
|
||||
.category = "Path Overrides",
|
||||
// intentionally not setting a file picker here to discourage people setting this without a good reason
|
||||
},
|
||||
{
|
||||
.title = "Open Configurator",
|
||||
@@ -121,6 +122,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.setting_name = "E004010000000000",
|
||||
.category = "Network",
|
||||
.sensitive = true,
|
||||
.picker = OptionPickerType::EACard,
|
||||
},
|
||||
{
|
||||
.title = "Player 2 Card",
|
||||
@@ -130,6 +132,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.setting_name = "E004010000000000",
|
||||
.category = "Network",
|
||||
.sensitive = true,
|
||||
.picker = OptionPickerType::EACard,
|
||||
},
|
||||
{
|
||||
// Player1PinMacro
|
||||
@@ -256,9 +259,21 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.hidden = true,
|
||||
.category = "Graphics (Full Screen)"
|
||||
},
|
||||
{
|
||||
// FullscreenSubResolution
|
||||
.title = "Force FS Subscreen Resolution (EXPERIMENTAL)",
|
||||
.name = "forceressub",
|
||||
.desc =
|
||||
"Override fullscreen resolution requested by the game for second monitor, "
|
||||
"useful if you have a sub monitor that is not quite exactly what the game expects.\n\n"
|
||||
"WARNING: experimental as we have not done extensive testing to see if this causes desyncs.",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "1280,720",
|
||||
.category = "Graphics (Full Screen)"
|
||||
},
|
||||
{
|
||||
// FullscreenSubRefreshRate
|
||||
.title = "Force Submonitor Refresh Rate (EXPERIMENTAL)",
|
||||
.title = "Force FS Subscreen Refresh Rate (EXPERIMENTAL)",
|
||||
.name = "graphics-force-refresh-sub",
|
||||
.desc =
|
||||
"Override fullscreen refresh rate requested by the game for second monitor, "
|
||||
@@ -606,6 +621,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Text,
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options",
|
||||
.picker = OptionPickerType::AsioDriver,
|
||||
},
|
||||
{
|
||||
.title = "IIDX BIO2 Firmware Update",
|
||||
@@ -766,6 +782,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Text,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options (Advanced)",
|
||||
.picker = OptionPickerType::DirectoryPath,
|
||||
},
|
||||
{
|
||||
.title = "SDVX Printer Output Clear",
|
||||
@@ -814,7 +831,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
.title = "SDVX Native Touch Handling",
|
||||
.title = "SDVX FS Subscreen Native Touch Handling",
|
||||
.name = "sdvxnativetouch",
|
||||
.desc = "Disables touch hooks and lets the game access a touch screen directly. "
|
||||
"Requires a touch screen to be connected as a secondary monitor. "
|
||||
@@ -824,6 +841,18 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// spice2x_SDVXSubRedraw
|
||||
.title = "SDVX FS Subscreen Force Redraw",
|
||||
.name = "sp2x-sdvxsubredraw",
|
||||
.display_name = "sdvxsubredraw",
|
||||
.aliases= "sdvxsubredraw",
|
||||
.desc = "Check if submonitor in fullscreen mode doesn't update every frame; "
|
||||
"this option forces subscreen to redraw every frame.",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// spice2x_SDVXDigitalKnobSensitivity
|
||||
.title = "SDVX Digital Knob Sensitivity",
|
||||
@@ -860,11 +889,12 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.display_name = "sdvxasio",
|
||||
.aliases= "sdvxasio",
|
||||
.desc = "ASIO driver name to use, replacing XONAR SOUND CARD(64). "
|
||||
"String should match registry key under HLKM\\SOFTWARE\\ASIO\\ \n\n"
|
||||
"String should match registry key under HKLM\\SOFTWARE\\ASIO\\ \n\n"
|
||||
"SDVX is EXTREMELY picky about ASIO devices it can support!",
|
||||
.type = OptionType::Text,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options",
|
||||
.picker = OptionPickerType::AsioDriver,
|
||||
},
|
||||
{
|
||||
// spice2x_SDVXSubPos
|
||||
@@ -884,18 +914,6 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
{"bottomright", "for landscape"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// spice2x_SDVXSubRedraw
|
||||
.title = "SDVX Subscreen Force Redraw",
|
||||
.name = "sp2x-sdvxsubredraw",
|
||||
.display_name = "sdvxsubredraw",
|
||||
.aliases= "sdvxsubredraw",
|
||||
.desc = "Check if second monitor for subscreen doesn't update every frame; "
|
||||
"forces subscreen to redraw every frame, only needed for newer EG.",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
.title = "Force Load DDR Module",
|
||||
.name = "ddr",
|
||||
@@ -1272,6 +1290,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Sets a custom path to the modules folder.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
// intentionally not setting a folder picker here to discourage people setting this without a good reason
|
||||
},
|
||||
{
|
||||
.title = "Screenshot Folder Override",
|
||||
@@ -1279,6 +1298,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Sets a custom path to the screenshots folder.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::DirectoryPath,
|
||||
},
|
||||
{
|
||||
.title = "Configuration Path Override",
|
||||
@@ -1298,6 +1318,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"If left empty, %appdata%\\spice2x\\spicetools_screen_resize.json will be used.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::FilePath,
|
||||
.file_extension = "JSON",
|
||||
},
|
||||
{
|
||||
// PatchManagerConfigPath
|
||||
@@ -1307,6 +1329,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"If left empty, %appdata%\\spice2x\\spicetools_patch_manager.json will be used.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::FilePath,
|
||||
.file_extension = "JSON",
|
||||
},
|
||||
{
|
||||
.title = "Intel SDE",
|
||||
@@ -1314,6 +1338,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Path to Intel SDE kit path for automatic attaching.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Development",
|
||||
.picker = OptionPickerType::DirectoryPath
|
||||
},
|
||||
{
|
||||
.title = "ea3-config.xml Override",
|
||||
@@ -1321,6 +1346,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Sets a custom path to ea3-config.xml.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::FilePath,
|
||||
.file_extension = "XML",
|
||||
},
|
||||
{
|
||||
.title = "app-config.xml Override",
|
||||
@@ -1328,6 +1355,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Sets a custom path to app-config.xml.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::FilePath,
|
||||
.file_extension = "XML",
|
||||
},
|
||||
{
|
||||
.title = "avs-config.xml Override",
|
||||
@@ -1335,6 +1364,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Sets a custom path to avs-config.xml.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::FilePath,
|
||||
.file_extension = "XML",
|
||||
},
|
||||
{
|
||||
.title = "bootstrap.xml Override",
|
||||
@@ -1342,6 +1373,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Sets a custom path to bootstrap.xml.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::FilePath,
|
||||
.file_extension = "XML",
|
||||
},
|
||||
{
|
||||
.title = "log.txt Override",
|
||||
@@ -1349,6 +1382,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "Sets a custom path to log.txt.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Path Overrides",
|
||||
.picker = OptionPickerType::FilePath,
|
||||
.file_extension = "TXT",
|
||||
},
|
||||
{
|
||||
.title = "API TCP Port",
|
||||
@@ -1497,9 +1532,9 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Touch Parameters",
|
||||
},
|
||||
{
|
||||
.title = "Invert Raw Input Touch",
|
||||
.title = "Invert Touch",
|
||||
.name = "touchinvert",
|
||||
.desc = "Inverts raw touch coordinates; only works for default raw input handler, "
|
||||
.desc = "Inverts touch coordinates; only works for default raw input handler and IIDX/SDVX native touch handler, "
|
||||
"and not Windows Touch API.",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Touch Parameters",
|
||||
@@ -1658,6 +1693,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"Must provide a hexadecimal mask (e.g., 0x1ff00).",
|
||||
.type = OptionType::Hex,
|
||||
.category = "Performance",
|
||||
.picker = OptionPickerType::CpuAffinity,
|
||||
},
|
||||
{
|
||||
// spice2x_ProcessorEfficiencyClass
|
||||
@@ -1730,11 +1766,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Audio (Hacks)",
|
||||
},
|
||||
{
|
||||
.title = "Spice Audio Hook Backend",
|
||||
// AudioBackend
|
||||
.title = "Spice Audio Hook Backend (DEPRECATED - use -asioconvert instead)",
|
||||
.name = "audiobackend",
|
||||
.desc = "Selects the audio backend to use when spice audio hook is enabled, overriding exclusive WASAPI. "
|
||||
"Does nothing for games that do not output to exclusive WASAPI.",
|
||||
.type = OptionType::Enum,
|
||||
.hidden = true,
|
||||
.category = "Audio",
|
||||
.elements = {
|
||||
{"asio", "ASIO"},
|
||||
@@ -1742,12 +1780,26 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "Spice Audio Hook ASIO Driver ID",
|
||||
// AsioDriverId
|
||||
.title = "Spice Audio Hook ASIO Driver ID (DEPRECATED - use -asioconvert instead)",
|
||||
.name = "asiodriverid",
|
||||
.desc = "Selects the ASIO driver id to use when Spice Audio Backend is set to ASIO.",
|
||||
.type = OptionType::Integer,
|
||||
.hidden = true,
|
||||
.category = "Audio",
|
||||
},
|
||||
{
|
||||
// AsioDriverName
|
||||
.title = "WASAPI Exclusive to ASIO Conversion",
|
||||
.name = "asioconvert",
|
||||
.desc = "Converts WASAPI Exclusive audio output to ASIO. Value here should match registry key under HKLM\\SOFTWARE\\ASIO\\\n\n"
|
||||
"Use this if the game is configured to use WASAPI Exclusive but you want to use your ASIO driver instead.\n\n"
|
||||
"This should only be used as last resort if your audio device does not support WASAPI Exclusive.\n\n"
|
||||
"Does nothing for games that do not output to exclusive WASAPI.",
|
||||
.type = OptionType::Text,
|
||||
.category = "Audio",
|
||||
.picker = OptionPickerType::AsioDriver,
|
||||
},
|
||||
{
|
||||
.title = "WASAPI Dummy Context",
|
||||
.name = "audiodummy",
|
||||
@@ -2251,7 +2303,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// spice2x_IIDXNativeTouch
|
||||
.title = "IIDX Native Touch Handling",
|
||||
.title = "IIDX TDJ Subscreen Native Touch Handling",
|
||||
.name = "sp2x-iidxnativetouch",
|
||||
.display_name = "iidxnativetouch",
|
||||
.aliases= "iidxnativetouch",
|
||||
@@ -2580,6 +2632,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Text,
|
||||
.game_name = "LovePlus",
|
||||
.category = "Game Options (Advanced)",
|
||||
.picker = OptionPickerType::DirectoryPath,
|
||||
},
|
||||
{
|
||||
.title = "LovePlus Printer Output Clear",
|
||||
@@ -2625,6 +2678,17 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.disabled = true,
|
||||
},
|
||||
{
|
||||
// OtocaCamHook
|
||||
.title = "Otoca Camera Check Bypass",
|
||||
.name = "otocacamhook",
|
||||
.desc =
|
||||
"This game REQUIRES a (compatible) webcam to save progress. If you don't have any, check this "
|
||||
"option to bypass camera error during boot, allowing you to try out the game.",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Otoca D'or",
|
||||
.category = "Game Options",
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace launcher {
|
||||
GraphicsForceRefresh,
|
||||
FullscreenResolution,
|
||||
FullscreenOrientationFlip,
|
||||
FullscreenSubResolution,
|
||||
FullscreenSubRefreshRate,
|
||||
Graphics9On12,
|
||||
spice2x_Dx9On12,
|
||||
@@ -85,11 +86,11 @@ namespace launcher {
|
||||
SDVXPrinterJPGQuality,
|
||||
SDVXDisableCameras,
|
||||
SDVXNativeTouch,
|
||||
spice2x_SDVXSubRedraw,
|
||||
spice2x_SDVXDigitalKnobSensitivity,
|
||||
SDVXDigitalKnobSocd,
|
||||
spice2x_SDVXAsioDriver,
|
||||
spice2x_SDVXSubPos,
|
||||
spice2x_SDVXSubRedraw,
|
||||
LoadDDRModule,
|
||||
DDR43Mode,
|
||||
DDRSkipCodecRegisteration,
|
||||
@@ -186,6 +187,7 @@ namespace launcher {
|
||||
spice2x_DisableVolumeHook,
|
||||
AudioBackend,
|
||||
AsioDriverId,
|
||||
AsioDriverName,
|
||||
AudioDummy,
|
||||
DelayBy5Seconds,
|
||||
spice2x_DelayByNSeconds,
|
||||
@@ -269,6 +271,7 @@ namespace launcher {
|
||||
LovePlusPrinterOutputFormat,
|
||||
LovePlusPrinterJPGQuality,
|
||||
OptionConflictResolution,
|
||||
OtocaCamHook,
|
||||
};
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
||||
{
|
||||
log_info("signal", "SetUnhandledExceptionFilter hook hit");
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
|
||||
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, []() {
|
||||
log_info("signal", "SetUnhandledExceptionFilter hook hit (printing once)");
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
+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, int> BT5API_THREAD_RESULTS;
|
||||
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, ...) {
|
||||
|
||||
@@ -240,46 +241,77 @@ void bt5api_hook(HINSTANCE module) {
|
||||
|
||||
void bt5api_poll_reader_card(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
// check if initialized or out of bounds
|
||||
if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
||||
// get sensor state
|
||||
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
|
||||
if (sensor_state > BT5API_CARD_STATES[unit_no]) {
|
||||
uint8_t card_id[8];
|
||||
eam_io_read_card(unit_no, card_id, 8);
|
||||
eamuse_card_insert(unit_no, card_id);
|
||||
// card inserted into the back.
|
||||
if(sensor_state & (1 << EAM_IO_SENSOR_BACK)) {
|
||||
uint8_t card_id[8];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void bt5api_poll_reader_keypad(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
// check if initialized or out of bounds
|
||||
if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
if (!eam_io_poll(unit_no)) {
|
||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
||||
static_cast<int>(unit_no));
|
||||
|
||||
uint16_t keypad_current = eam_io_get_keypad_state(unit_no);
|
||||
uint16_t keypad_changes = ~BT5API_KEYPAD_STATES[unit_no] & keypad_current;
|
||||
|
||||
// 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
|
||||
eamuse_set_keypad_overrides_bt5(unit_no, eam_io_get_keypad_state(unit_no));
|
||||
// send off the state to the main engine.
|
||||
eamuse_set_keypad_overrides_bt5(unit_no, keypad_current);
|
||||
}
|
||||
|
||||
void bt5api_dispose() {
|
||||
|
||||
@@ -202,7 +202,14 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) {
|
||||
|
||||
// bt5api
|
||||
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
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// mingw otherwise doesn't load touch stuff
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "wintouchemu.h"
|
||||
#include "rawinput/touch.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
@@ -63,6 +66,11 @@ namespace nativetouchhook {
|
||||
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(
|
||||
HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||
|
||||
@@ -72,9 +80,32 @@ namespace nativetouchhook {
|
||||
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++) {
|
||||
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;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "rawinput/touch.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
|
||||
@@ -126,6 +127,11 @@ namespace wintouchemu {
|
||||
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) {
|
||||
|
||||
// check if original should be called
|
||||
@@ -220,6 +226,12 @@ namespace wintouchemu {
|
||||
touch_input->cxContact = 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) {
|
||||
|
||||
// disable further mouse inputs this call
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ImGui {
|
||||
|
||||
void HelpMarker(const char* desc) {
|
||||
ImGui::TextDisabled("(?)");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
HelpTooltip(desc);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace ImGui {
|
||||
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||||
ImGui::TextDisabled("(!)");
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
WarnTooltip(desc, warn);
|
||||
}
|
||||
}
|
||||
@@ -92,4 +92,75 @@ namespace ImGui {
|
||||
ImGui::GetColorU32(ImGuiCol_PlotHistogram),
|
||||
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
|
||||
|
||||
#include <string>
|
||||
#include "external/imgui/imgui.h"
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
constexpr ImGuiHoveredFlags TOOLTIP_FLAGS =
|
||||
(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_AllowWhenDisabled);
|
||||
|
||||
void HelpTooltip(const char* desc);
|
||||
void HelpMarker(const char* desc);
|
||||
void WarnTooltip(const char* desc, const char* warn);
|
||||
void WarnMarker(const char* desc, const char* warn);
|
||||
void DummyMarker();
|
||||
|
||||
void Knob(float fraction, float size, float thickness = 2.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 "touch/touch.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
@@ -29,6 +30,8 @@ static HWND g_hWnd = nullptr;
|
||||
static INT64 g_Time = 0;
|
||||
static INT64 g_TicksPerSecond = 0;
|
||||
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||
static double g_LastMouseMovement = 0.f;
|
||||
static bool g_MouseCursorAutoHide = false;
|
||||
|
||||
bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||
log_misc("imgui_impl_spice", "init");
|
||||
@@ -83,6 +86,12 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||
// get display size
|
||||
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 true;
|
||||
}
|
||||
@@ -283,7 +292,7 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
memset(io.MouseDown, false, sizeof(io.MouseDown));
|
||||
|
||||
// early quit if window not in focus
|
||||
if (!superexit::has_focus()) {
|
||||
if (!superexit::has_focus() || rawinput::OS_WINDOW_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -375,12 +384,31 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
}
|
||||
|
||||
// set mouse wheel
|
||||
auto mouse_diff = mouse_wheel - mouse_wheel_last;
|
||||
auto wheel_diff = mouse_wheel - mouse_wheel_last;
|
||||
mouse_wheel_last = mouse_wheel;
|
||||
io.MouseWheel = mouse_diff;
|
||||
io.MouseWheel = wheel_diff;
|
||||
|
||||
// update OS mouse position
|
||||
const auto old_mouse_pos = io.MousePos;
|
||||
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 cursor is inside the client area, always set the OS cursor to what ImGui wants
|
||||
|
||||
@@ -188,71 +188,73 @@ void overlay::SpiceOverlay::init() {
|
||||
ImGui::GetIO().FontGlobalScale = scale;
|
||||
}
|
||||
|
||||
// red theme based on:
|
||||
// https://github.com/ocornut/imgui/issues/707#issuecomment-760220280
|
||||
// r, g, b, a
|
||||
// based on CrimsonVesuvius theme from
|
||||
// https://github.com/ocornut/imgui/issues/707#issuecomment-4107169777
|
||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||
|
||||
// note: also see bg_alpha value in each overlay window
|
||||
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 1.0f);
|
||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 1.0f);
|
||||
// Text
|
||||
colors[ImGuiCol_Text] = ImVec4(1.00f, 0.90f, 0.90f, 1.00f); // Slight pinkish tint to off-white
|
||||
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.40f, 0.40f, 1.00f);
|
||||
|
||||
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
|
||||
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
// Backgrounds
|
||||
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);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.18f, 0.18f, 0.18f, 0.80f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 0.80f);
|
||||
// Borders
|
||||
colors[ImGuiCol_Border] = ImVec4(0.25f, 0.15f, 0.15f, 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);
|
||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 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);
|
||||
// Frames
|
||||
colors[ImGuiCol_FrameBg] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.25f, 0.15f, 0.15f, 1.00f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.35f, 0.20f, 0.20f, 1.00f);
|
||||
|
||||
colors[ImGuiCol_Button] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.17f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.17f, 0.00f, 0.90f);
|
||||
// Title Bars
|
||||
colors[ImGuiCol_TitleBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
||||
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);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.67f);
|
||||
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);
|
||||
// Menus
|
||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
||||
|
||||
colors[ImGuiCol_Tab] = colors[ImGuiCol_Button];
|
||||
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_ButtonHovered];
|
||||
colors[ImGuiCol_TabActive] = colors[ImGuiCol_ButtonActive];
|
||||
colors[ImGuiCol_TabUnfocused] = colors[ImGuiCol_Tab] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = colors[ImGuiCol_TabActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
||||
// Scrollbars
|
||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.25f, 0.12f, 0.12f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.35f, 0.15f, 0.15f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.45f, 0.20f, 0.20f, 1.00f);
|
||||
|
||||
colors[ImGuiCol_DockingPreview] = ImVec4(0.47f, 0.47f, 0.47f, 0.47f);
|
||||
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
||||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.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);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
// Interactables (The High-Intensity Red)
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f); // Sharp Red
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.60f, 0.12f, 0.12f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f);
|
||||
colors[ImGuiCol_Button] = ImVec4(0.30f, 0.12f, 0.12f, 1.00f);
|
||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.70f, 0.25f, 0.25f, 1.00f);
|
||||
colors[ImGuiCol_Header] = ImVec4(0.30f, 0.12f, 0.12f, 1.00f);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.70f, 0.25f, 0.25f, 1.00f);
|
||||
|
||||
// Tabs
|
||||
colors[ImGuiCol_Tab] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
||||
colors[ImGuiCol_TabHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
||||
colors[ImGuiCol_TabActive] = ImVec4(0.35f, 0.12f, 0.12f, 1.00f);
|
||||
|
||||
// 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
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
+4238
-1742
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
#include "cfg/game.h"
|
||||
@@ -7,6 +8,7 @@
|
||||
#include "rawinput/device.h"
|
||||
#include "external/imgui/imgui_filebrowser.h"
|
||||
#include "patch_manager.h"
|
||||
#include "controller_presets.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
@@ -22,9 +24,18 @@ namespace overlay::windows {
|
||||
CONFIG_TAB_OPTIONS,
|
||||
CONFIG_TAB_ADVANCED,
|
||||
CONFIG_TAB_DEV,
|
||||
CONFIG_TAB_PRESETS,
|
||||
CONFIG_TAB_SEARCH,
|
||||
};
|
||||
|
||||
struct MatchEntry {
|
||||
std::string game_name;
|
||||
std::string device_name;
|
||||
int light_index;
|
||||
int control_index;
|
||||
bool soft;
|
||||
};
|
||||
|
||||
class Config : public Window {
|
||||
private:
|
||||
|
||||
@@ -38,7 +49,6 @@ namespace overlay::windows {
|
||||
ConfigTab tab_selected = ConfigTab::CONFIG_TAB_INVALID;
|
||||
|
||||
// buttons tab
|
||||
int buttons_page = 0;
|
||||
bool buttons_keyboard_state[0xFF];
|
||||
bool buttons_bind_active = false;
|
||||
bool buttons_many_active = false;
|
||||
@@ -55,11 +65,28 @@ namespace overlay::windows {
|
||||
int analogs_devices_control_selected = -1;
|
||||
|
||||
// lights tab
|
||||
int lights_page = 0;
|
||||
std::vector<rawinput::Device *> lights_devices;
|
||||
int lights_devices_selected = -1;
|
||||
int lights_devices_control_selected = -1;
|
||||
|
||||
// lights test all
|
||||
bool lights_testing = false;
|
||||
int lights_test_current = -1;
|
||||
std::chrono::steady_clock::time_point lights_test_time;
|
||||
|
||||
// lights auto match
|
||||
std::vector<rawinput::Device *> auto_match_devices;
|
||||
int auto_match_device_selected = -1;
|
||||
bool auto_match_testing = false;
|
||||
int auto_match_test_current = -1;
|
||||
std::chrono::steady_clock::time_point auto_match_test_time;
|
||||
std::string auto_match_test_device;
|
||||
unsigned int auto_match_test_control = 0;
|
||||
std::chrono::steady_clock::time_point auto_match_copy_time;
|
||||
bool auto_match_copied = false;
|
||||
bool auto_match_soft_enabled = true;
|
||||
bool auto_match_p2 = false;
|
||||
|
||||
// keypads tab
|
||||
int keypads_selected[2] {};
|
||||
char keypads_card_path[2][1024] {};
|
||||
@@ -68,6 +95,32 @@ namespace overlay::windows {
|
||||
ImGui::FileBrowser keypads_card_select_browser[2];
|
||||
char keypads_card_number[2][18] {};
|
||||
|
||||
// presets tab
|
||||
std::vector<ControllerTemplate> templates_cache;
|
||||
bool templates_cache_dirty = true;
|
||||
int templates_selected = -1;
|
||||
char template_save_name[256] = {};
|
||||
// per-source target mapping: source index -> selected target device index
|
||||
std::vector<int> template_target_selection;
|
||||
// deferred popup flags (to avoid ImGui ID stack mismatch inside tables)
|
||||
bool template_apply_open = false;
|
||||
bool template_edit_open = false;
|
||||
bool template_save_labels_open = false;
|
||||
bool template_delete_open = false;
|
||||
ControllerTemplate template_pending_save;
|
||||
std::vector<std::string> template_save_sources;
|
||||
std::vector<std::string> template_save_labels;
|
||||
bool apply_buttons = true;
|
||||
bool apply_keypads = true;
|
||||
bool apply_analogs = true;
|
||||
bool apply_lights = true;
|
||||
bool save_buttons = true;
|
||||
bool save_keypads = true;
|
||||
bool save_analogs = true;
|
||||
bool save_lights = true;
|
||||
bool all_cleared = false;
|
||||
std::vector<bool> template_is_applied;
|
||||
|
||||
// patches tab
|
||||
std::unique_ptr<PatchManager> patch_manager;
|
||||
|
||||
@@ -78,6 +131,70 @@ namespace overlay::windows {
|
||||
std::string search_filter = "";
|
||||
std::string search_filter_in_lower_case = "";
|
||||
|
||||
std::filesystem::path file_picker_path;
|
||||
std::thread *file_picker_thread = nullptr;
|
||||
bool file_picker_done = false;
|
||||
|
||||
void build_buttons(const std::string &name, std::vector<Button> *buttons, int min = 0, int max = -1);
|
||||
void build_button(
|
||||
const std::string &name,
|
||||
Button &primary_button,
|
||||
Button *button,
|
||||
const int button_it,
|
||||
const int button_it_max,
|
||||
const int alt_index);
|
||||
|
||||
void bind_button_popup(const std::string &bind_name, Button *button, const int button_it_max, const int alt_index);
|
||||
void naive_button_popup(const std::string &naive_string, Button *button, const int button_it_max, const int alt_index);
|
||||
void edit_button_popup(
|
||||
const std::string &edit_name,
|
||||
const std::string &button_display,
|
||||
Button *button,
|
||||
const float button_velocity,
|
||||
const int alt_index);
|
||||
void clear_button(Button *button, const int alt_index, std::optional<unsigned short> vKey_default = std::nullopt);
|
||||
void reset_button_to_default(Button *button, unsigned short vKey_default);
|
||||
unsigned int get_keypad_top_row(const Button &button);
|
||||
|
||||
void build_analogs(const std::string &name, std::vector<Analog> *analogs);
|
||||
void edit_analog_popup(Analog &analog);
|
||||
|
||||
void update() override;
|
||||
void stop_lights_test();
|
||||
void build_lights(const std::string &name, std::vector<Light> *lights);
|
||||
void build_light(Light &primary_light, Light *light, const int light_index, const int alt_index);
|
||||
void clear_light(Light *light, const int alt_index);
|
||||
void edit_light_popup(Light &primary_light, Light *light, const int alt_index);
|
||||
void auto_match_lights_popup(std::vector<Light> *lights);
|
||||
|
||||
std::string match_lights(
|
||||
rawinput::Device *device,
|
||||
std::vector<Light> *lights,
|
||||
std::vector<std::string> &raw_names,
|
||||
std::vector<MatchEntry> &matched,
|
||||
std::vector<MatchEntry> &unmatched);
|
||||
|
||||
void build_presets();
|
||||
void clear_all_bindings();
|
||||
void apply_template_source(const ControllerTemplate &tmpl,
|
||||
const std::string &source_filter,
|
||||
const std::string &target_device);
|
||||
ControllerTemplate capture_current_bindings(const std::string &name);
|
||||
|
||||
void build_cards();
|
||||
std::string build_option_value_picker_title(const OptionDefinition& option);
|
||||
void build_option_value_picker(Option& option);
|
||||
void build_options(
|
||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||
void build_about();
|
||||
void build_launcher();
|
||||
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
|
||||
|
||||
void build_menu(int *game_selected);
|
||||
void shutdown_system(bool force, bool reboot_instead);
|
||||
|
||||
void set_alternating_row_colors(const int row_index);
|
||||
|
||||
public:
|
||||
Config(SpiceOverlay *overlay);
|
||||
~Config() override;
|
||||
@@ -85,19 +202,6 @@ namespace overlay::windows {
|
||||
void read_card(int player = -1);
|
||||
void write_card(int player);
|
||||
void build_content() override;
|
||||
void build_buttons(const std::string &name, std::vector<Button> *buttons,
|
||||
int min = 0, int max = -1);
|
||||
void build_analogs(const std::string &name, std::vector<Analog> *analogs);
|
||||
void build_lights(const std::string &name, std::vector<Light> *lights);
|
||||
void build_cards();
|
||||
void build_options(
|
||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||
void build_about();
|
||||
void build_launcher();
|
||||
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
|
||||
|
||||
static void build_page_selector(int *page);
|
||||
void build_menu(int *game_selected);
|
||||
void shutdown_system(bool force, bool reboot_instead);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
#include "controller_presets.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "cfg/button.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
// helpers - iterate both buttons and keypad_buttons
|
||||
static void count_buttons_in(const std::vector<TemplateButtonBinding> &btns, int &naive, int &device) {
|
||||
for (auto &btn : btns) {
|
||||
if (btn.primary.is_naive()) naive++;
|
||||
if (btn.primary.is_device()) device++;
|
||||
for (auto &alt : btn.alternatives) {
|
||||
if (alt.is_naive()) naive++;
|
||||
if (alt.is_device()) device++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ControllerTemplate::count_naive_buttons() const {
|
||||
int naive = 0, device = 0;
|
||||
count_buttons_in(buttons, naive, device);
|
||||
count_buttons_in(keypad_buttons, naive, device);
|
||||
return naive;
|
||||
}
|
||||
|
||||
int ControllerTemplate::count_device_buttons() const {
|
||||
int naive = 0, device = 0;
|
||||
count_buttons_in(buttons, naive, device);
|
||||
count_buttons_in(keypad_buttons, naive, device);
|
||||
return device;
|
||||
}
|
||||
|
||||
int ControllerTemplate::count_device_analogs() const {
|
||||
int count = 0;
|
||||
for (auto &a : analogs) {
|
||||
if (a.is_device()) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int ControllerTemplate::count_device_lights() const {
|
||||
int count = 0;
|
||||
for (auto &l : lights) {
|
||||
if (l.primary.is_device()) count++;
|
||||
for (auto &alt : l.alternatives) {
|
||||
if (alt.is_device()) count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void collect_devices_from(const std::vector<TemplateButtonBinding> &btns,
|
||||
std::set<std::string> &devices) {
|
||||
for (auto &btn : btns) {
|
||||
if (btn.primary.is_device()) devices.insert(btn.primary.device_identifier);
|
||||
for (auto &alt : btn.alternatives) {
|
||||
if (alt.is_device()) devices.insert(alt.device_identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::string> ControllerTemplate::get_used_devices() const {
|
||||
std::set<std::string> devices;
|
||||
collect_devices_from(buttons, devices);
|
||||
collect_devices_from(keypad_buttons, devices);
|
||||
for (auto &a : analogs) {
|
||||
if (a.is_device()) devices.insert(a.device_identifier);
|
||||
}
|
||||
for (auto &l : lights) {
|
||||
if (l.primary.is_device()) devices.insert(l.primary.device_identifier);
|
||||
for (auto &alt : l.alternatives) {
|
||||
if (alt.is_device()) devices.insert(alt.device_identifier);
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
static void collect_sources_from(const std::vector<TemplateButtonBinding> &btns,
|
||||
std::set<std::string> &sources_set, bool &has_naive) {
|
||||
for (auto &btn : btns) {
|
||||
if (btn.primary.is_naive()) has_naive = true;
|
||||
if (btn.primary.is_device()) sources_set.insert(btn.primary.device_identifier);
|
||||
for (auto &alt : btn.alternatives) {
|
||||
if (alt.is_naive()) has_naive = true;
|
||||
if (alt.is_device()) sources_set.insert(alt.device_identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ControllerTemplate::get_binding_sources() const {
|
||||
std::set<std::string> sources_set;
|
||||
bool has_naive = false;
|
||||
|
||||
collect_sources_from(buttons, sources_set, has_naive);
|
||||
collect_sources_from(keypad_buttons, sources_set, has_naive);
|
||||
for (auto &a : analogs) {
|
||||
if (a.is_device()) {
|
||||
sources_set.insert(a.device_identifier);
|
||||
}
|
||||
}
|
||||
for (auto &l : lights) {
|
||||
if (l.primary.is_device()) sources_set.insert(l.primary.device_identifier);
|
||||
for (auto &alt : l.alternatives) {
|
||||
if (alt.is_device()) sources_set.insert(alt.device_identifier);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
if (has_naive) {
|
||||
result.emplace_back("Naive");
|
||||
}
|
||||
for (auto &s : sources_set) {
|
||||
result.push_back(s);
|
||||
}
|
||||
|
||||
// sort alphabetically
|
||||
std::sort(result.begin(), result.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ControllerTemplate::rename_source(const std::string &old_id, const std::string &new_id) {
|
||||
auto rename_in_buttons = [&](std::vector<TemplateButtonBinding> &btns) {
|
||||
for (auto &btn : btns) {
|
||||
if (btn.primary.device_identifier == old_id) btn.primary.device_identifier = new_id;
|
||||
for (auto &alt : btn.alternatives) {
|
||||
if (alt.device_identifier == old_id) alt.device_identifier = new_id;
|
||||
}
|
||||
}
|
||||
};
|
||||
rename_in_buttons(buttons);
|
||||
rename_in_buttons(keypad_buttons);
|
||||
for (auto &a : analogs) {
|
||||
if (a.device_identifier == old_id) a.device_identifier = new_id;
|
||||
}
|
||||
for (auto &l : lights) {
|
||||
if (l.primary.device_identifier == old_id) l.primary.device_identifier = new_id;
|
||||
for (auto &alt : l.alternatives) {
|
||||
if (alt.device_identifier == old_id) alt.device_identifier = new_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ControllerTemplate::get_source_summary(const std::string &source) const {
|
||||
bool is_naive = (source == "Naive");
|
||||
std::string result;
|
||||
|
||||
// collect button lines from a button vector
|
||||
auto collect_btn_lines = [&](const std::vector<TemplateButtonBinding> &btns,
|
||||
std::vector<std::string> &lines) {
|
||||
for (auto &tb : btns) {
|
||||
auto check_entry = [&](const TemplateButtonEntry &e) {
|
||||
if (is_naive && e.is_naive()) {
|
||||
Button b("");
|
||||
b.setVKey(e.vKey);
|
||||
lines.push_back(tb.name + " = " + (e.vKey == INVALID_VKEY ? "None" : b.getVKeyString()));
|
||||
} else if (!is_naive && e.is_device() && e.device_identifier == source) {
|
||||
std::string desc = std::to_string(e.vKey);
|
||||
if (e.analog_type != BAT_NONE) {
|
||||
desc += " (analog:" + std::to_string((int)e.analog_type) + ")";
|
||||
}
|
||||
lines.push_back(tb.name + " = " + desc);
|
||||
}
|
||||
};
|
||||
check_entry(tb.primary);
|
||||
for (auto &alt : tb.alternatives) {
|
||||
check_entry(alt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// buttons
|
||||
std::vector<std::string> btn_lines;
|
||||
collect_btn_lines(buttons, btn_lines);
|
||||
if (!btn_lines.empty()) {
|
||||
result += "Buttons:\n";
|
||||
for (auto &line : btn_lines) {
|
||||
result += " " + line + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// keypad buttons
|
||||
std::vector<std::string> kp_lines;
|
||||
collect_btn_lines(keypad_buttons, kp_lines);
|
||||
if (!kp_lines.empty()) {
|
||||
result += "Keypad:\n";
|
||||
for (auto &line : kp_lines) {
|
||||
result += " " + line + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// analogs
|
||||
if (!is_naive) {
|
||||
std::vector<std::string> analog_lines;
|
||||
for (auto &a : analogs) {
|
||||
if (a.is_device() && a.device_identifier == source) {
|
||||
analog_lines.push_back(a.name + " (idx:" + std::to_string(a.index) + ")");
|
||||
}
|
||||
}
|
||||
if (!analog_lines.empty()) {
|
||||
result += "Analogs:\n";
|
||||
for (auto &line : analog_lines) {
|
||||
result += " " + line + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lights
|
||||
if (!is_naive) {
|
||||
std::vector<std::string> light_lines;
|
||||
for (auto &l : lights) {
|
||||
auto check_light = [&](const TemplateLightEntry &e) {
|
||||
if (e.is_device() && e.device_identifier == source) {
|
||||
light_lines.push_back(l.name + " (idx:" + std::to_string(e.index) + ")");
|
||||
}
|
||||
};
|
||||
check_light(l.primary);
|
||||
for (auto &alt : l.alternatives) {
|
||||
check_light(alt);
|
||||
}
|
||||
}
|
||||
if (!light_lines.empty()) {
|
||||
result += "Lights:\n";
|
||||
for (auto &line : light_lines) {
|
||||
result += " " + line + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.empty()) {
|
||||
result = "(no bindings)";
|
||||
}
|
||||
// trim trailing newline
|
||||
while (!result.empty() && result.back() == '\n') {
|
||||
result.pop_back();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cfg/analog.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
// single button binding entry (primary or alternative)
|
||||
struct TemplateButtonEntry {
|
||||
unsigned short vKey = INVALID_VKEY;
|
||||
ButtonAnalogType analog_type = BAT_NONE;
|
||||
std::string device_identifier;
|
||||
bool invert = false;
|
||||
double debounce_up = 0.0;
|
||||
double debounce_down = 0.0;
|
||||
unsigned short velocity_threshold = 0;
|
||||
|
||||
bool is_naive() const { return device_identifier.empty() && vKey != INVALID_VKEY; }
|
||||
bool is_device() const { return !device_identifier.empty(); }
|
||||
bool is_unbound() const { return device_identifier.empty() && vKey == INVALID_VKEY; }
|
||||
|
||||
static TemplateButtonEntry FromButton(const Button &btn) {
|
||||
TemplateButtonEntry e;
|
||||
e.vKey = btn.getVKey();
|
||||
e.analog_type = btn.getAnalogType();
|
||||
e.device_identifier = btn.getDeviceIdentifier();
|
||||
e.invert = btn.getInvert();
|
||||
e.debounce_up = btn.getDebounceUp();
|
||||
e.debounce_down = btn.getDebounceDown();
|
||||
e.velocity_threshold = btn.getVelocityThreshold();
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
// button binding with primary + alternatives
|
||||
struct TemplateButtonBinding {
|
||||
std::string name;
|
||||
TemplateButtonEntry primary;
|
||||
std::vector<TemplateButtonEntry> alternatives;
|
||||
|
||||
TemplateButtonBinding() = default;
|
||||
|
||||
TemplateButtonBinding(std::string name, TemplateButtonEntry primary,
|
||||
std::vector<TemplateButtonEntry> alternatives)
|
||||
: name(std::move(name)), primary(std::move(primary)),
|
||||
alternatives(std::move(alternatives)) {}
|
||||
|
||||
explicit TemplateButtonBinding(Button &btn) {
|
||||
name = btn.getName();
|
||||
primary = TemplateButtonEntry::FromButton(btn);
|
||||
for (auto &alt : btn.getAlternatives()) {
|
||||
alternatives.push_back(TemplateButtonEntry::FromButton(alt));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// analog binding
|
||||
struct TemplateAnalogBinding {
|
||||
std::string name;
|
||||
std::string device_identifier;
|
||||
unsigned short index = 0xFF;
|
||||
float sensitivity = 1.f;
|
||||
float deadzone = 0.f;
|
||||
bool deadzone_mirror = false;
|
||||
bool invert = false;
|
||||
bool smoothing = false;
|
||||
int multiplier = 1;
|
||||
bool relative_mode = false;
|
||||
int delay_buffer_depth = 0;
|
||||
|
||||
bool is_device() const { return !device_identifier.empty(); }
|
||||
bool is_unbound() const { return device_identifier.empty() && index == 0xFF; }
|
||||
|
||||
TemplateAnalogBinding() = default;
|
||||
|
||||
explicit TemplateAnalogBinding(Analog &a) {
|
||||
name = a.getName();
|
||||
device_identifier = a.getDeviceIdentifier();
|
||||
index = a.getIndex();
|
||||
sensitivity = a.getSensitivity();
|
||||
deadzone = a.getDeadzone();
|
||||
deadzone_mirror = a.getDeadzoneMirror();
|
||||
invert = a.getInvert();
|
||||
smoothing = a.getSmoothing();
|
||||
multiplier = a.getMultiplier();
|
||||
relative_mode = a.isRelativeMode();
|
||||
delay_buffer_depth = a.getDelayBufferDepth();
|
||||
}
|
||||
};
|
||||
|
||||
// single light binding entry (primary or alternative)
|
||||
struct TemplateLightEntry {
|
||||
std::string device_identifier;
|
||||
unsigned int index = 0;
|
||||
|
||||
bool is_device() const { return !device_identifier.empty(); }
|
||||
bool is_unbound() const { return device_identifier.empty(); }
|
||||
};
|
||||
|
||||
// light binding with primary + alternatives
|
||||
struct TemplateLightBinding {
|
||||
std::string name;
|
||||
TemplateLightEntry primary;
|
||||
std::vector<TemplateLightEntry> alternatives;
|
||||
|
||||
TemplateLightBinding() = default;
|
||||
|
||||
TemplateLightBinding(std::string name, TemplateLightEntry primary,
|
||||
std::vector<TemplateLightEntry> alternatives)
|
||||
: name(std::move(name)), primary(std::move(primary)),
|
||||
alternatives(std::move(alternatives)) {}
|
||||
};
|
||||
|
||||
// controller preset
|
||||
struct ControllerTemplate {
|
||||
std::string name;
|
||||
std::string game_name;
|
||||
bool is_builtin = false;
|
||||
std::vector<TemplateButtonBinding> buttons;
|
||||
std::vector<TemplateButtonBinding> keypad_buttons;
|
||||
std::vector<TemplateAnalogBinding> analogs;
|
||||
std::vector<TemplateLightBinding> lights;
|
||||
|
||||
// helpers
|
||||
int count_naive_buttons() const;
|
||||
int count_device_buttons() const;
|
||||
int count_device_analogs() const;
|
||||
int count_device_lights() const;
|
||||
std::set<std::string> get_used_devices() const;
|
||||
|
||||
// get all binding sources (device_identifier strings used in this preset)
|
||||
std::vector<std::string> get_binding_sources() const;
|
||||
|
||||
// get summary of bindings for a specific source (for tooltip display)
|
||||
std::string get_source_summary(const std::string &source) const;
|
||||
|
||||
// replace all occurrences of a source device_identifier with a new one
|
||||
void rename_source(const std::string &old_id, const std::string &new_id);
|
||||
};
|
||||
|
||||
// template management API
|
||||
std::vector<ControllerTemplate> get_templates_for_game(const std::string &game_name);
|
||||
std::vector<ControllerTemplate> load_user_templates();
|
||||
bool save_user_template(
|
||||
const ControllerTemplate &tmpl,
|
||||
const bool save_buttons,
|
||||
const bool save_keypads,
|
||||
const bool save_analogs,
|
||||
const bool save_lights);
|
||||
|
||||
bool delete_user_template(const std::string &game_name, const std::string &template_name);
|
||||
bool rename_user_template(const std::string &game_name,
|
||||
const std::string &old_name, const std::string &new_name);
|
||||
}
|
||||
@@ -85,10 +85,12 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
||||
void GenericSubScreen::check_for_errors() {}
|
||||
|
||||
void GenericSubScreen::build_content() {
|
||||
this->flags |= ImGuiWindowFlags_NoBackground;
|
||||
|
||||
check_for_errors();
|
||||
if (this->disabled_message.has_value()) {
|
||||
this->draws_window = true;
|
||||
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace overlay::windows {
|
||||
|
||||
protected:
|
||||
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out);
|
||||
virtual void check_for_errors();
|
||||
ImVec2 overlay_content_top_left;
|
||||
ImVec2 overlay_content_size;
|
||||
std::optional<std::string> disabled_message = std::nullopt;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace overlay::windows {
|
||||
|
||||
static const size_t TICKER_SIZE = 9;
|
||||
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
|
||||
static const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
||||
static const ImVec4 RED(1.f, 0.f, 0.f, 1.f);
|
||||
static const float PADDING_Y = 8.f;
|
||||
static const float PADDING_X = 4.f;
|
||||
@@ -91,6 +92,23 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void IIDXSegmentDisplay::build_content() {
|
||||
|
||||
// explicitly check if TDJ I/O is enabled to account for the fact that I/O emulation may be
|
||||
// disabled
|
||||
if (games::iidx::CURRENT_IO_EMULATION_STATE == games::iidx::iidx_aio_emulation_state::bi2x_hook) {
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::TextColored(
|
||||
YELLOW,
|
||||
"%s",
|
||||
"Invalid I/O mode; DLL is configured for TDJ I/O!\n"
|
||||
"Enable -iidxtdj if you want TDJ subscreen overlay.");
|
||||
ImGui::TextUnformatted("");
|
||||
if (ImGui::Button("Close")) {
|
||||
this->set_active(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char input_ticker[TICKER_SIZE];
|
||||
|
||||
// get ticker content from game
|
||||
|
||||
@@ -12,11 +12,9 @@ namespace overlay::windows {
|
||||
this->title = "IIDX TDJ Subscreen";
|
||||
|
||||
if (GRAPHICS_IIDX_WSUB) {
|
||||
this->disabled_message =
|
||||
"Close this overlay and use the second window.\n"
|
||||
"If you don't see the window, double check your DLL type and apply TDJ I/O patches as needed.";
|
||||
this->disabled_message = "Close this overlay and use the second window. (try ALT+TAB)";
|
||||
} else if (games::iidx::IIDX_TDJ_MONITOR_WARNING) {
|
||||
this->disabled_message = "TDJ mode subscreen overlay is not compatible with -monitor option";
|
||||
this->disabled_message = "TDJ mode subscreen overlay is not compatible with -monitor option.";
|
||||
}
|
||||
|
||||
float size = 0.5f;
|
||||
@@ -44,6 +42,18 @@ namespace overlay::windows {
|
||||
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2));
|
||||
}
|
||||
|
||||
void IIDXSubScreen::check_for_errors() {
|
||||
if (games::iidx::CURRENT_IO_EMULATION_STATE ==
|
||||
games::iidx::iidx_aio_emulation_state::bi2a_com2) {
|
||||
|
||||
// explicitly check if LDJ I/O is enabled (as opposed to checking if NOT TDJ I/O)
|
||||
// to account for the fact that I/O emulation may be disabled
|
||||
this->disabled_message =
|
||||
"LDJ I/O detected; TDJ mode subscreen overlay requires TDJ I/O.\n"
|
||||
"Ensure you apply applicable TDJ I/O patches, or run with TDJ DLL.";
|
||||
}
|
||||
}
|
||||
|
||||
void IIDXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||
if (!this->get_active()) {
|
||||
return;
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace overlay::windows {
|
||||
|
||||
protected:
|
||||
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||
void check_for_errors() override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace overlay::windows {
|
||||
{
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("+", tall, this->test_button, this->service_button);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip("SERVICE + TEST");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "cfg/button.h"
|
||||
#include "cfg/light.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace overlay::windows {
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
|
||||
this->build_button("+", tiny_size, this->help[p], this->start[p], nullptr);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip("HELP + START");
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace overlay::windows {
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
this->build_button("?", tiny_size, this->help[p], nullptr, this->help_light[p]);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip("HELP");
|
||||
}
|
||||
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace overlay::windows {
|
||||
"Lightning Model cabinets (TDJ) do not have any keypads; they use the subscreen.\n\n"
|
||||
"Fullscreen mode: bind a key in Overlay tab, and press it in game to show the subscreen, "
|
||||
"then use your mouse to click. Page Up button is the default binding.\n\n"
|
||||
"Windowed mode: look for the second window in the taskbar.\n\n"
|
||||
"Windowed mode: look for the second window in the taskbar (or ALT+TAB).\n\n"
|
||||
"Windowed mode with -iidxnosub: bring up the subscreen overlay (default Page Up).\n\n"
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
struct LightMatchMap {
|
||||
const char *controller; // controller name for display
|
||||
const char *game; // game name to match
|
||||
unsigned short vid; // USB vendor ID filter, or 0 to skip
|
||||
unsigned short pid; // USB product ID filter, or 0 to skip
|
||||
const char *device_light; // light name (or prefix if rgb)
|
||||
const char *device_suffix; // after R/G/B for rgb mode, or ""
|
||||
const char *address; // control index for address mode, or ""
|
||||
bool rgb; // expand " R/G/B" between device_light and device_suffix
|
||||
const char *game_light; // target game light (or prefix if rgb), without P1/P2 prefix
|
||||
const char *game_light_alt; // alt game light for Valkyrie mode, or ""
|
||||
};
|
||||
|
||||
static const LightMatchMap LIGHT_MATCH_MAP[] = {
|
||||
|
||||
// FAUCETWO - Sound Voltex (RGB)
|
||||
{"FAUCETWO", "Sound Voltex", 0, 0, "Top LED", " 6/24", "", true, "Wing Left Up", "Upper Left Speaker Avg"},
|
||||
{"FAUCETWO", "Sound Voltex", 0, 0, "Top LED", " 12/24", "", true, "Wing Left Low", "Lower Left Speaker Avg"},
|
||||
{"FAUCETWO", "Sound Voltex", 0, 0, "Top LED", " 18/24", "", true, "Wing Right Low", "Lower Right Speaker Avg"},
|
||||
{"FAUCETWO", "Sound Voltex", 0, 0, "Top LED", " 24/24", "", true, "Wing Right Up", "Upper Right Speaker Avg"},
|
||||
{"FAUCETWO", "Sound Voltex", 0, 0, "Left and Right", "", "", true, "Controller", "Control Panel Avg"},
|
||||
|
||||
// FAUCETWO Portable - Sound Voltex (RGB)
|
||||
{"FAUCETWO Portable", "Sound Voltex", 0, 0, "Light of Left Side", "", "", true, "Wing Left Up", "Left Wing Avg"},
|
||||
{"FAUCETWO Portable", "Sound Voltex", 0, 0, "Light of Right Side", "", "", true, "Wing Right Up", "Right Wing Avg"},
|
||||
{"FAUCETWO Portable", "Sound Voltex", 0, 0, "Light of Center1", "", "", true, "Controller", "Control Panel Avg"},
|
||||
{"FAUCETWO Portable", "Sound Voltex", 0, 0, "Light of Center2", "", "", true, "Woofer", "Woofer Avg"},
|
||||
|
||||
// PHOENIXWAN - Beatmania IIDX (address based, VID:PID 034C:0368)
|
||||
{"PHOENIXWAN", "Beatmania IIDX", 0x034C, 0x0368, "Generic Indicator", "", "0x00", false, "1", ""},
|
||||
{"PHOENIXWAN", "Beatmania IIDX", 0x034C, 0x0368, "Generic Indicator", "", "0x01", false, "2", ""},
|
||||
{"PHOENIXWAN", "Beatmania IIDX", 0x034C, 0x0368, "Generic Indicator", "", "0x02", false, "3", ""},
|
||||
{"PHOENIXWAN", "Beatmania IIDX", 0x034C, 0x0368, "Generic Indicator", "", "0x03", false, "4", ""},
|
||||
{"PHOENIXWAN", "Beatmania IIDX", 0x034C, 0x0368, "Generic Indicator", "", "0x04", false, "5", ""},
|
||||
{"PHOENIXWAN", "Beatmania IIDX", 0x034C, 0x0368, "Generic Indicator", "", "0x05", false, "6", ""},
|
||||
{"PHOENIXWAN", "Beatmania IIDX", 0x034C, 0x0368, "Generic Indicator", "", "0x06", false, "7", ""},
|
||||
|
||||
// Nostroller VID_0E8F&PID_1212&MI_03 (Lights 01-14) (RGB)
|
||||
// The lights are named "LED_01R" "LED_01G" "LED_01B" and so on.
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_01", "", "", true, "Key 1", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_02", "", "", true, "Key 2", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_03", "", "", true, "Key 3", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_04", "", "", true, "Key 4", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_05", "", "", true, "Key 5", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_06", "", "", true, "Key 6", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_07", "", "", true, "Key 7", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_08", "", "", true, "Key 8", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_09", "", "", true, "Key 9", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_10", "", "", true, "Key 10", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_11", "", "", true, "Key 11", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_12", "", "", true, "Key 12", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_13", "", "", true, "Key 13", ""},
|
||||
{"Nostroller (Lights 01-14)", "Nostalgia", 0, 0, "LED_14", "", "", true, "Key 14", ""},
|
||||
|
||||
// Nostroller VID_0E8F&PID_1212&MI_02 (Lights 15-28) (RGB)
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_15", "", "", true, "Key 15", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_16", "", "", true, "Key 16", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_17", "", "", true, "Key 17", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_18", "", "", true, "Key 18", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_19", "", "", true, "Key 19", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_20", "", "", true, "Key 20", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_21", "", "", true, "Key 21", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_22", "", "", true, "Key 22", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_23", "", "", true, "Key 23", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_24", "", "", true, "Key 24", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_25", "", "", true, "Key 25", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_26", "", "", true, "Key 26", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_27", "", "", true, "Key 27", ""},
|
||||
{"Nostroller (Lights 15-28)", "Nostalgia", 0, 0, "LED_28", "", "", true, "Key 28", ""},
|
||||
|
||||
};
|
||||
|
||||
static const int LIGHT_MATCH_MAP_COUNT =
|
||||
sizeof(LIGHT_MATCH_MAP) / sizeof(LIGHT_MATCH_MAP[0]);
|
||||
|
||||
}
|
||||
@@ -150,9 +150,17 @@ namespace overlay::windows {
|
||||
const auto gle = GetLastError();
|
||||
log_warning("patchmanager", "WinHttpSendRequest failed: {}", gle);
|
||||
url_fetch_errors += fmt::format("WinHttpSendRequest failed: {}\n", gle);
|
||||
if (gle == 12175) {
|
||||
url_fetch_errors += "\nThis is ERROR_WINHTTP_SECURE_FAILURE - most likely TLS 1.1 / TLS 1.2 error on old OS versions.\n\n";
|
||||
if (gle == ERROR_WINHTTP_SECURE_FAILURE) {
|
||||
url_fetch_errors += "\nERROR_WINHTTP_SECURE_FAILURE\n\n";
|
||||
url_fetch_errors += "Most likely TLS 1.1 / TLS 1.2 error on old OS versions.\n\n";
|
||||
url_fetch_errors += "Look up MSDN article on 'Update to enable TLS 1.1 and TLS 1.2 as default secure protocols in WinHTTP in Windows' for a fix.\n";
|
||||
} else if (gle == ERROR_WINHTTP_NAME_NOT_RESOLVED) {
|
||||
url_fetch_errors += "\nERROR_WINHTTP_NAME_NOT_RESOLVED\n\n";
|
||||
url_fetch_errors += "The server name could not be resolved.\n\n";
|
||||
url_fetch_errors += "Website is offline, or your internet connection is down.";
|
||||
} else if (gle == ERROR_WINHTTP_TIMEOUT) {
|
||||
url_fetch_errors += "\nERROR_WINHTTP_TIMEOUT\n\n";
|
||||
url_fetch_errors += "The request timed out.";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -235,9 +243,9 @@ namespace overlay::windows {
|
||||
#ifdef _MSC_VER
|
||||
static_cast<PLDR_DLL_NOTIFICATION_FUNCTION>([]
|
||||
#else
|
||||
[] CALLBACK
|
||||
[]
|
||||
#endif
|
||||
(ULONG reason, PCLDR_DLL_NOTIFICATION_DATA data, PVOID ctx) {
|
||||
(ULONG reason, PCLDR_DLL_NOTIFICATION_DATA data, PVOID ctx) CALLBACK {
|
||||
if (reason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
||||
auto const dll = strtolower(std::filesystem::path({
|
||||
data->Loaded.FullDllName->Buffer,
|
||||
@@ -432,7 +440,7 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool url_entered = false;
|
||||
bool is_valid_url = false;
|
||||
bool patches_imported = false;
|
||||
@@ -459,7 +467,7 @@ namespace overlay::windows {
|
||||
&patch_url,
|
||||
ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_AutoSelectAll);
|
||||
|
||||
if (ImGui::Button("Paste")) {
|
||||
if (ImGui::Button("Paste from clipboard")) {
|
||||
auto clipboard_url = clipboard::paste_text();
|
||||
if (!clipboard_url.empty()) {
|
||||
strreplace(clipboard_url, "\r\n", "");
|
||||
@@ -534,7 +542,9 @@ namespace overlay::windows {
|
||||
}
|
||||
this->config_save();
|
||||
}
|
||||
reload_local_patches();
|
||||
// import_remote_patches_to_disk clears out old patches, so regardless of result,
|
||||
// reload patches from disk
|
||||
reload_local_patches(true);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
@@ -635,7 +645,7 @@ namespace overlay::windows {
|
||||
const auto search_str_in_lower = strtolower(patch_name_filter);
|
||||
size_t patches_shown = 0;
|
||||
for (auto &patch : patches) {
|
||||
|
||||
|
||||
// get patch status
|
||||
PatchStatus patch_status = is_patch_active(patch);
|
||||
patch.last_status = patch_status;
|
||||
@@ -721,7 +731,7 @@ namespace overlay::windows {
|
||||
if (style_color_pushed) {
|
||||
ImGui::PopStyleColor(style_color_pushed);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
|
||||
@@ -762,7 +772,7 @@ namespace overlay::windows {
|
||||
patch.last_status = is_patch_active(patch);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
|
||||
@@ -791,7 +801,7 @@ namespace overlay::windows {
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
} else if (patch.type == PatchType::Integer) {
|
||||
@@ -807,7 +817,7 @@ namespace overlay::windows {
|
||||
apply_patch(patch, true);
|
||||
config_dirty = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
}
|
||||
@@ -824,7 +834,7 @@ namespace overlay::windows {
|
||||
ImGui::InputInt("##dummy_int_input", &patch.patch_number.value);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
}
|
||||
@@ -836,7 +846,7 @@ namespace overlay::windows {
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
|
||||
if (patches_shown == 0) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
@@ -981,7 +991,7 @@ namespace overlay::windows {
|
||||
"patchmanager",
|
||||
"matched auto apply entry by full game identifier: {}",
|
||||
entry_id);
|
||||
|
||||
|
||||
} else if (is_game_id_wildcard_matched(entry_id)) {
|
||||
// match on model and ext, ignoring dest/spec/rev
|
||||
// sample: LDJ:J:E:A:2025011400
|
||||
@@ -1208,7 +1218,7 @@ namespace overlay::windows {
|
||||
identifier,
|
||||
time);
|
||||
}
|
||||
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@@ -2000,7 +2010,7 @@ namespace overlay::windows {
|
||||
};
|
||||
|
||||
// get data offset
|
||||
memory_patch_data.data_offset =
|
||||
memory_patch_data.data_offset =
|
||||
parse_json_data_offset(patch_data.name, memory_patch);
|
||||
if (memory_patch_data.data_offset == 0) {
|
||||
continue;
|
||||
@@ -2155,7 +2165,7 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
// get offset
|
||||
uint64_t union_offset =
|
||||
uint64_t union_offset =
|
||||
parse_json_data_offset(patch_data.name, union_patch_it->value);
|
||||
if (union_offset == 0) {
|
||||
continue;
|
||||
@@ -2193,7 +2203,7 @@ namespace overlay::windows {
|
||||
// move to list
|
||||
patch_data.patches_union.emplace_back(union_patch_data);
|
||||
}
|
||||
|
||||
|
||||
if (setting_union_patches_enabled.contains(patch_data.hash)) {
|
||||
patch_data.selected_union_name = setting_union_patches_enabled[patch_data.hash];
|
||||
}
|
||||
@@ -2742,7 +2752,7 @@ namespace overlay::windows {
|
||||
union_patch.data_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (union_patch.data_offset_ptr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace overlay::windows {
|
||||
~ScreenResize() override;
|
||||
|
||||
void build_content() override;
|
||||
void update();
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
size_t toggle_screen_resize = ~0u;
|
||||
bool toggle_screen_resize_state = false;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace overlay::windows {
|
||||
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
|
||||
} else {
|
||||
this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead.";
|
||||
this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead. (ALT+TAB)";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -120,9 +121,15 @@ namespace rawinput {
|
||||
std::vector<std::string> value_caps_names;
|
||||
std::vector<HIDP_VALUE_CAPS> value_output_caps_list;
|
||||
std::vector<std::string> value_output_caps_names;
|
||||
|
||||
std::vector<std::vector<bool>> button_states;
|
||||
std::vector<std::vector<double>> button_up, button_down;
|
||||
std::vector<std::vector<bool>> button_output_states;
|
||||
|
||||
// key: usage page, link collection
|
||||
// value: number of buttons for that key (combine ranges and nonranges)
|
||||
std::map<std::pair<USAGE, ULONG>, ULONG> button_usage_pages;
|
||||
|
||||
std::vector<float> value_states;
|
||||
std::vector<LONG> value_states_raw;
|
||||
std::vector<float> value_output_states;
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace rawinput {
|
||||
uint32_t MIDI_NOTE_SUSTAIN = 20;
|
||||
|
||||
static MidiNoteAlgorithm MIDI_NOTE_ALGORITHM = MidiNoteAlgorithm::V2;
|
||||
|
||||
// the price we pay for making spice overlay consume from raw input
|
||||
// making focus detection a nightmare
|
||||
bool OS_WINDOW_ACTIVE = false;
|
||||
}
|
||||
|
||||
rawinput::MidiNoteAlgorithm rawinput::get_midi_algorithm() {
|
||||
@@ -401,6 +405,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
std::vector<std::string> button_caps_names;
|
||||
std::vector<std::vector<bool>> button_states;
|
||||
std::vector<std::vector<double>> button_up, button_down;
|
||||
std::map<std::pair<USAGE, ULONG>, ULONG> button_usage_pages;
|
||||
for (int button_cap_num = 0; button_cap_num < button_cap_length; button_cap_num++) {
|
||||
auto &button_caps = button_cap_data[button_cap_num];
|
||||
|
||||
@@ -432,6 +437,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
button_states.emplace_back(std::vector<bool>(static_cast<unsigned int>(button_count), false));
|
||||
button_up.emplace_back(std::vector<double>(static_cast<unsigned int>(button_count), 0.0));
|
||||
button_down.emplace_back(std::vector<double>(static_cast<unsigned int>(button_count), 0.0));
|
||||
button_usage_pages[std::make_pair(button_caps.UsagePage, button_caps.LinkCollection)] += button_count;
|
||||
|
||||
// names
|
||||
for (USAGE usg = button_caps.Range.UsageMin; usg <= button_caps.Range.UsageMax; usg++) {
|
||||
@@ -753,6 +759,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
new_device.hidInfo->button_up = std::move(button_up);
|
||||
new_device.hidInfo->button_down = std::move(button_down);
|
||||
new_device.hidInfo->button_output_states = std::move(button_output_states);
|
||||
new_device.hidInfo->button_usage_pages = std::move(button_usage_pages);
|
||||
new_device.hidInfo->value_states = std::move(value_states);
|
||||
new_device.hidInfo->value_states_raw = std::move(value_states_raw);
|
||||
new_device.hidInfo->value_output_states = std::move(value_output_states);
|
||||
@@ -1709,53 +1716,81 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
||||
// get HID data
|
||||
auto &data_hid = data->data.hid;
|
||||
|
||||
// buttons
|
||||
for (size_t cap_num = 0; cap_num < device.hidInfo->button_caps_list.size(); cap_num++) {
|
||||
auto &button_caps = device.hidInfo->button_caps_list[cap_num];
|
||||
auto &button_states = device.hidInfo->button_states[cap_num];
|
||||
auto &button_down = device.hidInfo->button_down[cap_num];
|
||||
auto &button_up = device.hidInfo->button_up[cap_num];
|
||||
// parse reports
|
||||
for (const auto &pair : device.hidInfo->button_usage_pages) {
|
||||
const auto usage_page = pair.first.first;
|
||||
const auto link_collection = pair.first.second;
|
||||
const auto button_count = pair.second;
|
||||
|
||||
// get button count
|
||||
int button_count = button_caps.Range.UsageMax - button_caps.Range.UsageMin + 1;
|
||||
if (button_count <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get usages
|
||||
auto usages_length = static_cast<ULONG>(button_count);
|
||||
ULONG usages_length = button_count;
|
||||
std::vector<USAGE> usages(static_cast<size_t>(usages_length));
|
||||
if (HidP_GetUsages(
|
||||
HidP_Input,
|
||||
button_caps.UsagePage,
|
||||
button_caps.LinkCollection,
|
||||
usage_page,
|
||||
link_collection,
|
||||
usages.data(),
|
||||
&usages_length,
|
||||
reinterpret_cast<PHIDP_PREPARSED_DATA>(device.hidInfo->preparsed_data.get()),
|
||||
reinterpret_cast<PCHAR>(data_hid.bRawData),
|
||||
data_hid.dwSizeHid) != HIDP_STATUS_SUCCESS) {
|
||||
|
||||
// log_warning(
|
||||
// "rawinput",
|
||||
// "failed to get usages for device {}, usage page {:x} and link collection {:x}",
|
||||
// device.desc,
|
||||
// usage_page, link_collection);
|
||||
continue;
|
||||
}
|
||||
|
||||
// update buttons
|
||||
std::vector<bool> new_states(button_count);
|
||||
for (ULONG usage_num = 0; usage_num < usages_length; usage_num++) {
|
||||
USAGE usage = usages[usage_num] - button_caps.Range.UsageMin;
|
||||
// log_info(
|
||||
// "rawinput",
|
||||
// "processing HID input for device {}, usage page {:x} and link collection {:x} with {} buttons, got {} reports",
|
||||
// device.desc,
|
||||
// usage_page, link_collection, button_count, usages_length);
|
||||
|
||||
// guard against some buggy device sending an event for a usage below `UsageMin`
|
||||
if (usage < button_count) {
|
||||
new_states[usage] = true;
|
||||
// buttons
|
||||
for (size_t cap_num = 0; cap_num < device.hidInfo->button_caps_list.size(); cap_num++) {
|
||||
auto &button_caps = device.hidInfo->button_caps_list[cap_num];
|
||||
auto &button_states = device.hidInfo->button_states[cap_num];
|
||||
auto &button_down = device.hidInfo->button_down[cap_num];
|
||||
auto &button_up = device.hidInfo->button_up[cap_num];
|
||||
|
||||
// is this the right usage page and link collection?
|
||||
if (button_caps.UsagePage != usage_page || button_caps.LinkCollection != link_collection) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (int button_num = 0; button_num < button_count; button_num++) {
|
||||
if (!new_states[button_num] && button_states[button_num]) {
|
||||
device.updated = true;
|
||||
button_states[button_num] = new_states[button_num];
|
||||
button_down[button_num] = input_time;
|
||||
} else if (new_states[button_num] && !button_states[button_num]) {
|
||||
device.updated = true;
|
||||
button_states[button_num] = new_states[button_num];
|
||||
button_up[button_num] = input_time;
|
||||
|
||||
// get button count
|
||||
int button_count = button_caps.Range.UsageMax - button_caps.Range.UsageMin + 1;
|
||||
if (button_count <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// update buttons
|
||||
std::vector<bool> new_states(button_count);
|
||||
for (ULONG usage_num = 0; usage_num < usages_length; usage_num++) {
|
||||
if (usages[usage_num] < button_caps.Range.UsageMin ||
|
||||
usages[usage_num] > button_caps.Range.UsageMax) {
|
||||
continue;
|
||||
}
|
||||
|
||||
USAGE usage = usages[usage_num] - button_caps.Range.UsageMin;
|
||||
|
||||
// guard against some buggy device sending an event for a usage below `UsageMin`
|
||||
if (usage < button_count) {
|
||||
new_states[usage] = true;
|
||||
}
|
||||
}
|
||||
for (int button_num = 0; button_num < button_count; button_num++) {
|
||||
if (!new_states[button_num] && button_states[button_num]) {
|
||||
device.updated = true;
|
||||
button_states[button_num] = new_states[button_num];
|
||||
button_down[button_num] = input_time;
|
||||
} else if (new_states[button_num] && !button_states[button_num]) {
|
||||
device.updated = true;
|
||||
button_states[button_num] = new_states[button_num];
|
||||
button_up[button_num] = input_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "device.h"
|
||||
#include "hotplug.h"
|
||||
#include "util/scope_guard.h"
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
@@ -31,6 +32,15 @@ namespace rawinput {
|
||||
extern bool NAIVE_REQUIRE_FOCUS;
|
||||
extern bool RAWINPUT_REQUIRE_FOCUS;
|
||||
|
||||
// while active, prevents overlay from accepting any input
|
||||
// can be used while OS modal dialog is shown on top of overlay/spicecfg
|
||||
// always prefer RAII set_os_window_focus_guard instead of the global bool
|
||||
extern bool OS_WINDOW_ACTIVE;
|
||||
inline scope_guard set_os_window_focus_guard() {
|
||||
rawinput::OS_WINDOW_ACTIVE = true;
|
||||
return scope_guard {[]() { rawinput::OS_WINDOW_ACTIVE = false; }};
|
||||
}
|
||||
|
||||
struct DeviceCallback {
|
||||
void *data;
|
||||
std::function<void(void*, Device*)> f;
|
||||
|
||||
@@ -26,10 +26,10 @@ namespace rawinput::touch {
|
||||
bool INVERTED = false;
|
||||
|
||||
// state
|
||||
static bool DISPLAY_INITIALIZED = false;
|
||||
static DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
|
||||
static long DISPLAY_SIZE_X = 1920L;
|
||||
static long DISPLAY_SIZE_Y = 1080L;
|
||||
DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
|
||||
long DISPLAY_SIZE_X = 1920L;
|
||||
long DISPLAY_SIZE_Y = 1080L;
|
||||
bool DISPLAY_INITIALIZED = false;
|
||||
|
||||
bool is_touchscreen(Device *device) {
|
||||
|
||||
|
||||
@@ -9,6 +9,12 @@ namespace rawinput::touch {
|
||||
extern bool DISABLED;
|
||||
extern bool INVERTED;
|
||||
|
||||
// global state
|
||||
extern DWORD DISPLAY_ORIENTATION;
|
||||
extern long DISPLAY_SIZE_X;
|
||||
extern long DISPLAY_SIZE_Y;
|
||||
extern bool DISPLAY_INITIALIZED;
|
||||
|
||||
bool is_touchscreen(Device *device);
|
||||
void enable(Device *device);
|
||||
void disable(Device *device);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// GetCurrentProcessorNumberEx
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "cpuutils.h"
|
||||
|
||||
#include <thread>
|
||||
@@ -98,7 +101,7 @@ namespace cpuutils {
|
||||
log_misc("cpuutils", "detected {} processors", PROCESSOR_COUNT);
|
||||
|
||||
done = true;
|
||||
|
||||
|
||||
// init processor states
|
||||
get_load();
|
||||
}
|
||||
@@ -144,11 +147,11 @@ namespace cpuutils {
|
||||
// dump cpu id
|
||||
log_misc("cpuinfo", "{}, {} ({})",
|
||||
cpu.vendor,
|
||||
cpu.brand_string,
|
||||
cpu.brand_string,
|
||||
GetX86MicroarchitectureName(GetX86Microarchitecture(&cpu)));
|
||||
log_misc("cpuinfo", "family 0x{:x}, model 0x{:x}, stepping 0x{:x}", cpu.family, cpu.model, cpu.stepping);
|
||||
|
||||
// dump features
|
||||
// dump features
|
||||
std::string features = "";
|
||||
for (size_t i = 0; i < X86_LAST_; ++i) {
|
||||
if (GetX86FeaturesEnumValue(&cpu.features, static_cast<X86FeaturesEnum>(i))) {
|
||||
@@ -253,7 +256,7 @@ namespace cpuutils {
|
||||
if (GetLogicalProcessorInformationEx == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// determine buffer size
|
||||
returned_length = 0;
|
||||
result = GetLogicalProcessorInformationEx(
|
||||
|
||||
@@ -56,8 +56,8 @@ public:
|
||||
|
||||
template<class T, class... Args>
|
||||
auto add(T&& func, Args&&... args)
|
||||
-> std::future<typename std::result_of<T(Args...)>::type> {
|
||||
using ret_t = typename std::result_of<T(Args...)>::type;
|
||||
-> std::future<typename std::invoke_result<T, Args...>::type> {
|
||||
using ret_t = typename std::invoke_result<T, Args...>::type;
|
||||
auto task = std::make_shared<std::packaged_task<ret_t()>>(
|
||||
std::bind(std::forward<T>(func), std::forward<Args>(args)...));
|
||||
std::future<ret_t> fut = task->get_future();
|
||||
|
||||
Reference in New Issue
Block a user