mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 336500c8ae | |||
| ee3fa58bfa | |||
| a4c3eddc2f | |||
| e0f6cb2cbd | |||
| 1ad6a9412e | |||
| 5af3e0d494 | |||
| b1ee828457 | |||
| 0d60649715 | |||
| 220b27159b | |||
| b06a0a7d47 | |||
| d66af8e16b | |||
| ad366ecdd0 | |||
| 22cb4d689c | |||
| b38ea463aa | |||
| 056d1b2d90 | |||
| 37693916d9 | |||
| 889e8b43eb | |||
| 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 |
@@ -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")
|
||||
|
||||
@@ -260,6 +266,7 @@ set_source_files_properties(cfg/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/icon.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/Win32D.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(build/manifest64.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(stubs/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
|
||||
# sources
|
||||
#########
|
||||
@@ -346,6 +353,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 +567,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
|
||||
@@ -805,7 +814,8 @@ endif()
|
||||
|
||||
# nvcuda.dll
|
||||
set(SOURCE_FILES stubs/nvcuda.cpp)
|
||||
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} stubs/nvcuda.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuda.def)
|
||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
|
||||
|
||||
@@ -815,7 +825,8 @@ endif()
|
||||
|
||||
# nvcuvid.dll
|
||||
set(SOURCE_FILES stubs/nvcuvid.cpp)
|
||||
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} stubs/nvcuvid.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuvid.def)
|
||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
|
||||
|
||||
@@ -825,7 +836,8 @@ endif()
|
||||
|
||||
# nvEncodeAPI64.dll
|
||||
set(SOURCE_FILES stubs/nvEncodeAPI64.cpp)
|
||||
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} stubs/nvEncodeAPI64.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvEncodeAPI64.def)
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
|
||||
|
||||
@@ -835,7 +847,8 @@ endif()
|
||||
|
||||
# cpusbxpkm.dll (32 bit)
|
||||
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
|
||||
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} stubs/cpusbxpkm.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/cpusbxpkm.def)
|
||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-3
@@ -217,7 +217,23 @@ namespace avs {
|
||||
ea3_config_name = "prop/eamuse-config.xml";
|
||||
}
|
||||
|
||||
log_info("avs-ea3", "booting (using {})", ea3_config_name);
|
||||
if (avs::core::file_exists(ea3_config_name)) {
|
||||
log_info("avs-ea3", "booting (using {})", ea3_config_name);
|
||||
} else {
|
||||
log_warning("avs-ea3", "looked for the following files in order:");
|
||||
log_warning("avs-ea3", " * prop/ea3-config.xml");
|
||||
log_warning("avs-ea3", " * prop/ea3-cfg.xml");
|
||||
if (avs::game::DLL_NAME == "beatstream1.dll") {
|
||||
log_warning("avs-ea3", " * prop/ea3-config-1.xml");
|
||||
}
|
||||
if (avs::game::DLL_NAME == "beatstream2.dll") {
|
||||
log_warning("avs-ea3", " * prop/ea3-config-2.xml");
|
||||
}
|
||||
log_warning("avs-ea3", " * prop/eamuse-config.xml");
|
||||
log_warning("avs-ea3", "none of the files above were found, game will fail to boot");
|
||||
log_warning("avs-ea3", "usually, this means your game data is incomplete");
|
||||
log_fatal("avs-ea3", "no ea3 config file found in prop directory; check log messages");
|
||||
}
|
||||
|
||||
// remember new config path
|
||||
CFG_PATH = ea3_config_name;
|
||||
@@ -554,9 +570,17 @@ namespace avs {
|
||||
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
||||
|
||||
// call the game init
|
||||
log_info("avs-ea3", "calling entry init (init code = {})", init_code_str);
|
||||
log_info("avs-ea3", "calling dll_entry_init (init code = {})", init_code_str);
|
||||
if (!avs::game::entry_init(init_code_str.data(), app_param)) {
|
||||
log_fatal("avs-ea3", "entry init failed :(");
|
||||
for (size_t i = 0; i < 255; i++) {
|
||||
log_warning("avs-ea3", "dll_entry_init failed :(");
|
||||
}
|
||||
deferredlogs::defer_error_messages({
|
||||
"dll_entry_init failed",
|
||||
" init code: " + init_code_str,
|
||||
" this is a fatal error that causes boot failure",
|
||||
" most likely, your prop files have errors"
|
||||
});
|
||||
}
|
||||
|
||||
// accommodate changes to soft id code
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+44
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,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"
|
||||
|
||||
+19
-17
@@ -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:
|
||||
@@ -183,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:
|
||||
@@ -278,6 +278,8 @@ std::string Button::getVKeyString() {
|
||||
return "Left Alt";
|
||||
case 0xA5:
|
||||
return "Right Alt";
|
||||
case INVALID_VKEY:
|
||||
return "Invalid";
|
||||
default:
|
||||
|
||||
// check win API
|
||||
|
||||
@@ -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,6 +49,10 @@ 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;
|
||||
@@ -51,10 +63,10 @@ private:
|
||||
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;
|
||||
@@ -71,12 +83,20 @@ public:
|
||||
if (this->override_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// has a device
|
||||
if (!this->device_identifier.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// naive, and has a valid vKey
|
||||
if (this->vKey != INVALID_VKEY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto &alternative : this->alternatives) {
|
||||
if (alternative.vKey != INVALID_VKEY) {
|
||||
if (!alternative.getDeviceIdentifier().empty() ||
|
||||
alternative.getVKey() != INVALID_VKEY) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -117,6 +137,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;
|
||||
}
|
||||
|
||||
@@ -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,6 +11,7 @@ class Light {
|
||||
private:
|
||||
std::vector<Light> alternatives;
|
||||
std::string lightName;
|
||||
std::string lightCategory;
|
||||
std::string deviceIdentifier = "";
|
||||
unsigned int index = 0;
|
||||
bool is_temporary = false;
|
||||
@@ -23,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);
|
||||
|
||||
@@ -51,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;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,16 @@ enum class OptionType {
|
||||
Hex,
|
||||
};
|
||||
|
||||
enum class OptionPickerType {
|
||||
None,
|
||||
AsioDriver,
|
||||
EACard,
|
||||
CpuAffinity,
|
||||
FilePath,
|
||||
DirectoryPath,
|
||||
Monitor,
|
||||
};
|
||||
|
||||
struct OptionDefinition {
|
||||
std::string title;
|
||||
// unique identifier used for flag matching but also stored in config files
|
||||
@@ -32,6 +42,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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2023 Omar Cornut
|
||||
Copyright (c) 2014-2026 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+645
-269
@@ -1,16 +1,37 @@
|
||||
// dear imgui: Renderer for DirectX9
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
// dear imgui: Renderer Backend for DirectX9
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
|
||||
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
|
||||
// 2026-03-19: Fixed issue in ImGui_ImplDX9_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310)
|
||||
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
|
||||
// 2025-06-11: DirectX9: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
|
||||
// 2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
||||
// 2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575)
|
||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-06-25: DirectX9: Explicitly disable texture state stages after >= 1.
|
||||
// 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states.
|
||||
// 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857).
|
||||
// 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file.
|
||||
// 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer.
|
||||
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
|
||||
@@ -22,160 +43,154 @@
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
|
||||
#include "imgui_impl_dx9.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// DirectX
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_dx9.h"
|
||||
|
||||
// allow std::min use
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
|
||||
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
|
||||
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
|
||||
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
|
||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
||||
// DirectX
|
||||
#include <d3d9.h>
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
struct ImGui_ImplDX9_Data
|
||||
{
|
||||
LPDIRECT3DDEVICE9 pd3dDevice;
|
||||
LPDIRECT3DVERTEXBUFFER9 pVB;
|
||||
LPDIRECT3DINDEXBUFFER9 pIB;
|
||||
int VertexBufferSize;
|
||||
int IndexBufferSize;
|
||||
bool HasRgbaSupport;
|
||||
|
||||
ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
|
||||
};
|
||||
|
||||
struct CUSTOMVERTEX
|
||||
{
|
||||
float pos[3];
|
||||
D3DCOLOR col;
|
||||
float uv[2];
|
||||
};
|
||||
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
|
||||
|
||||
// Render function.
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
||||
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
|
||||
#define IMGUI_COL_TO_DX9_ARGB(_COL) (_COL)
|
||||
#else
|
||||
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
|
||||
#endif
|
||||
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||
}
|
||||
|
||||
// Create and grow buffers if needed
|
||||
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) {
|
||||
if (g_pVB) {
|
||||
g_pVB->Release();
|
||||
g_pVB = NULL;
|
||||
}
|
||||
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(ImDrawVert),
|
||||
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX,
|
||||
D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
|
||||
return;
|
||||
}
|
||||
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) {
|
||||
if (g_pIB) {
|
||||
g_pIB->Release();
|
||||
g_pIB = NULL;
|
||||
}
|
||||
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx),
|
||||
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY,
|
||||
sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32,
|
||||
D3DPOOL_DEFAULT, &g_pIB, NULL) < 0)
|
||||
return;
|
||||
}
|
||||
// Forward Declarations
|
||||
static void ImGui_ImplDX9_InitMultiViewportSupport();
|
||||
static void ImGui_ImplDX9_ShutdownMultiViewportSupport();
|
||||
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
|
||||
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
|
||||
|
||||
// Backup the DX9 state
|
||||
IDirect3DStateBlock9 *d3d9_state_block = NULL;
|
||||
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
|
||||
return;
|
||||
struct render_state_t {
|
||||
IDirect3DSurface9 *back_buffer;
|
||||
IDirect3DSurface9 *depth_stencil;
|
||||
IDirect3DSurface9 *render_targets[8];
|
||||
D3DCAPS9 caps;
|
||||
};
|
||||
|
||||
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
|
||||
D3DMATRIX last_world, last_view, last_projection;
|
||||
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
|
||||
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
|
||||
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// Copy all vertices into a single contiguous buffer
|
||||
ImDrawVert *vtx_dst;
|
||||
ImDrawIdx *idx_dst;
|
||||
if (g_pVB->Lock(0, (UINT) (draw_data->TotalVtxCount * sizeof(ImDrawVert)), (void **) &vtx_dst,
|
||||
D3DLOCK_DISCARD) < 0)
|
||||
return;
|
||||
if (g_pIB->Lock(0, (UINT) (draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void **) &idx_dst,
|
||||
D3DLOCK_DISCARD) < 0)
|
||||
return;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++) {
|
||||
const ImDrawList *cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
g_pVB->Unlock();
|
||||
g_pIB->Unlock();
|
||||
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(ImDrawVert));
|
||||
g_pd3dDevice->SetIndices(g_pIB);
|
||||
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
|
||||
// Functions
|
||||
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data, struct render_state_t *render_state)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
|
||||
// Setup viewport
|
||||
D3DVIEWPORT9 vp;
|
||||
vp.X = vp.Y = 0;
|
||||
vp.Width = (DWORD) draw_data->DisplaySize.x;
|
||||
vp.Height = (DWORD) draw_data->DisplaySize.y;
|
||||
vp.Width = (DWORD)draw_data->DisplaySize.x;
|
||||
vp.Height = (DWORD)draw_data->DisplaySize.y;
|
||||
vp.MinZ = 0.0f;
|
||||
vp.MaxZ = 1.0f;
|
||||
g_pd3dDevice->SetViewport(&vp);
|
||||
|
||||
g_pd3dDevice->SetPixelShader(nullptr);
|
||||
g_pd3dDevice->SetVertexShader(nullptr);
|
||||
LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
|
||||
device->SetViewport(&vp);
|
||||
|
||||
D3DCAPS9 caps {};
|
||||
if (FAILED(g_pd3dDevice->GetDeviceCaps(&caps))) {
|
||||
caps.NumSimultaneousRTs = 0UL;
|
||||
}
|
||||
// spice ldj
|
||||
{
|
||||
if (FAILED(device->GetDeviceCaps(&render_state->caps))) {
|
||||
render_state->caps.NumSimultaneousRTs = 0UL;
|
||||
}
|
||||
|
||||
IDirect3DSurface9 *back_buffer = nullptr;
|
||||
IDirect3DSurface9 *depth_stencil = nullptr;
|
||||
IDirect3DSurface9 *render_targets[8];
|
||||
// save all previous render target state
|
||||
for (size_t target = 0; target < std::min(8UL, render_state->caps.NumSimultaneousRTs); target++) {
|
||||
if (FAILED(device->GetRenderTarget(target, &render_state->render_targets[target]))) {
|
||||
render_state->render_targets[target] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// save all previous render target state
|
||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
||||
if (FAILED(g_pd3dDevice->GetRenderTarget(target, &render_targets[target]))) {
|
||||
render_targets[target] = nullptr;
|
||||
// get the previous depth stencil
|
||||
if (FAILED(device->GetDepthStencilSurface(&render_state->depth_stencil))) {
|
||||
render_state->depth_stencil = nullptr;
|
||||
}
|
||||
|
||||
// set the back buffer as the current render target
|
||||
if (SUCCEEDED(device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &render_state->back_buffer))) {
|
||||
device->SetRenderTarget(0, render_state->back_buffer);
|
||||
device->SetDepthStencilSurface(nullptr);
|
||||
|
||||
for (size_t target = 1; target < std::min(8UL, render_state->caps.NumSimultaneousRTs); target++) {
|
||||
device->SetRenderTarget(target, nullptr);
|
||||
}
|
||||
} else {
|
||||
render_state->back_buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// get the previous depth stencil
|
||||
if (FAILED(g_pd3dDevice->GetDepthStencilSurface(&depth_stencil))) {
|
||||
depth_stencil = nullptr;
|
||||
}
|
||||
|
||||
// set the back buffer as the current render target
|
||||
if (SUCCEEDED(g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer))) {
|
||||
g_pd3dDevice->SetRenderTarget(0, back_buffer);
|
||||
g_pd3dDevice->SetDepthStencilSurface(nullptr);
|
||||
|
||||
for (size_t target = 1; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
||||
g_pd3dDevice->SetRenderTarget(target, nullptr);
|
||||
}
|
||||
} else {
|
||||
back_buffer = nullptr;
|
||||
}
|
||||
|
||||
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
|
||||
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||||
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling.
|
||||
device->SetPixelShader(nullptr);
|
||||
device->SetVertexShader(nullptr);
|
||||
device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
|
||||
device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
||||
device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
device->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
||||
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
|
||||
device->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
|
||||
device->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
|
||||
device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
|
||||
device->SetRenderState(D3DRS_FOGENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_CLIPPING, TRUE);
|
||||
device->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
|
||||
device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
|
||||
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
||||
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||||
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
|
||||
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
|
||||
|
||||
// Setup orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
@@ -185,176 +200,537 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
|
||||
float T = draw_data->DisplayPos.y + 0.5f;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
|
||||
D3DMATRIX mat_identity = {{{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } };
|
||||
D3DMATRIX mat_projection =
|
||||
{{{
|
||||
2.0f / (R - L), 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f / (T - B), 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.5f, 0.0f,
|
||||
(L + R) / (L - R), (T + B) / (B - T), 0.5f, 1.0f
|
||||
}}};
|
||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
|
||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
|
||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
||||
{ { {
|
||||
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f/(T-B), 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.5f, 0.0f,
|
||||
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
|
||||
} } };
|
||||
device->SetTransform(D3DTS_WORLD, &mat_identity);
|
||||
device->SetTransform(D3DTS_VIEW, &mat_identity);
|
||||
device->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
||||
}
|
||||
}
|
||||
|
||||
// Render function.
|
||||
void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplDX9_UpdateTexture(tex);
|
||||
|
||||
// Create and grow buffers if needed
|
||||
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
if (device->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0)
|
||||
return;
|
||||
}
|
||||
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
if (device->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Backup the DX9 state
|
||||
IDirect3DStateBlock9* state_block = nullptr;
|
||||
if (device->CreateStateBlock(D3DSBT_ALL, &state_block) < 0)
|
||||
return;
|
||||
if (state_block->Capture() < 0)
|
||||
{
|
||||
state_block->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
|
||||
D3DMATRIX last_world, last_view, last_projection;
|
||||
device->GetTransform(D3DTS_WORLD, &last_world);
|
||||
device->GetTransform(D3DTS_VIEW, &last_view);
|
||||
device->GetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// Allocate buffers
|
||||
CUSTOMVERTEX* vtx_dst;
|
||||
ImDrawIdx* idx_dst;
|
||||
if (bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
|
||||
{
|
||||
state_block->Release();
|
||||
return;
|
||||
}
|
||||
if (bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
|
||||
{
|
||||
bd->pVB->Unlock();
|
||||
state_block->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
|
||||
// FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and
|
||||
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
{
|
||||
const ImDrawVert* vtx_src = draw_list->VtxBuffer.Data;
|
||||
for (int i = 0; i < draw_list->VtxBuffer.Size; i++)
|
||||
{
|
||||
vtx_dst->pos[0] = vtx_src->pos.x;
|
||||
vtx_dst->pos[1] = vtx_src->pos.y;
|
||||
vtx_dst->pos[2] = 0.0f;
|
||||
vtx_dst->col = IMGUI_COL_TO_DX9_ARGB(vtx_src->col);
|
||||
vtx_dst->uv[0] = vtx_src->uv.x;
|
||||
vtx_dst->uv[1] = vtx_src->uv.y;
|
||||
vtx_dst++;
|
||||
vtx_src++;
|
||||
}
|
||||
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
idx_dst += draw_list->IdxBuffer.Size;
|
||||
}
|
||||
bd->pVB->Unlock();
|
||||
bd->pIB->Unlock();
|
||||
device->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX));
|
||||
device->SetIndices(bd->pIB);
|
||||
device->SetFVF(D3DFVF_CUSTOMVERTEX);
|
||||
|
||||
// Setup desired DX state
|
||||
struct render_state_t render_state = {};
|
||||
ImGui_ImplDX9_SetupRenderState(draw_data, &render_state);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++) {
|
||||
const ImDrawList *cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
|
||||
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL) {
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
} else {
|
||||
const RECT r = {(LONG) (pcmd->ClipRect.x - clip_off.x), (LONG) (pcmd->ClipRect.y - clip_off.y),
|
||||
(LONG) (pcmd->ClipRect.z - clip_off.x), (LONG) (pcmd->ClipRect.w - clip_off.y)};
|
||||
auto texture = reinterpret_cast<IDirect3DBaseTexture9 *>(pcmd->TextureId);
|
||||
g_pd3dDevice->SetTexture(0, texture);
|
||||
g_pd3dDevice->SetScissorRect(&r);
|
||||
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
|
||||
pcmd->VtxOffset + global_vtx_offset, 0,
|
||||
(UINT) cmd_list->VtxBuffer.Size,
|
||||
pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
{
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != nullptr)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX9_SetupRenderState(draw_data, &render_state);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
const RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
device->SetScissorRect(&r);
|
||||
|
||||
// Bind texture, Draw
|
||||
const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID();
|
||||
device->SetTexture(0, texture);
|
||||
device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)draw_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
global_idx_offset += draw_list->IdxBuffer.Size;
|
||||
global_vtx_offset += draw_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
if (back_buffer) {
|
||||
back_buffer->Release();
|
||||
back_buffer = nullptr;
|
||||
}
|
||||
// When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows
|
||||
// from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560)
|
||||
if (global_vtx_offset == 0)
|
||||
bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 0, 0, 0);
|
||||
|
||||
// restore previous depth stencil
|
||||
if (depth_stencil) {
|
||||
g_pd3dDevice->SetDepthStencilSurface(depth_stencil);
|
||||
depth_stencil->Release();
|
||||
depth_stencil = nullptr;
|
||||
}
|
||||
// spice ldj
|
||||
{
|
||||
if (render_state.back_buffer) {
|
||||
render_state.back_buffer->Release();
|
||||
render_state.back_buffer = nullptr;
|
||||
}
|
||||
// restore previous depth stencil
|
||||
if (render_state.depth_stencil) {
|
||||
device->SetDepthStencilSurface(render_state.depth_stencil);
|
||||
render_state.depth_stencil->Release();
|
||||
render_state.depth_stencil = nullptr;
|
||||
}
|
||||
// restore all render target state
|
||||
for (size_t target = 0; target < std::min(8UL, render_state.caps.NumSimultaneousRTs); target++) {
|
||||
auto render_target = render_state.render_targets[target];
|
||||
|
||||
// restore all render target state
|
||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
||||
auto render_target = render_targets[target];
|
||||
|
||||
if (render_target) {
|
||||
g_pd3dDevice->SetRenderTarget(target, render_target);
|
||||
render_target->Release();
|
||||
if (render_target) {
|
||||
device->SetRenderTarget(target, render_target);
|
||||
render_target->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore the DX9 transform
|
||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
|
||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
|
||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
// Restore the DX9 transform
|
||||
device->SetTransform(D3DTS_WORLD, &last_world);
|
||||
device->SetTransform(D3DTS_VIEW, &last_view);
|
||||
device->SetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// restore the DX9 state
|
||||
d3d9_state_block->Apply();
|
||||
d3d9_state_block->Release();
|
||||
// Restore the DX9 state
|
||||
state_block->Apply();
|
||||
state_block->Release();
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_Init(IDirect3DDevice9 *device) {
|
||||
static bool ImGui_ImplDX9_CheckFormatSupport(LPDIRECT3DDEVICE9 pDevice, D3DFORMAT format)
|
||||
{
|
||||
LPDIRECT3D9 pd3d = nullptr;
|
||||
if (pDevice->GetDirect3D(&pd3d) != D3D_OK)
|
||||
return false;
|
||||
D3DDEVICE_CREATION_PARAMETERS param = {};
|
||||
D3DDISPLAYMODE mode = {};
|
||||
if (pDevice->GetCreationParameters(¶m) != D3D_OK || pDevice->GetDisplayMode(0, &mode) != D3D_OK)
|
||||
{
|
||||
pd3d->Release();
|
||||
return false;
|
||||
}
|
||||
// Font texture should support linear filter, color blend and write to render-target
|
||||
bool support = (pd3d->CheckDeviceFormat(param.AdapterOrdinal, param.DeviceType, mode.Format, D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, format)) == D3D_OK;
|
||||
pd3d->Release();
|
||||
return support;
|
||||
}
|
||||
|
||||
// Setup back-end capabilities flags
|
||||
auto &io = ImGui::GetIO();
|
||||
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplDX9_Data* bd = IM_NEW(ImGui_ImplDX9_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx9";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
|
||||
|
||||
// We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = 4096;
|
||||
|
||||
bd->pd3dDevice = device;
|
||||
bd->pd3dDevice->AddRef();
|
||||
bd->HasRgbaSupport = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8);
|
||||
|
||||
ImGui_ImplDX9_InitMultiViewportSupport();
|
||||
|
||||
g_pd3dDevice = device;
|
||||
g_pd3dDevice->AddRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_Shutdown() {
|
||||
void ImGui_ImplDX9_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
|
||||
ImGui_ImplDX9_ShutdownMultiViewportSupport();
|
||||
ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||
|
||||
if (g_pd3dDevice) {
|
||||
g_pd3dDevice->Release();
|
||||
g_pd3dDevice = NULL;
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
|
||||
platform_io.ClearRendererHandlers();
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
|
||||
static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, const ImU32* src, int src_pitch, ImU32* dst, int dst_pitch, int w, int h)
|
||||
{
|
||||
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
const bool convert_rgba_to_bgra = (!bd->HasRgbaSupport && tex_use_colors);
|
||||
#else
|
||||
const bool convert_rgba_to_bgra = false;
|
||||
IM_UNUSED(tex_use_colors);
|
||||
#endif
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
const ImU32* src_p = (const ImU32*)(const void*)((const unsigned char*)src + src_pitch * y);
|
||||
ImU32* dst_p = (ImU32*)(void*)((unsigned char*)dst + dst_pitch * y);
|
||||
if (convert_rgba_to_bgra)
|
||||
for (int x = w; x > 0; x--, src_p++, dst_p++) // Convert copy
|
||||
*dst_p = IMGUI_COL_TO_DX9_ARGB(*src_p);
|
||||
else
|
||||
memcpy(dst_p, src_p, w * 4); // Raw copy
|
||||
}
|
||||
}
|
||||
|
||||
static bool ImGui_ImplDX9_CreateFontsTexture() {
|
||||
// Build texture atlas
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
unsigned char *pixels;
|
||||
int width, height, bytes_per_pixel;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
|
||||
void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
|
||||
// Upload texture to graphics system
|
||||
g_FontTexture = NULL;
|
||||
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
|
||||
D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
|
||||
return false;
|
||||
D3DLOCKED_RECT tex_locked_rect;
|
||||
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
|
||||
return false;
|
||||
for (int y = 0; y < height; y++)
|
||||
memcpy((unsigned char *) tex_locked_rect.pBits + tex_locked_rect.Pitch * y,
|
||||
pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
|
||||
g_FontTexture->UnlockRect(0);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID) g_FontTexture;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_CreateDeviceObjects() {
|
||||
if (!g_pd3dDevice) {
|
||||
return false;
|
||||
}
|
||||
return ImGui_ImplDX9_CreateFontsTexture();
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_InvalidateDeviceObjects() {
|
||||
if (!g_pd3dDevice)
|
||||
return;
|
||||
if (g_pVB) {
|
||||
g_pVB->Release();
|
||||
g_pVB = NULL;
|
||||
}
|
||||
if (g_pIB) {
|
||||
g_pIB->Release();
|
||||
g_pIB = NULL;
|
||||
}
|
||||
if (g_FontTexture) {
|
||||
g_FontTexture->Release();
|
||||
g_FontTexture = NULL;
|
||||
ImGui::GetIO().Fonts->TexID = 0;
|
||||
} // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_NewFrame() {
|
||||
if (!g_FontTexture) {
|
||||
ImGui_ImplDX9_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
IDirect3DSwapChain9 *swap_chain = nullptr;
|
||||
if (SUCCEEDED(g_pd3dDevice->GetSwapChain(0, &swap_chain))) {
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
D3DPRESENT_PARAMETERS present_params {};
|
||||
|
||||
if (SUCCEEDED(swap_chain->GetPresentParameters(&present_params))) {
|
||||
if (present_params.BackBufferWidth != 0 && present_params.BackBufferHeight != 0) {
|
||||
io.DisplaySize.x = static_cast<float>(present_params.BackBufferWidth);
|
||||
io.DisplaySize.y = static_cast<float>(present_params.BackBufferHeight);
|
||||
} else {
|
||||
RECT rect {};
|
||||
GetClientRect(present_params.hDeviceWindow, &rect);
|
||||
|
||||
io.DisplaySize.x = static_cast<float>(rect.right - rect.left);
|
||||
io.DisplaySize.y = static_cast<float>(rect.bottom - rect.top);
|
||||
}
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
LPDIRECT3DTEXTURE9 dx_tex = nullptr;
|
||||
HRESULT hr = bd->pd3dDevice->CreateTexture(tex->Width, tex->Height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &dx_tex, nullptr);
|
||||
if (hr < 0)
|
||||
{
|
||||
IM_ASSERT(hr >= 0 && "Backend failed to create texture!");
|
||||
return;
|
||||
}
|
||||
|
||||
swap_chain->Release();
|
||||
D3DLOCKED_RECT locked_rect;
|
||||
if (dx_tex->LockRect(0, &locked_rect, nullptr, 0) == D3D_OK)
|
||||
{
|
||||
ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixels(), tex->Width * 4, (ImU32*)locked_rect.pBits, (ImU32)locked_rect.Pitch, tex->Width, tex->Height);
|
||||
dx_tex->UnlockRect(0);
|
||||
}
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)dx_tex);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantUpdates)
|
||||
{
|
||||
// Update selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
||||
LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)(intptr_t)tex->TexID;
|
||||
RECT update_rect = { (LONG)tex->UpdateRect.x, (LONG)tex->UpdateRect.y, (LONG)(tex->UpdateRect.x + tex->UpdateRect.w), (LONG)(tex->UpdateRect.y + tex->UpdateRect.h) };
|
||||
D3DLOCKED_RECT locked_rect;
|
||||
if (backend_tex->LockRect(0, &locked_rect, &update_rect, 0) == D3D_OK)
|
||||
for (ImTextureRect& r : tex->Updates)
|
||||
ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixelsAt(r.x, r.y), tex->Width * 4,
|
||||
(ImU32*)locked_rect.pBits + (r.x - update_rect.left) + (r.y - update_rect.top) * (locked_rect.Pitch / 4), (int)locked_rect.Pitch, r.w, r.h);
|
||||
backend_tex->UnlockRect(0);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantDestroy)
|
||||
{
|
||||
if (tex->TexID != ImTextureID_Invalid)
|
||||
if (LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)tex->TexID)
|
||||
{
|
||||
IM_ASSERT(tex->TexID == (ImTextureID)(intptr_t)backend_tex);
|
||||
backend_tex->Release();
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_CreateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
if (!bd || !bd->pd3dDevice)
|
||||
return false;
|
||||
ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_InvalidateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
if (!bd || !bd->pd3dDevice)
|
||||
return;
|
||||
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
{
|
||||
tex->SetStatus(ImTextureStatus_WantDestroy);
|
||||
ImGui_ImplDX9_UpdateTexture(tex);
|
||||
}
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_NewFrame()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX9_Init()?");
|
||||
// IM_UNUSED(bd);
|
||||
|
||||
// spice ldj
|
||||
{
|
||||
IDirect3DSwapChain9 *swap_chain = nullptr;
|
||||
if (SUCCEEDED(bd->pd3dDevice->GetSwapChain(0, &swap_chain))) {
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
D3DPRESENT_PARAMETERS present_params {};
|
||||
|
||||
if (SUCCEEDED(swap_chain->GetPresentParameters(&present_params))) {
|
||||
if (present_params.BackBufferWidth != 0 && present_params.BackBufferHeight != 0) {
|
||||
io.DisplaySize.x = static_cast<float>(present_params.BackBufferWidth);
|
||||
io.DisplaySize.y = static_cast<float>(present_params.BackBufferHeight);
|
||||
} else {
|
||||
RECT rect {};
|
||||
GetClientRect(present_params.hDeviceWindow, &rect);
|
||||
|
||||
io.DisplaySize.x = static_cast<float>(rect.right - rect.left);
|
||||
io.DisplaySize.y = static_cast<float>(rect.bottom - rect.top);
|
||||
}
|
||||
}
|
||||
|
||||
swap_chain->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
|
||||
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
|
||||
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
|
||||
struct ImGui_ImplDX9_ViewportData
|
||||
{
|
||||
IDirect3DSwapChain9* SwapChain;
|
||||
D3DPRESENT_PARAMETERS d3dpp;
|
||||
|
||||
ImGui_ImplDX9_ViewportData() { SwapChain = nullptr; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); }
|
||||
~ImGui_ImplDX9_ViewportData() { IM_ASSERT(SwapChain == nullptr); }
|
||||
};
|
||||
|
||||
static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
ImGui_ImplDX9_ViewportData* vd = IM_NEW(ImGui_ImplDX9_ViewportData)();
|
||||
viewport->RendererUserData = vd;
|
||||
|
||||
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL's WindowID).
|
||||
// Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
|
||||
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
|
||||
IM_ASSERT(hwnd != 0);
|
||||
|
||||
ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS));
|
||||
vd->d3dpp.Windowed = TRUE;
|
||||
vd->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||
vd->d3dpp.BackBufferWidth = (UINT)viewport->Size.x;
|
||||
vd->d3dpp.BackBufferHeight = (UINT)viewport->Size.y;
|
||||
vd->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
|
||||
vd->d3dpp.hDeviceWindow = hwnd;
|
||||
vd->d3dpp.EnableAutoDepthStencil = FALSE;
|
||||
vd->d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
|
||||
vd->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync
|
||||
|
||||
HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr);
|
||||
IM_ASSERT(hr == D3D_OK);
|
||||
IM_ASSERT(vd->SwapChain != nullptr);
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport)
|
||||
{
|
||||
// The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
|
||||
if (ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData)
|
||||
{
|
||||
if (vd->SwapChain)
|
||||
vd->SwapChain->Release();
|
||||
vd->SwapChain = nullptr;
|
||||
ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS));
|
||||
IM_DELETE(vd);
|
||||
}
|
||||
viewport->RendererUserData = nullptr;
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
|
||||
if (vd->SwapChain)
|
||||
{
|
||||
vd->SwapChain->Release();
|
||||
vd->SwapChain = nullptr;
|
||||
vd->d3dpp.BackBufferWidth = (UINT)size.x;
|
||||
vd->d3dpp.BackBufferHeight = (UINT)size.y;
|
||||
HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr);
|
||||
IM_ASSERT(hr == D3D_OK);
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
|
||||
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
LPDIRECT3DSURFACE9 render_target = nullptr;
|
||||
LPDIRECT3DSURFACE9 last_render_target = nullptr;
|
||||
LPDIRECT3DSURFACE9 last_depth_stencil = nullptr;
|
||||
vd->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target);
|
||||
bd->pd3dDevice->GetRenderTarget(0, &last_render_target);
|
||||
bd->pd3dDevice->GetDepthStencilSurface(&last_depth_stencil);
|
||||
bd->pd3dDevice->SetRenderTarget(0, render_target);
|
||||
bd->pd3dDevice->SetDepthStencilSurface(nullptr);
|
||||
|
||||
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
|
||||
{
|
||||
D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*255.0f), (int)(clear_color.y*255.0f), (int)(clear_color.z*255.0f), (int)(clear_color.w*255.0f));
|
||||
bd->pd3dDevice->Clear(0, nullptr, D3DCLEAR_TARGET, clear_col_dx, 1.0f, 0);
|
||||
}
|
||||
|
||||
ImGui_ImplDX9_RenderDrawData(viewport->DrawData);
|
||||
|
||||
// Restore render target
|
||||
bd->pd3dDevice->SetRenderTarget(0, last_render_target);
|
||||
bd->pd3dDevice->SetDepthStencilSurface(last_depth_stencil);
|
||||
render_target->Release();
|
||||
last_render_target->Release();
|
||||
if (last_depth_stencil) last_depth_stencil->Release();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*)
|
||||
{
|
||||
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
|
||||
HRESULT hr = vd->SwapChain->Present(nullptr, nullptr, vd->d3dpp.hDeviceWindow, nullptr, 0);
|
||||
// Let main application handle D3DERR_DEVICELOST by resetting the device.
|
||||
IM_ASSERT(SUCCEEDED(hr) || hr == D3DERR_DEVICELOST);
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_InitMultiViewportSupport()
|
||||
{
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow;
|
||||
platform_io.Renderer_DestroyWindow = ImGui_ImplDX9_DestroyWindow;
|
||||
platform_io.Renderer_SetWindowSize = ImGui_ImplDX9_SetWindowSize;
|
||||
platform_io.Renderer_RenderWindow = ImGui_ImplDX9_RenderWindow;
|
||||
platform_io.Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers;
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_ShutdownMultiViewportSupport()
|
||||
{
|
||||
ImGui::DestroyPlatformWindows();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows()
|
||||
{
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
for (int i = 1; i < platform_io.Viewports.Size; i++)
|
||||
if (!platform_io.Viewports[i]->RendererUserData)
|
||||
ImGui_ImplDX9_CreateWindow(platform_io.Viewports[i]);
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows()
|
||||
{
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
for (int i = 1; i < platform_io.Viewports.Size; i++)
|
||||
if (platform_io.Viewports[i]->RendererUserData)
|
||||
ImGui_ImplDX9_DestroyWindow(platform_io.Viewports[i]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
+25
-12
@@ -1,25 +1,38 @@
|
||||
// dear imgui: Renderer for DirectX9
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
// dear imgui: Renderer Backend for DirectX9
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
|
||||
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct IDirect3DDevice9;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9 *device);
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing ImGui state.
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||
|
||||
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
+13
-5
@@ -15,7 +15,8 @@
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
// - If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
// - Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
@@ -29,7 +30,6 @@
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
@@ -49,6 +49,7 @@
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean/ProggyForever), remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
@@ -59,12 +60,15 @@
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Performance optimization for DX9
|
||||
#define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
@@ -83,13 +87,13 @@
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience.
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
||||
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
|
||||
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
@@ -130,6 +134,10 @@
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
|
||||
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
|
||||
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
|
||||
Vendored
+3439
-1953
File diff suppressed because it is too large
Load Diff
Vendored
+1023
-517
File diff suppressed because it is too large
Load Diff
+2825
-2079
File diff suppressed because it is too large
Load Diff
+3216
-1153
File diff suppressed because it is too large
Load Diff
+780
-340
File diff suppressed because it is too large
Load Diff
+258
-164
@@ -1,6 +1,7 @@
|
||||
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
||||
// Get latest version at http://www.github.com/ocornut/imgui_club
|
||||
//
|
||||
// Licensed under The MIT License (MIT)
|
||||
|
||||
// Right-click anywhere to access the Options menu!
|
||||
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
||||
// The code assume a mono-space font for simplicity!
|
||||
@@ -39,10 +40,22 @@
|
||||
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]
|
||||
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
|
||||
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
|
||||
// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34)
|
||||
// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87.
|
||||
// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35)
|
||||
// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2])
|
||||
// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3]
|
||||
// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3]
|
||||
// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview.
|
||||
// *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed]
|
||||
// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54)
|
||||
// - v0.57 (2025/03/26): fixed warnings. using ImGui's ImSXX/ImUXX types instead of e.g. int32_t/uint32_t. (#56)
|
||||
// - v0.58 (2025/03/31): fixed extraneous footer spacing (added in 0.51) breaking vertical auto-resize. (#53)
|
||||
// - v0.59 (2025/04/08): fixed GotoAddrAndHighlight() not working if OptShowOptions is disabled.
|
||||
//
|
||||
// Todo/Bugs:
|
||||
// TODO:
|
||||
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.
|
||||
// - PageUp/PageDown are supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
|
||||
// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
|
||||
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
|
||||
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
||||
|
||||
@@ -51,15 +64,18 @@
|
||||
#include <stdio.h> // sprintf, scanf
|
||||
#include <stdint.h> // uint8_t, etc.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _PRISizeT "I"
|
||||
#if defined(_MSC_VER) && !defined(snprintf)
|
||||
#define ImSnprintf _snprintf
|
||||
#else
|
||||
#define _PRISizeT "z"
|
||||
#define ImSnprintf snprintf
|
||||
#endif
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define _PRISizeT "I"
|
||||
#else
|
||||
#define _PRISizeT "z"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) || defined(_UCRT)
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||
#endif
|
||||
@@ -88,9 +104,17 @@ struct MemoryEditor
|
||||
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
||||
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
|
||||
ImU32 HighlightColor; // // background color of highlighted bytes.
|
||||
ImU8 (*ReadFn)(const ImU8* data, size_t off); // = 0 // optional handler to read bytes.
|
||||
void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes.
|
||||
bool (*HighlightFn)(const ImU8* data, size_t off);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||
|
||||
// Function handlers
|
||||
ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes.
|
||||
void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes.
|
||||
bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||
ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes.
|
||||
void* UserData; // = NULL // user data forwarded to the function handlers
|
||||
|
||||
// Public read-only data
|
||||
bool MouseHovered; // set when mouse is hovering a value.
|
||||
size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set.
|
||||
|
||||
// [Internal State]
|
||||
bool ContentsWidthChanged;
|
||||
@@ -101,7 +125,7 @@ struct MemoryEditor
|
||||
char AddrInputBuf[32];
|
||||
size_t GotoAddr;
|
||||
size_t HighlightMin, HighlightMax;
|
||||
int PreviewEndianess;
|
||||
int PreviewEndianness;
|
||||
ImGuiDataType PreviewDataType;
|
||||
|
||||
MemoryEditor()
|
||||
@@ -120,9 +144,11 @@ struct MemoryEditor
|
||||
OptAddrDigitsCount = 0;
|
||||
OptFooterExtraHeight = 0.0f;
|
||||
HighlightColor = IM_COL32(255, 255, 255, 50);
|
||||
ReadFn = NULL;
|
||||
WriteFn = NULL;
|
||||
HighlightFn = NULL;
|
||||
ReadFn = nullptr;
|
||||
WriteFn = nullptr;
|
||||
HighlightFn = nullptr;
|
||||
BgColorFn = nullptr;
|
||||
UserData = nullptr;
|
||||
|
||||
// State/Internals
|
||||
ContentsWidthChanged = false;
|
||||
@@ -131,8 +157,10 @@ struct MemoryEditor
|
||||
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
||||
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
||||
GotoAddr = (size_t)-1;
|
||||
MouseHovered = false;
|
||||
MouseHoveredAddr = 0;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
PreviewEndianess = 0;
|
||||
PreviewEndianness = 0;
|
||||
PreviewDataType = ImGuiDataType_S32;
|
||||
}
|
||||
|
||||
@@ -145,16 +173,16 @@ struct MemoryEditor
|
||||
|
||||
struct Sizes
|
||||
{
|
||||
int AddrDigitsCount;
|
||||
float LineHeight;
|
||||
float GlyphWidth;
|
||||
float HexCellWidth;
|
||||
float SpacingBetweenMidCols;
|
||||
float PosHexStart;
|
||||
float PosHexEnd;
|
||||
float PosAsciiStart;
|
||||
float PosAsciiEnd;
|
||||
float WindowWidth;
|
||||
int AddrDigitsCount; // Number of digits required to represent maximum address.
|
||||
float LineHeight; // Height of each line (no spacing).
|
||||
float GlyphWidth; // Glyph width (assume mono-space).
|
||||
float HexCellWidth; // Width of a hex edit cell ~2.5f * GlypHWidth.
|
||||
float SpacingBetweenMidCols; // Spacing between each columns section (OptMidColsCount).
|
||||
float OffsetHexMinX;
|
||||
float OffsetHexMaxX;
|
||||
float OffsetAsciiMinX;
|
||||
float OffsetAsciiMaxX;
|
||||
float WindowWidth; // Ideal window width.
|
||||
|
||||
Sizes() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
@@ -170,17 +198,17 @@ struct MemoryEditor
|
||||
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
|
||||
s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere
|
||||
s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
|
||||
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
||||
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
||||
s.OffsetHexMinX = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||
s.OffsetHexMaxX = s.OffsetHexMinX + (s.HexCellWidth * Cols);
|
||||
s.OffsetAsciiMinX = s.OffsetAsciiMaxX = s.OffsetHexMaxX;
|
||||
if (OptShowAscii)
|
||||
{
|
||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||
s.OffsetAsciiMinX = s.OffsetHexMaxX + s.GlyphWidth * 1;
|
||||
if (OptMidColsCount > 0)
|
||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
||||
s.OffsetAsciiMinX += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.OffsetAsciiMaxX = s.OffsetAsciiMinX + Cols * s.GlyphWidth;
|
||||
}
|
||||
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||
s.WindowWidth = s.OffsetAsciiMaxX + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||
}
|
||||
|
||||
// Standalone Memory Editor window
|
||||
@@ -194,8 +222,6 @@ struct MemoryEditor
|
||||
Open = true;
|
||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("context");
|
||||
DrawContents(mem_data, mem_size, base_display_addr);
|
||||
if (ContentsWidthChanged)
|
||||
{
|
||||
@@ -217,6 +243,8 @@ struct MemoryEditor
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos();
|
||||
|
||||
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
||||
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
||||
const float height_separator = style.ItemSpacing.y;
|
||||
@@ -225,20 +253,21 @@ struct MemoryEditor
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||
if (OptShowDataPreview)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
|
||||
const ImVec2 avail_size = ImGui::GetContentRegionAvail();
|
||||
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(line_total_count, s.LineHeight);
|
||||
|
||||
bool data_next = false;
|
||||
|
||||
if (ReadOnly || DataEditingAddr >= mem_size)
|
||||
if (DataEditingAddr >= mem_size)
|
||||
DataEditingAddr = (size_t)-1;
|
||||
if (DataPreviewAddr >= mem_size)
|
||||
DataPreviewAddr = (size_t)-1;
|
||||
@@ -249,16 +278,16 @@ struct MemoryEditor
|
||||
if (DataEditingAddr != (size_t)-1)
|
||||
{
|
||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
||||
}
|
||||
|
||||
// Draw vertical separator
|
||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||
if (OptShowAscii)
|
||||
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||
draw_list->AddLine(ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||
|
||||
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
||||
@@ -268,36 +297,51 @@ struct MemoryEditor
|
||||
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||
|
||||
MouseHovered = false;
|
||||
MouseHoveredAddr = 0;
|
||||
|
||||
while (clipper.Step())
|
||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
||||
{
|
||||
size_t addr = (size_t)(line_i * Cols);
|
||||
size_t addr = (size_t)line_i * Cols;
|
||||
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||
|
||||
// Draw Hexadecimal
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
|
||||
float byte_pos_x = s.OffsetHexMinX + s.HexCellWidth * n;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
||||
// Draw highlight or custom background color
|
||||
const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData));
|
||||
const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
||||
|
||||
ImU32 bg_color = 0;
|
||||
bool is_next_byte_highlighted = false;
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float highlight_width = s.GlyphWidth * 2;
|
||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1)));
|
||||
is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size));
|
||||
bg_color = HighlightColor;
|
||||
}
|
||||
else if (BgColorFn != nullptr)
|
||||
{
|
||||
is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0);
|
||||
bg_color = BgColorFn(mem_data, addr, UserData);
|
||||
}
|
||||
if (bg_color != 0)
|
||||
{
|
||||
float bg_width = s.GlyphWidth * 2;
|
||||
if (is_next_byte_highlighted || (n + 1 == Cols))
|
||||
{
|
||||
highlight_width = s.HexCellWidth;
|
||||
bg_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||
highlight_width += s.SpacingBetweenMidCols;
|
||||
bg_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color);
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr)
|
||||
@@ -308,17 +352,21 @@ struct MemoryEditor
|
||||
if (DataEditingTakeFocus)
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||
ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||
}
|
||||
struct UserData
|
||||
struct InputTextUserData
|
||||
{
|
||||
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
||||
static int Callback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
UserData* user_data = (UserData*)data->UserData;
|
||||
InputTextUserData* user_data = (InputTextUserData*)data->UserData;
|
||||
if (!data->HasSelection())
|
||||
user_data->CursorPos = data->CursorPos;
|
||||
#if IMGUI_VERSION_NUM < 19102
|
||||
if (data->Flags & ImGuiInputTextFlags_ReadOnly)
|
||||
return 0;
|
||||
#endif
|
||||
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||
{
|
||||
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
|
||||
@@ -334,39 +382,42 @@ struct MemoryEditor
|
||||
char CurrentBufOverwrite[3]; // Input
|
||||
int CursorPos; // Output
|
||||
};
|
||||
UserData user_data;
|
||||
user_data.CursorPos = -1;
|
||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
InputTextUserData input_text_user_data;
|
||||
input_text_user_data.CursorPos = -1;
|
||||
ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
|
||||
#if IMGUI_VERSION_NUM >= 18104
|
||||
flags |= ImGuiInputTextFlags_AlwaysOverwrite;
|
||||
#else
|
||||
flags |= ImGuiInputTextFlags_AlwaysInsertMode;
|
||||
#endif
|
||||
if (ReadOnly)
|
||||
flags |= ImGuiInputTextFlags_ReadOnly;
|
||||
flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode
|
||||
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
|
||||
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, UserData::Callback, &user_data))
|
||||
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data))
|
||||
data_write = data_next = true;
|
||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
if (user_data.CursorPos >= 2)
|
||||
if (input_text_user_data.CursorPos >= 2)
|
||||
data_write = data_next = true;
|
||||
if (data_editing_addr_next != (size_t)-1)
|
||||
data_write = data_next = false;
|
||||
unsigned int data_input_value = 0;
|
||||
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||
if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||
{
|
||||
if (WriteFn)
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);
|
||||
else
|
||||
mem_data[addr] = (ImU8)data_input_value;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = addr;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
else
|
||||
{
|
||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||
|
||||
if (OptShowHexII)
|
||||
{
|
||||
@@ -386,10 +437,15 @@ struct MemoryEditor
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = addr;
|
||||
if (ImGui::IsMouseClicked(0))
|
||||
{
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -397,15 +453,24 @@ struct MemoryEditor
|
||||
if (OptShowAscii)
|
||||
{
|
||||
// Draw ASCII values
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImGui::SameLine(s.OffsetAsciiMinX);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
addr = (size_t)line_i * Cols;
|
||||
|
||||
const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x;
|
||||
const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.OffsetAsciiMaxX - s.OffsetAsciiMinX) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1;
|
||||
|
||||
ImGui::PushID(line_i);
|
||||
if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))
|
||||
if (ImGui::InvisibleButton("ascii", ImVec2(s.OffsetAsciiMaxX - s.OffsetAsciiMinX, s.LineHeight)))
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = addr + (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth);
|
||||
DataEditingAddr = DataPreviewAddr = mouse_addr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = mouse_addr;
|
||||
}
|
||||
ImGui::PopID();
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
@@ -414,7 +479,11 @@ struct MemoryEditor
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
else if (BgColorFn)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData));
|
||||
}
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||
pos.x += s.GlyphWidth;
|
||||
@@ -422,10 +491,14 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
const float child_width = ImGui::GetWindowSize().x;
|
||||
ImGui::EndChild();
|
||||
|
||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
||||
ImVec2 backup_pos = ImGui::GetCursorScreenPos();
|
||||
ImGui::SetCursorPosX(s.WindowWidth);
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
ImGui::SetCursorScreenPos(backup_pos);
|
||||
|
||||
if (data_next && DataEditingAddr + 1 < mem_size)
|
||||
{
|
||||
@@ -450,18 +523,28 @@ struct MemoryEditor
|
||||
ImGui::Separator();
|
||||
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
IM_UNUSED(mem_data);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
||||
if (GotoAddr != (size_t)-1)
|
||||
{
|
||||
if (GotoAddr < mem_size)
|
||||
{
|
||||
ImGui::BeginChild("##scrolling");
|
||||
ImGui::SetScrollY((GotoAddr / Cols) * ImGui::GetTextLineHeight() - avail_size.y * 0.5f);
|
||||
ImGui::EndChild();
|
||||
DataEditingAddr = DataPreviewAddr = GotoAddr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
GotoAddr = (size_t)-1;
|
||||
}
|
||||
|
||||
// Options menu
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("context");
|
||||
if (ImGui::BeginPopup("context"))
|
||||
const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y);
|
||||
//ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255));
|
||||
if (OptShowOptions)
|
||||
if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end))
|
||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("OptionsPopup");
|
||||
|
||||
if (ImGui::BeginPopup("OptionsPopup"))
|
||||
{
|
||||
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
|
||||
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
|
||||
@@ -473,6 +556,17 @@ struct MemoryEditor
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
IM_UNUSED(mem_data);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
||||
|
||||
// Options menu
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("OptionsPopup");
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
||||
@@ -488,18 +582,11 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
|
||||
if (GotoAddr != (size_t)-1)
|
||||
{
|
||||
if (GotoAddr < mem_size)
|
||||
{
|
||||
ImGui::BeginChild("##scrolling");
|
||||
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());
|
||||
ImGui::EndChild();
|
||||
DataEditingAddr = DataPreviewAddr = GotoAddr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
GotoAddr = (size_t)-1;
|
||||
}
|
||||
//if (MouseHovered)
|
||||
//{
|
||||
// ImGui::SameLine();
|
||||
// ImGui::Text("Hovered: %p", MouseHoveredAddr);
|
||||
//}
|
||||
}
|
||||
|
||||
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
||||
@@ -511,16 +598,21 @@ struct MemoryEditor
|
||||
ImGui::Text("Preview as:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
|
||||
static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double };
|
||||
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
||||
{
|
||||
for (int n = 0; n < ImGuiDataType_COUNT; n++)
|
||||
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
|
||||
PreviewDataType = (ImGuiDataType)n;
|
||||
for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++)
|
||||
{
|
||||
ImGuiDataType data_type = supported_data_types[n];
|
||||
if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))
|
||||
PreviewDataType = data_type;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
|
||||
ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0");
|
||||
|
||||
char buf[128] = "";
|
||||
float x = s.GlyphWidth * 6.0f;
|
||||
@@ -537,18 +629,19 @@ struct MemoryEditor
|
||||
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
}
|
||||
|
||||
// Utilities for Data Preview
|
||||
// Utilities for Data Preview (since we don't access imgui_internal.h)
|
||||
// FIXME: This technically depends on ImGuiDataType order.
|
||||
const char* DataTypeGetDesc(ImGuiDataType data_type) const
|
||||
{
|
||||
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs));
|
||||
return descs[data_type];
|
||||
}
|
||||
|
||||
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
||||
{
|
||||
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes));
|
||||
return sizes[data_type];
|
||||
}
|
||||
|
||||
@@ -561,18 +654,18 @@ struct MemoryEditor
|
||||
|
||||
bool IsBigEndian() const
|
||||
{
|
||||
uint16_t x = 1;
|
||||
ImU16 x = 1;
|
||||
char c[2];
|
||||
memcpy(c, &x, 2);
|
||||
return c[0] != 0;
|
||||
}
|
||||
|
||||
static void* EndianessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
{
|
||||
if (is_little_endian)
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
ImU8* dst = (ImU8*)_dst;
|
||||
ImU8* src = (ImU8*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
@@ -583,7 +676,7 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
|
||||
static void* EndianessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
{
|
||||
if (is_little_endian)
|
||||
{
|
||||
@@ -591,23 +684,23 @@ struct MemoryEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
ImU8* dst = (ImU8*)_dst;
|
||||
ImU8* src = (ImU8*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
}
|
||||
}
|
||||
|
||||
void* EndianessCopy(void* dst, void* src, size_t size) const
|
||||
void* EndiannessCopy(void* dst, void* src, size_t size) const
|
||||
{
|
||||
static void* (*fp)(void*, void*, size_t, int) = NULL;
|
||||
if (fp == NULL)
|
||||
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianess);
|
||||
static void* (*fp)(void*, void*, size_t, int) = nullptr;
|
||||
if (fp == nullptr)
|
||||
fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianness);
|
||||
}
|
||||
|
||||
const char* FormatBinary(const uint8_t* buf, int width) const
|
||||
const char* FormatBinary(const ImU8* buf, int width) const
|
||||
{
|
||||
IM_ASSERT(width <= 64);
|
||||
size_t out_n = 0;
|
||||
@@ -627,19 +720,19 @@ struct MemoryEditor
|
||||
// [Internal]
|
||||
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
||||
{
|
||||
uint8_t buf[8];
|
||||
ImU8 buf[8];
|
||||
size_t elem_size = DataTypeGetSize(data_type);
|
||||
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
||||
if (ReadFn)
|
||||
for (int i = 0, n = (int)size; i < n; ++i)
|
||||
buf[i] = ReadFn(mem_data, addr + i);
|
||||
buf[i] = ReadFn(mem_data, addr + i, UserData);
|
||||
else
|
||||
memcpy(buf, mem_data + addr, size);
|
||||
|
||||
if (data_format == DataFormat_Bin)
|
||||
{
|
||||
uint8_t binbuf[8];
|
||||
EndianessCopy(binbuf, buf, size);
|
||||
ImU8 binbuf[8];
|
||||
EndiannessCopy(binbuf, buf, size);
|
||||
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
||||
return;
|
||||
}
|
||||
@@ -649,84 +742,85 @@ struct MemoryEditor
|
||||
{
|
||||
case ImGuiDataType_S8:
|
||||
{
|
||||
int8_t int8 = 0;
|
||||
EndianessCopy(&int8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", int8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
|
||||
ImS8 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U8:
|
||||
{
|
||||
uint8_t uint8 = 0;
|
||||
EndianessCopy(&uint8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", uint8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
|
||||
ImU8 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S16:
|
||||
{
|
||||
int16_t int16 = 0;
|
||||
EndianessCopy(&int16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", int16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
|
||||
ImS16 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U16:
|
||||
{
|
||||
uint16_t uint16 = 0;
|
||||
EndianessCopy(&uint16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", uint16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
|
||||
ImU16 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S32:
|
||||
{
|
||||
int32_t int32 = 0;
|
||||
EndianessCopy(&int32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", int32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
|
||||
ImS32 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U32:
|
||||
{
|
||||
uint32_t uint32 = 0;
|
||||
EndianessCopy(&uint32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", uint32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
|
||||
ImU32 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S64:
|
||||
{
|
||||
int64_t int64 = 0;
|
||||
EndianessCopy(&int64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
|
||||
ImS64 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U64:
|
||||
{
|
||||
uint64_t uint64 = 0;
|
||||
EndianessCopy(&uint64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)uint64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
|
||||
ImU64 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Float:
|
||||
{
|
||||
float float32 = 0.0f;
|
||||
EndianessCopy(&float32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
|
||||
float data = 0.0f;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Double:
|
||||
{
|
||||
double float64 = 0.0;
|
||||
EndianessCopy(&float64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
|
||||
double data = 0.0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case ImGuiDataType_COUNT:
|
||||
break;
|
||||
} // Switch
|
||||
|
||||
+352
-176
File diff suppressed because it is too large
Load Diff
+1497
-961
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -315,7 +315,7 @@ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
// but we'll now add more waste for everything we've visited
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
@@ -470,7 +470,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
// stitched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
|
||||
+113
-55
@@ -5,7 +5,8 @@
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||
// - Added name to struct or it may be forward declared in our code.
|
||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
// - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||
// Grep for [DEAR IMGUI] to find some changes.
|
||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||
|
||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||
@@ -39,6 +40,7 @@
|
||||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||
// 1.14 (2021-07-11) page up/down, various fixes
|
||||
// 1.13 (2019-02-07) fix bug in undo size management
|
||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||
@@ -141,12 +143,14 @@
|
||||
// with previous char)
|
||||
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
||||
// (return type is int, -1 means not valid to insert)
|
||||
// (not supported if you want to use UTF-8, see below)
|
||||
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
|
||||
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
||||
// as manually wordwrapping for end-of-line positioning
|
||||
//
|
||||
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
|
||||
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
||||
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
||||
// returns number of characters actually inserted. [DEAR IMGUI]
|
||||
//
|
||||
// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
|
||||
//
|
||||
@@ -178,6 +182,13 @@
|
||||
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
|
||||
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
|
||||
//
|
||||
// To support UTF-8:
|
||||
//
|
||||
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
|
||||
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
|
||||
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
|
||||
// Instead, call stb_textedit_text() directly for text contents.
|
||||
//
|
||||
// Keyboard input must be encoded as a single integer value; e.g. a character code
|
||||
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
||||
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
||||
@@ -250,8 +261,10 @@
|
||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||
// transformed into text and stb_textedit_text() is automatically called.
|
||||
//
|
||||
// text: [DEAR IMGUI] added 2024-09
|
||||
// call this to text inputs sent to the textfield.
|
||||
// text: (added 2025)
|
||||
// call this to directly send text input the textfield, which is required
|
||||
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
|
||||
// cannot infer text length.
|
||||
//
|
||||
//
|
||||
// When rendering, you can read the cursor position and selection state from
|
||||
@@ -400,6 +413,16 @@ typedef struct
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -407,7 +430,7 @@ typedef struct
|
||||
//
|
||||
|
||||
// traverse the layout to locate the nearest character to a display position
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
@@ -417,6 +440,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
r.x0 = r.x1 = 0;
|
||||
r.ymin = r.ymax = 0;
|
||||
r.num_chars = 0;
|
||||
*out_side_on_line = 0;
|
||||
|
||||
// search rows to find one that straddles 'y'
|
||||
while (i < n) {
|
||||
@@ -436,7 +460,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
|
||||
// below all text, return 'after' last character
|
||||
if (i >= n)
|
||||
{
|
||||
*out_side_on_line = 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
// check if it's before the beginning of the line
|
||||
if (x < r.x0)
|
||||
@@ -449,6 +476,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||
if (x < prev_x+w) {
|
||||
*out_side_on_line = (k == 0) ? 0 : 1;
|
||||
if (x < prev_x+w/2)
|
||||
return k+i;
|
||||
else
|
||||
@@ -460,6 +488,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
}
|
||||
|
||||
// if the last character is a newline, return that. otherwise return 'after' the last character
|
||||
*out_side_on_line = 1;
|
||||
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
|
||||
return i+r.num_chars-1;
|
||||
else
|
||||
@@ -471,6 +500,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
||||
{
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
int side_on_line;
|
||||
if( state->single_line )
|
||||
{
|
||||
StbTexteditRow r;
|
||||
@@ -478,16 +508,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
||||
y = r.ymin;
|
||||
}
|
||||
|
||||
state->cursor = stb_text_locate_coord(str, x, y);
|
||||
state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||
state->select_start = state->cursor;
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||
}
|
||||
|
||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
int p = 0;
|
||||
int side_on_line;
|
||||
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
@@ -501,8 +533,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
||||
if (state->select_start == state->select_end)
|
||||
state->select_start = state->cursor;
|
||||
|
||||
p = stb_text_locate_coord(str, x, y);
|
||||
p = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||
state->cursor = state->select_end = p;
|
||||
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -552,6 +585,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||
if (n < i + r.num_chars)
|
||||
break;
|
||||
if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
|
||||
break;
|
||||
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
|
||||
break; // [DEAR IMGUI]
|
||||
prev_start = i;
|
||||
@@ -648,15 +683,33 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
|
||||
}
|
||||
}
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
|
||||
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
|
||||
#ifndef STB_TEXTEDIT_MOVELINESTART
|
||||
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||
{
|
||||
if (state->single_line)
|
||||
return 0;
|
||||
while (cursor > 0) {
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
|
||||
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
cursor = prev;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
|
||||
#ifndef STB_TEXTEDIT_MOVELINEEND
|
||||
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||
{
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->single_line)
|
||||
return n;
|
||||
while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
|
||||
return cursor;
|
||||
}
|
||||
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
|
||||
#endif
|
||||
|
||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||
@@ -668,9 +721,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
--c; // always move at least one character
|
||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||
--c;
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
|
||||
while (c >= 0 && !is_word_boundary(str, c))
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
|
||||
|
||||
if( c < 0 )
|
||||
c = 0;
|
||||
@@ -684,9 +737,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
|
||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||
++c; // always move at least one character
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
|
||||
while( c < len && !is_word_boundary( str, c ) )
|
||||
++c;
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
|
||||
|
||||
if( c > len )
|
||||
c = len;
|
||||
@@ -725,7 +778,8 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_delete_selection(str,state);
|
||||
// try to insert the characters
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {
|
||||
len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len);
|
||||
if (len) {
|
||||
stb_text_makeundo_insert(state, state->cursor, len);
|
||||
state->cursor += len;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -739,6 +793,7 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
||||
#define STB_TEXTEDIT_KEYTYPE int
|
||||
#endif
|
||||
|
||||
// API key: process text input
|
||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
@@ -749,14 +804,15 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||
if (text_len) {
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||
if (text_len) {
|
||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -771,6 +827,7 @@ retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||
// This is not suitable for UTF-8 support.
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||
@@ -911,15 +968,16 @@ retry:
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// going down while being on the last line shouldn't bring us to that line end
|
||||
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
//if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
// break;
|
||||
|
||||
// now find character position down a row
|
||||
state->cursor = start;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -927,10 +985,13 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
if (state->cursor == find.first_char + find.length)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||
state->has_preferred_x = 1;
|
||||
state->preferred_x = goal_x;
|
||||
|
||||
@@ -980,8 +1041,9 @@ retry:
|
||||
state->cursor = find.prev_first;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -989,10 +1051,15 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
if (state->cursor == find.first_char)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Right;
|
||||
else if (state->cursor == find.prev_first)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||
state->has_preferred_x = 1;
|
||||
state->preferred_x = goal_x;
|
||||
|
||||
@@ -1002,10 +1069,15 @@ retry:
|
||||
// go to previous line
|
||||
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
||||
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
|
||||
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
--prev_scan;
|
||||
while (prev_scan > 0)
|
||||
{
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
|
||||
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
prev_scan = prev;
|
||||
}
|
||||
find.first_char = find.prev_first;
|
||||
find.prev_first = prev_scan;
|
||||
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1079,10 +1151,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINESTART:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_move_to_first(state);
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -1090,13 +1159,9 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINEEND2:
|
||||
#endif
|
||||
case STB_TEXTEDIT_K_LINEEND: {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_move_to_first(state);
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
stb_textedit_move_to_last(str, state);
|
||||
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
}
|
||||
@@ -1107,10 +1172,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1119,13 +1181,9 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
|
||||
#endif
|
||||
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1300,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// check type of recorded action:
|
||||
if (u.insert_length) {
|
||||
// easy case: was a deletion, so we need to insert n characters
|
||||
STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
||||
u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
||||
s->undo_char_point -= u.insert_length;
|
||||
}
|
||||
|
||||
@@ -1351,7 +1409,7 @@ static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
|
||||
if (r.insert_length) {
|
||||
// easy case: need to insert n characters
|
||||
STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
||||
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
||||
s->redo_char_point += r.insert_length;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -886,7 +886,7 @@ STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, fl
|
||||
// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
|
||||
|
||||
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
|
||||
// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
|
||||
// the same as stbtt_GetCodepointBitmap, but you can specify a subpixel
|
||||
// shift for the character
|
||||
|
||||
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
|
||||
@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
|
||||
q2[0] = (float)x2;
|
||||
q2[1] = (float)y2;
|
||||
if (equal(q0,q1) || equal(q1,q2)) {
|
||||
x0 = (int)verts[i-1].x;
|
||||
y0 = (int)verts[i-1].y;
|
||||
x0 = (int)verts[i-1].x; //-V1048
|
||||
y0 = (int)verts[i-1].y; //-V1048
|
||||
x1 = (int)verts[i ].x;
|
||||
y1 = (int)verts[i ].y;
|
||||
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
||||
|
||||
@@ -65,6 +65,10 @@ namespace games::ccj {
|
||||
libutils::load_library("libaio-iob2_video.dll");
|
||||
libutils::load_library("win10actlog.dll");
|
||||
|
||||
// we do this to prevent this unexpectedly being unloaded
|
||||
// which leads to D3D10Warp.dll_unloaded crash
|
||||
libutils::load_library("d3d10warp.dll");
|
||||
|
||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
||||
|
||||
|
||||
+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
|
||||
@@ -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",
|
||||
@@ -552,18 +552,6 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modified &&
|
||||
(width > height) &&
|
||||
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
||||
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW)) {
|
||||
log_misc(
|
||||
"graphics::d3d9", "swapping width and height for mode {}, {}x{} @ {}Hz (-autoorientation)",
|
||||
Mode, width, height, refresh);
|
||||
pMode->Height = width;
|
||||
pMode->Width = height;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_RESULT(ret);
|
||||
@@ -945,26 +933,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 +991,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 +1558,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);
|
||||
|
||||
@@ -58,7 +58,6 @@ bool GRAPHICS_CAPTURE_CURSOR = false;
|
||||
bool GRAPHICS_LOG_HRESULT = false;
|
||||
bool GRAPHICS_SDVX_FORCE_720 = false;
|
||||
bool GRAPHICS_SHOW_CURSOR = false;
|
||||
graphics_orientation GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_NORMAL;
|
||||
bool GRAPHICS_WINDOWED = false;
|
||||
std::vector<HWND> GRAPHICS_WINDOWS;
|
||||
UINT GRAPHICS_FORCE_REFRESH = 0;
|
||||
@@ -71,6 +70,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;
|
||||
@@ -300,30 +300,6 @@ static BOOL WINAPI ClipCursor_hook(const RECT *lpRect) {
|
||||
return ClipCursor_orig(lpRect);
|
||||
}
|
||||
|
||||
static void fix_up_rb_auto_rotate_dimensions(DWORD &w, DWORD &h) {
|
||||
// reflec beat
|
||||
// for whatever reason, rb creates the first window it weird dimensions (774x1389 for reflesia, for example)
|
||||
// so doing ChangeDisplaySettings as-is ends up with DISP_CHANGE_BADMODE
|
||||
// while a second window (with title D3DProxyWindow) does get created with correct dimensions, it's
|
||||
// too late to rotate the window then; as a workaround force the display change now with patched up dimensions
|
||||
|
||||
bool changed = false;
|
||||
if (w == 774) {
|
||||
// LBR, MBR reflesia
|
||||
w = 768;
|
||||
h = 1360;
|
||||
changed = true;
|
||||
} else if (w == 1086) {
|
||||
// MBR (before reflesia)
|
||||
w = 1080;
|
||||
h = 1920;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
log_misc("graphics", "fix auto-rotate dimensions for reflec beat: w={}, h={}", w, h);
|
||||
}
|
||||
}
|
||||
|
||||
static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName,
|
||||
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
|
||||
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
|
||||
@@ -345,62 +321,6 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
fmt::ptr(hInstance),
|
||||
fmt::ptr(lpParam));
|
||||
|
||||
// set display orientation and/or refresh rate
|
||||
// only set orientation when the target window is portrait
|
||||
// (avoid doing this for SDVX subscreen which is in landscape, for example)
|
||||
auto adjust_orientation =
|
||||
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
||||
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW);
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model("KBR")) {
|
||||
// KBR (rb1) launches landscape and draws rotated by itself, don't do any rotation
|
||||
adjust_orientation = false;
|
||||
} else if (avs::game::is_model({"LBR", "MBR"}) && window_name == "D3DProxyWindow") {
|
||||
// do not auto-rotate for second window (D3DProxyWindow)
|
||||
adjust_orientation = false;
|
||||
}
|
||||
|
||||
if ((nHeight > nWidth && adjust_orientation) || GRAPHICS_FORCE_REFRESH > 0) {
|
||||
DEVMODE mode {};
|
||||
|
||||
// get display settings
|
||||
if (EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &mode)) {
|
||||
if (adjust_orientation) {
|
||||
DWORD orientation = GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ? DMDO_90 : DMDO_270;
|
||||
log_misc(
|
||||
"graphics",
|
||||
"auto-rotate: call ChangeDisplaySettings and rotate display to DMDO_xx mode {}, w={}, h={}",
|
||||
orientation, nWidth, nHeight);
|
||||
mode.dmPelsWidth = nWidth;
|
||||
mode.dmPelsHeight = nHeight;
|
||||
mode.dmDisplayOrientation = orientation;
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model({"LBR", "MBR"})) {
|
||||
fix_up_rb_auto_rotate_dimensions(mode.dmPelsWidth, mode.dmPelsHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (GRAPHICS_FORCE_REFRESH > 0) {
|
||||
log_info(
|
||||
"graphics",
|
||||
"call ChangeDisplaySettings to force refresh rate: {} => {} Hz (-graphics-force-refresh)",
|
||||
mode.dmDisplayFrequency,
|
||||
GRAPHICS_FORCE_REFRESH);
|
||||
|
||||
mode.dmDisplayFrequency = GRAPHICS_FORCE_REFRESH;
|
||||
}
|
||||
|
||||
const auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
||||
log_warning("graphics", "ChangeDisplaySettings failed: {}", disp_res);
|
||||
}
|
||||
} else {
|
||||
log_warning("graphics", "failed to get display settings");
|
||||
}
|
||||
}
|
||||
|
||||
// gfdm
|
||||
if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
||||
// set window name
|
||||
@@ -1051,6 +971,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
|
||||
@@ -1147,3 +1068,259 @@ std::string graphics_screenshot_genpath() {
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_dmdo_string(DWORD dmdo) {
|
||||
switch (dmdo) {
|
||||
case DMDO_DEFAULT:
|
||||
return "DMDO_DEFAULT";
|
||||
case DMDO_90:
|
||||
return "DMDO_90";
|
||||
case DMDO_180:
|
||||
return "DMDO_180";
|
||||
case DMDO_270:
|
||||
return "DMDO_270";
|
||||
default:
|
||||
return fmt::format("Unknown ({})", dmdo);
|
||||
}
|
||||
}
|
||||
|
||||
void change_primary_monitor(const std::string &monitor_name) {
|
||||
log_misc("graphics", "try changing primary monitor to {}...", monitor_name);
|
||||
|
||||
// for WinXP, since these are Vista+ or 7+ APIs
|
||||
const auto user32 = LoadLibraryA("user32.dll");
|
||||
if (!user32) {
|
||||
log_warning("graphics", "can't find user32.dll???");
|
||||
return;
|
||||
}
|
||||
const auto GetDisplayConfigBufferSizes_addr =
|
||||
reinterpret_cast<decltype(GetDisplayConfigBufferSizes) *>(
|
||||
GetProcAddress(user32, "GetDisplayConfigBufferSizes"));
|
||||
const auto QueryDisplayConfig_addr =
|
||||
reinterpret_cast<decltype(QueryDisplayConfig) *>(
|
||||
GetProcAddress(user32, "QueryDisplayConfig"));
|
||||
const auto DisplayConfigGetDeviceInfo_addr =
|
||||
reinterpret_cast<decltype(DisplayConfigGetDeviceInfo) *>(
|
||||
GetProcAddress(user32, "DisplayConfigGetDeviceInfo"));
|
||||
const auto SetDisplayConfig_addr =
|
||||
reinterpret_cast<decltype(SetDisplayConfig) *>(
|
||||
GetProcAddress(user32, "SetDisplayConfig"));
|
||||
if (GetDisplayConfigBufferSizes_addr == nullptr || QueryDisplayConfig_addr == nullptr ||
|
||||
DisplayConfigGetDeviceInfo_addr == nullptr || SetDisplayConfig_addr == nullptr) {
|
||||
log_warning("graphics", "cannot change primary monitor, OS does not support required APIs)");
|
||||
return;
|
||||
}
|
||||
|
||||
UINT32 path_count = 0;
|
||||
UINT32 mode_count = 0;
|
||||
std::vector<DISPLAYCONFIG_PATH_INFO> paths;
|
||||
std::vector<DISPLAYCONFIG_MODE_INFO> modes;
|
||||
bool succeeded = false;
|
||||
|
||||
// in a retry loop, try to query for display config
|
||||
// retry loop is needed because it can fail with ERROR_INSUFFICIENT_BUFFER
|
||||
for (int attempt = 0; attempt < 5; ++attempt) {
|
||||
auto status = GetDisplayConfigBufferSizes_addr(QDC_DATABASE_CURRENT, &path_count, &mode_count);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
log_warning("graphics", "GetDisplayConfigBufferSizes failed: {}", status);
|
||||
return;
|
||||
}
|
||||
|
||||
paths.resize(path_count);
|
||||
modes.resize(mode_count);
|
||||
DISPLAYCONFIG_TOPOLOGY_ID topology_id = DISPLAYCONFIG_TOPOLOGY_INTERNAL;
|
||||
status = QueryDisplayConfig_addr(
|
||||
QDC_DATABASE_CURRENT,
|
||||
&path_count,
|
||||
paths.data(),
|
||||
&mode_count,
|
||||
modes.data(),
|
||||
&topology_id);
|
||||
|
||||
if (status == ERROR_SUCCESS) {
|
||||
// Shrink to actual returned counts
|
||||
paths.resize(path_count);
|
||||
modes.resize(mode_count);
|
||||
succeeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != ERROR_INSUFFICIENT_BUFFER) {
|
||||
log_warning("graphics", "QueryDisplayConfig failed: {}", status);
|
||||
return;
|
||||
}
|
||||
|
||||
Sleep(500);
|
||||
}
|
||||
if (!succeeded) {
|
||||
log_warning("graphics", "QueryDisplayConfig failed after reaching max retries");
|
||||
return;
|
||||
}
|
||||
|
||||
LONG x = 0;
|
||||
LONG y = 0;
|
||||
bool found = false;
|
||||
|
||||
// find the new main monitor
|
||||
for (auto& mode : modes) {
|
||||
if (mode.infoType != DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DISPLAYCONFIG_SOURCE_DEVICE_NAME name = {};
|
||||
name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
|
||||
name.header.size = sizeof(name);
|
||||
name.header.adapterId = mode.adapterId;
|
||||
name.header.id = mode.id;
|
||||
if (DisplayConfigGetDeviceInfo_addr(&name.header) != ERROR_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto device_name = std::string(ws2s(name.viewGdiDeviceName));
|
||||
if (monitor_name == device_name) {
|
||||
x = mode.sourceMode.position.x;
|
||||
y = mode.sourceMode.position.y;
|
||||
found = true;
|
||||
log_info(
|
||||
"graphics",
|
||||
"new main monitor target found: {}, old position: ({}, {})",
|
||||
monitor_name,
|
||||
x, y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
log_fatal(
|
||||
"graphics",
|
||||
"new main monitor target not found, check -mainmonitor option: {}",
|
||||
monitor_name);
|
||||
return;
|
||||
}
|
||||
|
||||
// update monitor positions so that the new monitor is at (0, 0)
|
||||
for (auto& mode : modes) {
|
||||
if (mode.infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE) {
|
||||
mode.sourceMode.position.x -= x;
|
||||
mode.sourceMode.position.y -= y;
|
||||
}
|
||||
}
|
||||
|
||||
// finally, commit the new display config
|
||||
const auto status = SetDisplayConfig_addr(
|
||||
path_count,
|
||||
paths.data(),
|
||||
mode_count,
|
||||
modes.data(),
|
||||
SDC_APPLY | SDC_USE_SUPPLIED_DISPLAY_CONFIG);
|
||||
|
||||
if (status != ERROR_SUCCESS) {
|
||||
log_fatal("graphics", "SetDisplayConfig failed, check -mainmonitor option: {}", status);
|
||||
}
|
||||
|
||||
// a little extra time for windows to settle and redraw things
|
||||
Sleep(2000);
|
||||
}
|
||||
|
||||
void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate) {
|
||||
// note: all of this is only being done for the primary motnior
|
||||
|
||||
// get current settings
|
||||
DEVMODEA dm = {};
|
||||
dm.dmSize = sizeof(dm);
|
||||
if (!EnumDisplaySettingsExA(NULL, ENUM_CURRENT_SETTINGS, &dm, 0)) {
|
||||
log_warning("graphics", "EnumDisplaySettingsExa failed {}", get_last_error_string());
|
||||
return;
|
||||
}
|
||||
|
||||
bool needs_update = false;
|
||||
|
||||
if (target_orientation.has_value()) {
|
||||
// convert orientation values, and figure out if resolution needs to be swapped
|
||||
bool rotate_resolution = false;
|
||||
DWORD orientation = DMDO_DEFAULT;
|
||||
switch (target_orientation.value()) {
|
||||
case ORIENTATION_CW:
|
||||
orientation = DMDO_90;
|
||||
if (dm.dmDisplayOrientation == DMDO_DEFAULT || dm.dmDisplayOrientation == DMDO_180) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
case ORIENTATION_CCW:
|
||||
orientation = DMDO_270;
|
||||
if (dm.dmDisplayOrientation == DMDO_DEFAULT || dm.dmDisplayOrientation == DMDO_180) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
case ORIENTATION_FLIPPED:
|
||||
orientation = DMDO_180;
|
||||
if (dm.dmDisplayOrientation == DMDO_90 || dm.dmDisplayOrientation == DMDO_270) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
orientation = DMDO_DEFAULT;
|
||||
if (dm.dmDisplayOrientation == DMDO_90 || dm.dmDisplayOrientation == DMDO_270) {
|
||||
rotate_resolution = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// update orientation (and resolution if it must be swapped)
|
||||
if (dm.dmDisplayOrientation != orientation) {
|
||||
log_misc("graphics",
|
||||
"current orientation {} => desired orientation {}",
|
||||
get_dmdo_string(dm.dmDisplayOrientation), get_dmdo_string(orientation));
|
||||
|
||||
const DWORD originalWidth = dm.dmPelsWidth;
|
||||
const DWORD originalHeight = dm.dmPelsHeight;
|
||||
|
||||
// change orientation
|
||||
dm.dmDisplayOrientation = orientation;
|
||||
dm.dmFields |= DM_DISPLAYORIENTATION;
|
||||
|
||||
// rotate resolution
|
||||
if (rotate_resolution) {
|
||||
dm.dmPelsWidth = originalHeight;
|
||||
dm.dmPelsHeight = originalWidth;
|
||||
dm.dmFields |= DM_PELSHEIGHT | DM_PELSWIDTH;
|
||||
}
|
||||
|
||||
needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
// update refresh rate
|
||||
if (target_refresh_rate > 0) {
|
||||
log_misc("graphics",
|
||||
"current refresh rate {} => desired refresh rate {}",
|
||||
dm.dmDisplayFrequency, target_refresh_rate);
|
||||
|
||||
dm.dmDisplayFrequency = target_refresh_rate;
|
||||
dm.dmFields |= DM_DISPLAYFREQUENCY;
|
||||
needs_update = true;
|
||||
}
|
||||
|
||||
if (!needs_update) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
|
||||
if (result != DISP_CHANGE_SUCCESSFUL) {
|
||||
log_fatal(
|
||||
"graphics",
|
||||
"failed to update display settings ({}px x {}px @ {}Hz): error {}, double check options",
|
||||
dm.dmPelsWidth,
|
||||
dm.dmPelsHeight,
|
||||
dm.dmDisplayFrequency,
|
||||
result);
|
||||
} else {
|
||||
log_info("graphics", "display settings updated successfully ({}px x {}px @ {}Hz)",
|
||||
dm.dmPelsWidth,
|
||||
dm.dmPelsHeight,
|
||||
dm.dmDisplayFrequency);
|
||||
}
|
||||
|
||||
// sleep for a little bit after changing monitor settings to delay game launch
|
||||
Sleep(1000);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ enum graphics_orientation {
|
||||
ORIENTATION_CW = 0,
|
||||
ORIENTATION_CCW = 1,
|
||||
ORIENTATION_NORMAL = 2,
|
||||
ORIENTATION_FLIPPED = 3
|
||||
};
|
||||
|
||||
enum graphics_dx9on12_state {
|
||||
@@ -29,7 +30,6 @@ extern bool GRAPHICS_LOG_HRESULT;
|
||||
extern bool GRAPHICS_SDVX_FORCE_720;
|
||||
extern bool GRAPHICS_SHOW_CURSOR;
|
||||
extern bool GRAPHICS_WINDOWED;
|
||||
extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
||||
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
||||
extern UINT GRAPHICS_FORCE_REFRESH;
|
||||
extern std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
||||
@@ -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;
|
||||
@@ -106,3 +107,6 @@ bool graphics_window_decoration_change_crashes_game();
|
||||
bool graphics_window_resize_breaks_game();
|
||||
bool graphics_window_move_and_resize_breaks_game();
|
||||
void graphics_load_windowed_subscreen_parameters();
|
||||
|
||||
void change_primary_monitor(const std::string &monitor_name);
|
||||
void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate);
|
||||
@@ -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"
|
||||
|
||||
@@ -351,9 +351,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
||||
}
|
||||
if (options[launcher::Options::DisplayAdapter].is_active() &&
|
||||
options[launcher::Options::DisplayAdapter].value_uint32() != D3DADAPTER_DEFAULT) {
|
||||
D3D9_ADAPTER = options[launcher::Options::DisplayAdapter].value_uint32();
|
||||
if (options[launcher::Options::DX9DisplayAdapter].is_active() &&
|
||||
options[launcher::Options::DX9DisplayAdapter].value_uint32() != D3DADAPTER_DEFAULT) {
|
||||
D3D9_ADAPTER = options[launcher::Options::DX9DisplayAdapter].value_uint32();
|
||||
|
||||
// when we fix up adapter numbers, we only fix the first adapter, and not any subsequent
|
||||
// adapters, so we can't deal with multi-monitor games
|
||||
@@ -374,12 +374,15 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::AllowEA3Verbose].value_bool()) {
|
||||
avs::ea3::EA3_DEBUG_VERBOSE = true;
|
||||
}
|
||||
|
||||
std::optional<graphics_orientation> monitor_orientation;
|
||||
if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
|
||||
GRAPHICS_ADJUST_ORIENTATION =
|
||||
monitor_orientation =
|
||||
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
|
||||
} else if (options[launcher::Options::AdjustOrientation].value_bool()) {
|
||||
GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_CW;
|
||||
monitor_orientation = ORIENTATION_CW;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::spice2x_NoD3D9DeviceHook].value_bool()) {
|
||||
D3D9_DEVICE_HOOK_DISABLE = true;
|
||||
// touch emulation gets disabled, might as well turn these on
|
||||
@@ -559,6 +562,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;
|
||||
}
|
||||
@@ -976,7 +983,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// do not explicitly set GRAPHICS_9_ON_12_STATE to default here; must respect
|
||||
// legacy Graphics9On12 option from above if set
|
||||
}
|
||||
if (options[launcher::Options::NoLegacy].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
if (options[launcher::Options::NoLegacy].value_bool() && !cfg_run) {
|
||||
rawinput::NOLEGACY = true;
|
||||
}
|
||||
if (options[launcher::Options::RichPresence].value_bool()) {
|
||||
@@ -1011,18 +1018,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 +1098,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()) {
|
||||
@@ -1401,8 +1416,17 @@ int main_implementation(int argc, char *argv[]) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1486,6 +1510,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// early hooks
|
||||
for (auto &hook : early_hooks) {
|
||||
log_info("launcher", "loading early hook DLL {}", hook);
|
||||
libutils::print_dll_info(hook);
|
||||
HMODULE module;
|
||||
if (!(module = libutils::try_library(hook))) {
|
||||
log_warning("launcher", "failed to load early hook {}: {}", hook, get_last_error_string());
|
||||
@@ -2111,6 +2136,12 @@ int main_implementation(int argc, char *argv[]) {
|
||||
sysutils::print_gpus();
|
||||
}
|
||||
|
||||
// fix up monitor
|
||||
if (options[launcher::Options::PrimaryMonitor].is_active()) {
|
||||
change_primary_monitor(options[launcher::Options::PrimaryMonitor].value_text());
|
||||
}
|
||||
update_monitor_on_boot(monitor_orientation, GRAPHICS_FORCE_REFRESH);
|
||||
|
||||
// initialize raw input
|
||||
RI_MGR = std::make_unique<rawinput::RawInputManager>();
|
||||
for (const auto &device : sextet_devices) {
|
||||
@@ -2286,6 +2317,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// load hooks
|
||||
for (auto &hook : game_hooks) {
|
||||
log_info("launcher", "loading hook DLL {}", hook);
|
||||
libutils::print_dll_info(hook);
|
||||
HMODULE module;
|
||||
if (!(module = libutils::try_library(hook))) {
|
||||
log_warning("launcher", "failed to load hook {}: {}", hook, get_last_error_string());
|
||||
@@ -2546,9 +2578,10 @@ void dump_button_bindings(std::vector<Button> *buttons) {
|
||||
|
||||
if (button->isNaive()) {
|
||||
log_misc(
|
||||
"rawinput", " [{}] dev=Naive, vkey={}",
|
||||
"rawinput", " [{}] dev=Naive, vkey={} ({})",
|
||||
button->getName(),
|
||||
button->getVKey()
|
||||
button->getVKey(),
|
||||
button->getVKeyString()
|
||||
);
|
||||
} else {
|
||||
log_misc(
|
||||
@@ -2561,16 +2594,25 @@ void dump_button_bindings(std::vector<Button> *buttons) {
|
||||
}
|
||||
|
||||
for (auto& alt : button->getAlternatives()) {
|
||||
if (alt.getVKey() == INVALID_VKEY) {
|
||||
if (!alt.isValid()) {
|
||||
continue;
|
||||
}
|
||||
log_misc(
|
||||
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
|
||||
button->getName(),
|
||||
alt.isNaive() ? "Naive" : alt.getDeviceIdentifier(),
|
||||
alt.getVKey(),
|
||||
static_cast<uint32_t>(alt.getAnalogType())
|
||||
);
|
||||
if (alt.isNaive()) {
|
||||
log_misc(
|
||||
"rawinput", " [{}] (alt) dev=Naive, vkey={} ({})",
|
||||
alt.getName(),
|
||||
alt.getVKey(),
|
||||
alt.getVKeyString()
|
||||
);
|
||||
} else {
|
||||
log_misc(
|
||||
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
|
||||
alt.getName(),
|
||||
alt.getDeviceIdentifier(),
|
||||
alt.getVKey(),
|
||||
static_cast<uint32_t>(alt.getAnalogType())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
|
||||
"Game Options",
|
||||
"Common",
|
||||
"Network",
|
||||
"Monitor",
|
||||
"Graphics (Common)",
|
||||
"Graphics (Full Screen)",
|
||||
"Graphics (Windowed)",
|
||||
@@ -71,6 +72,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 +123,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.setting_name = "E004010000000000",
|
||||
.category = "Network",
|
||||
.sensitive = true,
|
||||
.picker = OptionPickerType::EACard,
|
||||
},
|
||||
{
|
||||
.title = "Player 2 Card",
|
||||
@@ -130,6 +133,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.setting_name = "E004010000000000",
|
||||
.category = "Network",
|
||||
.sensitive = true,
|
||||
.picker = OptionPickerType::EACard,
|
||||
},
|
||||
{
|
||||
// Player1PinMacro
|
||||
@@ -203,9 +207,24 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Common",
|
||||
},
|
||||
{
|
||||
.title = "Monitor",
|
||||
// PrimaryMonitor
|
||||
.title = "Change Main Monitor",
|
||||
.name = "mainmonitor",
|
||||
.desc = "Changes the primary monitor before launching the game. It will be restored on exit.\n\n"
|
||||
"This may fail to work properly on hybrid laptops with iGPU + dGPU.",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "\\\\.\\DISPLAY2",
|
||||
.category = "Monitor",
|
||||
.picker = OptionPickerType::Monitor,
|
||||
},
|
||||
{
|
||||
// DX9DisplayAdapter
|
||||
.title = "DX9 Primary Display Adapter Override",
|
||||
.name = "monitor",
|
||||
.desc = "Sets the display that the game will be opened in, for multiple monitors.\n\n"
|
||||
.display_name = "dx9mainadapter",
|
||||
.aliases = "dx9mainadapter",
|
||||
.desc = "Prefer to use Change Main Monitor option instead of this one.\n\n"
|
||||
"Sets the display that the game will be opened in, for multiple monitors.\n\n"
|
||||
"0 is the primary monitor, 1 is the second monitor, and so on.\n\n"
|
||||
"Not all games will respect this. Not recommended for multi-monitor games like Lightning Model / Valkyrie Model modes. "
|
||||
"Disable Full Screen Optimizations for best results.",
|
||||
@@ -213,21 +232,19 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Graphics (Full Screen)",
|
||||
},
|
||||
{
|
||||
.title = "Only Use One Monitor",
|
||||
.title = "Only Use Main Monitor For Full Screen",
|
||||
.name = "graphics-force-single-adapter",
|
||||
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
|
||||
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Graphics (Full Screen)",
|
||||
.category = "Monitor",
|
||||
},
|
||||
{
|
||||
.title = "Force Monitor Refresh Rate",
|
||||
.title = "Monitor Refresh Rate",
|
||||
.name = "graphics-force-refresh",
|
||||
.desc =
|
||||
"Attempt to change the refresh rate for the primary monitor before the game boots; "
|
||||
"works in both full screen and windowed modes, but known to fail for some games.",
|
||||
.desc = "Change the refresh rate for the primary monitor before launching the game. It will be restored on exit.",
|
||||
.type = OptionType::Integer,
|
||||
.category = "Graphics (Common)",
|
||||
.category = "Monitor",
|
||||
},
|
||||
{
|
||||
// FullscreenResolution
|
||||
@@ -256,9 +273,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 +635,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 +796,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 +845,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 +855,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 +903,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 +928,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 +1304,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 +1312,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 +1332,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 +1343,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 +1352,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 +1360,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 +1369,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 +1378,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 +1387,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 +1396,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 +1546,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 +1707,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 +1780,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 +1794,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",
|
||||
@@ -1795,17 +1861,20 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// spice2x_AutoOrientation
|
||||
.title = "Auto-rotate Display",
|
||||
.title = "Rotate Monitor",
|
||||
.name = "sp2x-autoorientation",
|
||||
.display_name = "autoorientation",
|
||||
.aliases= "autoorientation",
|
||||
.desc = "Automatically adjust the orientation of your display when launched. "
|
||||
"WARNING: game may launch at incorrect refresh rate! Use in combination with "
|
||||
"-graphics-force-refresh and potentially either -9on12 or full-screen optimizations (FSO) to fix.",
|
||||
.desc = "Change the orientation of the primary display before launching the game. It will be restored on exit",
|
||||
.type = OptionType::Enum,
|
||||
.category = "Graphics (Common)",
|
||||
// match graphics_orientation
|
||||
.elements = {{"0", "90 (CW)"}, {"1", "270 (CCW)"}},
|
||||
.category = "Monitor",
|
||||
// match graphics_orientation enum
|
||||
.elements = {
|
||||
{"0", "Portrait"},
|
||||
{"1", "Portrait, Flipped"},
|
||||
{"2", "Landscape"},
|
||||
{"3", "Landscape, Flipped"}
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "AVS Log Level",
|
||||
@@ -2251,7 +2320,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 +2649,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 +2695,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",
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,11 +26,13 @@ namespace launcher {
|
||||
ExecuteScript,
|
||||
CaptureCursor,
|
||||
ShowCursor,
|
||||
DisplayAdapter,
|
||||
PrimaryMonitor,
|
||||
DX9DisplayAdapter,
|
||||
GraphicsForceSingleAdapter,
|
||||
GraphicsForceRefresh,
|
||||
FullscreenResolution,
|
||||
FullscreenOrientationFlip,
|
||||
FullscreenSubResolution,
|
||||
FullscreenSubRefreshRate,
|
||||
Graphics9On12,
|
||||
spice2x_Dx9On12,
|
||||
@@ -85,11 +87,11 @@ namespace launcher {
|
||||
SDVXPrinterJPGQuality,
|
||||
SDVXDisableCameras,
|
||||
SDVXNativeTouch,
|
||||
spice2x_SDVXSubRedraw,
|
||||
spice2x_SDVXDigitalKnobSensitivity,
|
||||
SDVXDigitalKnobSocd,
|
||||
spice2x_SDVXAsioDriver,
|
||||
spice2x_SDVXSubPos,
|
||||
spice2x_SDVXSubRedraw,
|
||||
LoadDDRModule,
|
||||
DDR43Mode,
|
||||
DDRSkipCodecRegisteration,
|
||||
@@ -186,6 +188,7 @@ namespace launcher {
|
||||
spice2x_DisableVolumeHook,
|
||||
AudioBackend,
|
||||
AsioDriverId,
|
||||
AsioDriverName,
|
||||
AudioDummy,
|
||||
DelayBy5Seconds,
|
||||
spice2x_DelayByNSeconds,
|
||||
@@ -269,6 +272,7 @@ namespace launcher {
|
||||
LovePlusPrinterOutputFormat,
|
||||
LovePlusPrinterJPGQuality,
|
||||
OptionConflictResolution,
|
||||
OtocaCamHook,
|
||||
};
|
||||
|
||||
enum class OptionsCategory {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -142,7 +142,6 @@ namespace ImGui {
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpTooltip(tooltip.c_str());
|
||||
}
|
||||
ImGui::PopID();
|
||||
@@ -159,10 +158,22 @@ namespace ImGui {
|
||||
bool clicked = ImGui::Button("\u00D7"); // multiplication sign (×)
|
||||
ImGui::PopStyleColor(4);
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpTooltip(tooltip.c_str());
|
||||
}
|
||||
ImGui::PopID();
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void InvisibleTableRowSelectable() {
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0);
|
||||
ImGui::Selectable("##row", false,
|
||||
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
|
||||
ImGui::PopStyleColor(3);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, IM_COL32(200, 200, 200, 24));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,5 @@ namespace ImGui {
|
||||
void TextTruncated(const std::string& p_text, float p_truncated_width);
|
||||
bool DeleteButton(const std::string& tooltip);
|
||||
bool ClearButton(const std::string& tooltip);
|
||||
void InvisibleTableRowSelectable();
|
||||
}
|
||||
|
||||
@@ -11,9 +11,19 @@
|
||||
#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"
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
#if !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) || \
|
||||
!defined(IMGUI_DISABLE_DEFAULT_ALLOCATORS) || \
|
||||
!defined(IMGUI_USE_BGRA_PACKED_COLOR) || \
|
||||
@@ -29,6 +39,97 @@ 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;
|
||||
|
||||
constexpr size_t VKEY_MAX = 255;
|
||||
static std::array<bool, VKEY_MAX> g_KeysDown;
|
||||
static std::array<bool, ImGuiMouseButton_COUNT> g_MouseDown;
|
||||
|
||||
static ImGuiKey get_imgui_key(int vkey) {
|
||||
switch (vkey) {
|
||||
case VK_TAB:
|
||||
return ImGuiKey_Tab;
|
||||
case VK_LEFT:
|
||||
return ImGuiKey_LeftArrow;
|
||||
case VK_RIGHT:
|
||||
return ImGuiKey_RightArrow;
|
||||
case VK_UP:
|
||||
return ImGuiKey_UpArrow;
|
||||
case VK_DOWN:
|
||||
return ImGuiKey_DownArrow;
|
||||
case VK_PRIOR:
|
||||
return ImGuiKey_PageUp;
|
||||
case VK_NEXT:
|
||||
return ImGuiKey_PageDown;
|
||||
case VK_HOME:
|
||||
return ImGuiKey_Home;
|
||||
case VK_END:
|
||||
return ImGuiKey_End;
|
||||
case VK_INSERT:
|
||||
return ImGuiKey_Insert;
|
||||
case VK_DELETE:
|
||||
return ImGuiKey_Delete;
|
||||
case VK_BACK:
|
||||
return ImGuiKey_Backspace;
|
||||
case VK_SPACE:
|
||||
return ImGuiKey_Space;
|
||||
case VK_RETURN:
|
||||
return ImGuiKey_Enter;
|
||||
case VK_ESCAPE:
|
||||
return ImGuiKey_Escape;
|
||||
case VK_SHIFT:
|
||||
case VK_LSHIFT:
|
||||
return ImGuiKey_LeftShift;
|
||||
case VK_RSHIFT:
|
||||
return ImGuiKey_RightShift;
|
||||
case VK_CONTROL:
|
||||
case VK_LCONTROL:
|
||||
return ImGuiKey_LeftCtrl;
|
||||
case VK_RCONTROL:
|
||||
return ImGuiKey_RightCtrl;
|
||||
case 'A':
|
||||
case 'a':
|
||||
return ImGuiKey_A;
|
||||
case 'C':
|
||||
case 'c':
|
||||
return ImGuiKey_C;
|
||||
case 'V':
|
||||
case 'v':
|
||||
return ImGuiKey_V;
|
||||
case 'X':
|
||||
case 'x':
|
||||
return ImGuiKey_X;
|
||||
case 'Y':
|
||||
case 'y':
|
||||
return ImGuiKey_Y;
|
||||
case 'Z':
|
||||
case 'z':
|
||||
return ImGuiKey_Z;
|
||||
default:
|
||||
return ImGuiKey_None;
|
||||
}
|
||||
}
|
||||
|
||||
static ImGuiKey get_imgui_mod_key(int vkey) {
|
||||
switch (vkey) {
|
||||
case VK_SHIFT:
|
||||
case VK_LSHIFT:
|
||||
case VK_RSHIFT:
|
||||
return ImGuiMod_Shift;
|
||||
return ImGuiMod_Shift;
|
||||
case VK_CONTROL:
|
||||
case VK_LCONTROL:
|
||||
case VK_RCONTROL:
|
||||
return ImGuiMod_Ctrl;
|
||||
case VK_MENU:
|
||||
case VK_LMENU:
|
||||
case VK_RMENU:
|
||||
return ImGuiMod_Alt;
|
||||
default:
|
||||
return ImGuiMod_None;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||
log_misc("imgui_impl_spice", "init");
|
||||
@@ -56,33 +157,15 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||
io.BackendPlatformName = "imgui_impl_spice";
|
||||
io.ConfigErrorRecoveryEnableTooltip = true;
|
||||
|
||||
// keyboard mapping
|
||||
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
|
||||
io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
|
||||
io.KeyMap[ImGuiKey_Home] = VK_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = VK_END;
|
||||
io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
|
||||
io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
|
||||
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_KeypadEnter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_A] = 'A';
|
||||
io.KeyMap[ImGuiKey_C] = 'C';
|
||||
io.KeyMap[ImGuiKey_V] = 'V';
|
||||
io.KeyMap[ImGuiKey_X] = 'X';
|
||||
io.KeyMap[ImGuiKey_Y] = 'Y';
|
||||
io.KeyMap[ImGuiKey_Z] = 'Z';
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -239,7 +322,7 @@ static void ImGui_ImplSpice_UpdateMousePos() {
|
||||
}
|
||||
|
||||
// delay press
|
||||
io.MouseDown[0] = delay_touch++ >= delay_touch_target && last_touch_id == tp.id;
|
||||
g_MouseDown[ImGuiMouseButton_Left] = delay_touch++ >= delay_touch_target && last_touch_id == tp.id;
|
||||
if (last_touch_id == ~0u) {
|
||||
last_touch_id = tp.id;
|
||||
}
|
||||
@@ -266,38 +349,26 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
g_Time = current_time;
|
||||
|
||||
// remember old state
|
||||
BYTE KeysDownOld[sizeof(io.KeysDown)];
|
||||
for (size_t i = 0; i < sizeof(io.KeysDown); i++) {
|
||||
KeysDownOld[i] = io.KeysDown[i] ? ~0 : 0;
|
||||
std::array<BYTE, VKEY_MAX> KeysDownOld;
|
||||
for (size_t i = 0; i < VKEY_MAX; i++) {
|
||||
KeysDownOld[i] = g_KeysDown[i] ? ~0 : 0;
|
||||
}
|
||||
KeysDownOld[VK_SHIFT] |= KeysDownOld[VK_LSHIFT];
|
||||
KeysDownOld[VK_SHIFT] |= KeysDownOld[VK_RSHIFT];
|
||||
|
||||
const auto MouseDownOld = g_MouseDown;
|
||||
|
||||
// reset keys state
|
||||
io.MouseWheel = 0;
|
||||
io.KeyCtrl = false;
|
||||
io.KeyShift = false;
|
||||
io.KeyAlt = false;
|
||||
io.KeySuper = false;
|
||||
memset(io.KeysDown, false, sizeof(io.KeysDown));
|
||||
memset(io.MouseDown, false, sizeof(io.MouseDown));
|
||||
g_MouseDown.fill(false);
|
||||
g_KeysDown.fill(false);
|
||||
|
||||
// early quit if window not in focus
|
||||
if (!superexit::has_focus()) {
|
||||
if (!superexit::has_focus() || rawinput::OS_WINDOW_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// read keyboard modifiers inputs
|
||||
io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
|
||||
io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||
io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
io.KeySuper = (::GetKeyState(VK_LWIN) & 0x8000) != 0;
|
||||
io.KeySuper |= (::GetKeyState(VK_RWIN) & 0x8000) != 0;
|
||||
|
||||
// apply windows mouse buttons
|
||||
io.MouseDown[0] |= (get_async_primary_mouse()) != 0;
|
||||
io.MouseDown[1] |= (get_async_secondary_mouse()) != 0;
|
||||
io.MouseDown[2] |= (GetAsyncKeyState(VK_MBUTTON)) != 0;
|
||||
g_MouseDown[ImGuiMouseButton_Left] |= (get_async_primary_mouse()) != 0;
|
||||
g_MouseDown[ImGuiMouseButton_Right] |= (get_async_secondary_mouse()) != 0;
|
||||
g_MouseDown[ImGuiMouseButton_Middle] |= (GetAsyncKeyState(VK_MBUTTON)) != 0;
|
||||
|
||||
// read new keys state
|
||||
static long mouse_wheel_last = 0;
|
||||
@@ -312,21 +383,21 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
// mouse button triggers
|
||||
if (GetSystemMetrics(SM_SWAPBUTTON)) {
|
||||
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
|
||||
io.MouseDown[0] = true;
|
||||
g_MouseDown[ImGuiMouseButton_Left] = true;
|
||||
}
|
||||
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
|
||||
io.MouseDown[1] = true;
|
||||
g_MouseDown[ImGuiMouseButton_Right] = true;
|
||||
}
|
||||
} else {
|
||||
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
|
||||
io.MouseDown[0] = true;
|
||||
g_MouseDown[ImGuiMouseButton_Left] = true;
|
||||
}
|
||||
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
|
||||
io.MouseDown[1] = true;
|
||||
g_MouseDown[ImGuiMouseButton_Right] = true;
|
||||
}
|
||||
}
|
||||
if (mouse->key_states[rawinput::MOUSEBTN_MIDDLE]) {
|
||||
io.MouseDown[2] = true;
|
||||
g_MouseDown[ImGuiMouseButton_Middle] = true;
|
||||
}
|
||||
|
||||
// final mouse wheel value should be all devices combined
|
||||
@@ -337,33 +408,11 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
case rawinput::KEYBOARD: {
|
||||
|
||||
// iterate all virtual key codes
|
||||
for (size_t vKey = 0; vKey < 256; vKey++) {
|
||||
|
||||
for (size_t vKey = 0; vKey < VKEY_MAX; vKey++) {
|
||||
// get state (combined from all pages)
|
||||
auto &key_states = device.keyboardInfo->key_states;
|
||||
bool state = false;
|
||||
for (size_t page_index = 0; page_index < 1024; page_index += 256) {
|
||||
state |= key_states[page_index + vKey];
|
||||
}
|
||||
|
||||
// trigger
|
||||
io.KeysDown[vKey] |= state;
|
||||
|
||||
// generate character input, but only if WM_CHAR didn't take over the
|
||||
// functionality
|
||||
if (!overlay::USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT && !KeysDownOld[vKey] && state) {
|
||||
UCHAR buf[2];
|
||||
auto ret = ToAscii(
|
||||
static_cast<UINT>(vKey),
|
||||
0,
|
||||
static_cast<const BYTE *>(KeysDownOld),
|
||||
reinterpret_cast<LPWORD>(buf),
|
||||
0);
|
||||
if (ret > 0) {
|
||||
for (int i = 0; i < ret; i++) {
|
||||
overlay::OVERLAY->input_char(buf[i]);
|
||||
}
|
||||
}
|
||||
g_KeysDown[vKey] |= key_states[page_index + vKey];
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -374,13 +423,81 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
// process keyboard input from all keyboard collapsed into one state (g_KeysDown)
|
||||
for (size_t vKey = 0; vKey < VKEY_MAX; vKey++) {
|
||||
const bool state = g_KeysDown[vKey];
|
||||
const auto imgui_key = get_imgui_key(vKey);
|
||||
const auto changed =
|
||||
(state && !KeysDownOld[vKey]) ||
|
||||
(!state && KeysDownOld[vKey]);
|
||||
|
||||
if (imgui_key != ImGuiKey_None && changed) {
|
||||
io.AddKeyEvent(imgui_key, state);
|
||||
log_debug("imgui_impl_spice", "vkey {:#x} added as navigation event, state: {}", static_cast<uint64_t>(vKey), state);
|
||||
|
||||
// mod key must also be processed separately
|
||||
const auto imgui_mod_key = get_imgui_mod_key(vKey);
|
||||
if (imgui_mod_key != ImGuiMod_None) {
|
||||
io.AddKeyEvent(imgui_mod_key, state);
|
||||
}
|
||||
}
|
||||
|
||||
// generate character input, but only if WM_CHAR didn't take over the functionality
|
||||
// only detecting rising edges here - this means holding a key won't work
|
||||
// (it's better than repeating a character input every frame - cost we pay for providing input
|
||||
// on top of rawinput instead of WM_CHAR)
|
||||
if (!overlay::USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT && !KeysDownOld[vKey] && state) {
|
||||
UCHAR buf[2];
|
||||
auto ret = ToAscii(
|
||||
static_cast<UINT>(vKey),
|
||||
0,
|
||||
static_cast<const BYTE *>(KeysDownOld.data()),
|
||||
reinterpret_cast<LPWORD>(buf),
|
||||
0);
|
||||
if (ret > 0) {
|
||||
for (int i = 0; i < ret; i++) {
|
||||
overlay::OVERLAY->input_char(buf[i]);
|
||||
log_debug("imgui_impl_spice", "vkey {:#x} inputted as character", vKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.AddMouseWheelEvent(0, wheel_diff);
|
||||
|
||||
// update OS mouse position
|
||||
const auto old_mouse_pos = io.MousePos;
|
||||
ImGui_ImplSpice_UpdateMousePos();
|
||||
const auto new_mouse_pos = io.MousePos;
|
||||
|
||||
// update mouse buttons
|
||||
// doing this after ImGui_ImplSpice_UpdateMousePos since it can set g_MouseDown for touch input
|
||||
for (size_t i = 0; i < g_MouseDown.size(); i++) {
|
||||
if (MouseDownOld[i] != g_MouseDown[i]) {
|
||||
io.AddMouseButtonEvent(i, g_MouseDown[i]);
|
||||
log_debug("imgui_impl_spice", "mouse button {} event", g_MouseDown[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ||
|
||||
g_MouseDown[ImGuiMouseButton_Left] || g_MouseDown[ImGuiMouseButton_Right] || g_MouseDown[ImGuiMouseButton_Middle]) {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -501,7 +501,7 @@ void paint_draw_cmd(
|
||||
const SwOptions& options,
|
||||
Stats* stats)
|
||||
{
|
||||
const auto texture = reinterpret_cast<const Texture*>(pcmd.TextureId);
|
||||
const auto texture = reinterpret_cast<const Texture*>(pcmd.GetTexID());
|
||||
const auto offset = pcmd.IdxOffset;
|
||||
|
||||
// ImGui uses the first pixel for "white".
|
||||
@@ -678,7 +678,7 @@ void bind_imgui_painting()
|
||||
int font_width, font_height;
|
||||
io.Fonts->GetTexDataAsAlpha8(&tex_data, &font_width, &font_height);
|
||||
const auto texture = new Texture{tex_data, font_width, font_height};
|
||||
io.Fonts->TexID = reinterpret_cast<ImTextureID>(texture);
|
||||
io.Fonts->SetTexID(reinterpret_cast<ImTextureID>(texture));
|
||||
}
|
||||
|
||||
static Stats s_stats; // TODO: pass as an argument?
|
||||
@@ -700,7 +700,7 @@ void paint_imgui(uint32_t* pixels, int width_pixels, int height_pixels, const Sw
|
||||
void unbind_imgui_painting()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
delete reinterpret_cast<Texture*>(io.Fonts->TexID);
|
||||
delete reinterpret_cast<Texture*>(io.Fonts->TexID.GetTexID());
|
||||
io.Fonts = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -185,74 +185,76 @@ void overlay::SpiceOverlay::init() {
|
||||
if (UI_SCALE_PERCENT.has_value()) {
|
||||
const auto scale = static_cast<float>(UI_SCALE_PERCENT.value()) / 100.f;
|
||||
ImGui::GetStyle().ScaleAllSizes(scale);
|
||||
ImGui::GetIO().FontGlobalScale = scale;
|
||||
ImGui::GetStyle().FontScaleMain = 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.35f, 0.26f, 0.26f, 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();
|
||||
@@ -281,7 +283,7 @@ void overlay::SpiceOverlay::init() {
|
||||
io.FontAllowUserScaling = true;
|
||||
|
||||
// add default font
|
||||
io.Fonts->AddFontDefault();
|
||||
io.Fonts->AddFontDefaultBitmap();
|
||||
|
||||
// add fallback fonts for missing glyph ranges
|
||||
ImFontConfig config {};
|
||||
|
||||
+2795
-222
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:
|
||||
|
||||
@@ -58,13 +69,60 @@ namespace overlay::windows {
|
||||
int lights_devices_selected = -1;
|
||||
int lights_devices_control_selected = -1;
|
||||
|
||||
// keypads tab
|
||||
// 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;
|
||||
|
||||
// cards tab
|
||||
int keypads_selected[2] {};
|
||||
char keypads_card_path[2][1024] {};
|
||||
std::thread *keypads_card_select = nullptr;
|
||||
bool keypads_card_select_done = false;
|
||||
ImGui::FileBrowser keypads_card_select_browser[2];
|
||||
char keypads_card_number[2][18] {};
|
||||
bool keypads_card_override_valid[2] = { false, false };
|
||||
bool keypads_card_file_contents_valid[2] = { false, false };
|
||||
bool keypads_card_overwrite_confirmed[2];
|
||||
|
||||
// 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;
|
||||
@@ -76,6 +134,12 @@ 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;
|
||||
|
||||
std::pair<std::string, int> analog_as_button_warning_show_next_frame = { "", 0 };
|
||||
|
||||
void build_buttons(const std::string &name, std::vector<Button> *buttons, int min = 0, int max = -1);
|
||||
void build_button(
|
||||
const std::string &name,
|
||||
@@ -93,17 +157,38 @@ namespace overlay::windows {
|
||||
Button *button,
|
||||
const float button_velocity,
|
||||
const int alt_index);
|
||||
void clear_button(Button *button, 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();
|
||||
@@ -115,12 +200,13 @@ namespace overlay::windows {
|
||||
|
||||
void set_alternating_row_colors(const int row_index);
|
||||
|
||||
bool validate_ea_card(char card_number[16]);
|
||||
|
||||
public:
|
||||
Config(SpiceOverlay *overlay);
|
||||
~Config() override;
|
||||
|
||||
void read_card(int player = -1);
|
||||
void write_card(int player);
|
||||
void build_content() override;
|
||||
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "cfg/button.h"
|
||||
#include "cfg/light.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
|
||||
@@ -40,13 +40,17 @@ namespace overlay::windows {
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushID("P1");
|
||||
this->draw_buttons(0);
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushID("P2");
|
||||
this->draw_buttons(1);
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void DDRIOPanel::draw_buttons(const int 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;
|
||||
@@ -834,9 +844,13 @@ namespace overlay::windows {
|
||||
ImGui::TextUnformatted(patch_checked ? "ON" : "off");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
// row hover detection (invisible selectable that spans entire row)
|
||||
ImGui::InvisibleTableRowSelectable();
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
|
||||
if (patches_shown == 0) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
@@ -981,7 +995,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 +1222,7 @@ namespace overlay::windows {
|
||||
identifier,
|
||||
time);
|
||||
}
|
||||
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@@ -2000,7 +2014,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 +2169,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 +2207,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 +2756,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() {
|
||||
@@ -172,7 +176,7 @@ void rawinput::RawInputManager::input_hwnd_destroy() {
|
||||
|
||||
void rawinput::RawInputManager::devices_reload() {
|
||||
this->devices_destruct();
|
||||
log_info("rawinput", "reloading devices");
|
||||
log_info("rawinput", "reloading devices...");
|
||||
|
||||
// scan for devices
|
||||
this->devices_scan_rawinput();
|
||||
@@ -195,6 +199,7 @@ void rawinput::RawInputManager::devices_reload() {
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_scan_rawinput(const std::string &device_name) {
|
||||
log_misc("rawinput", "scan rawinput devices...");
|
||||
|
||||
// get number of devices
|
||||
UINT device_no = 0;
|
||||
@@ -232,6 +237,8 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
return;
|
||||
}
|
||||
|
||||
log_misc("rawinput", "found rawinput device: {}", device_name);
|
||||
|
||||
// extract information out of name
|
||||
auto device_info = rawinput::RawInputManager::get_device_info(device_name);
|
||||
auto device_description = rawinput::RawInputManager::rawinput_get_device_description(device_info, device_name);
|
||||
@@ -401,6 +408,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 +440,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 +762,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);
|
||||
@@ -803,6 +813,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_scan_midi() {
|
||||
log_misc("rawinput", "scan MIDI devices...");
|
||||
|
||||
// add midi devices
|
||||
auto midi_device_count = midiInGetNumDevs();
|
||||
@@ -814,6 +825,9 @@ void rawinput::RawInputManager::devices_scan_midi() {
|
||||
continue;
|
||||
}
|
||||
|
||||
log_misc("rawinput", "found MIDI device: id {}, name {}, mid {}, pid {}",
|
||||
midi_device_id, midi_device_caps.szPname, midi_device_caps.wMid, midi_device_caps.wPid);
|
||||
|
||||
// open device
|
||||
HMIDIIN midi_device_handle;
|
||||
if (midiInOpen(&midi_device_handle,
|
||||
@@ -913,6 +927,7 @@ void rawinput::RawInputManager::devices_scan_midi() {
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_scan_piuio() {
|
||||
log_misc("rawinput", "scan PIUIO devices...");
|
||||
|
||||
// add device to vector first so pointer is valid
|
||||
auto *new_piuio_device = new Device();
|
||||
@@ -943,6 +958,8 @@ void rawinput::RawInputManager::devices_scan_piuio() {
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_scan_smxstage() {
|
||||
log_misc("rawinput", "scan SMX Stage devices...");
|
||||
|
||||
auto *new_smxstage_device = new Device();
|
||||
new_smxstage_device->type = SMX_STAGE;
|
||||
new_smxstage_device->name = "smxstage";
|
||||
@@ -967,6 +984,8 @@ void rawinput::RawInputManager::devices_scan_smxstage() {
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_scan_smxdedicab() {
|
||||
log_misc("rawinput", "scan SMX Dedicated Cabinet devices...");
|
||||
|
||||
auto *new_smxdedicab_device = new Device();
|
||||
new_smxdedicab_device->type = SMX_DEDICAB;
|
||||
new_smxdedicab_device->name = "smxdedicab";
|
||||
@@ -1211,6 +1230,8 @@ std::string rawinput::RawInputManager::rawinput_get_device_description(const raw
|
||||
void rawinput::RawInputManager::sextet_register(const std::string &port_name, const std::string &alias,
|
||||
bool warn) {
|
||||
|
||||
log_misc("rawinput", "checking for sextet-stream device on {}...", port_name);
|
||||
|
||||
// check for any sextet-stream devices
|
||||
Device device {};
|
||||
device.type = SEXTET_OUTPUT;
|
||||
@@ -1251,12 +1272,14 @@ void rawinput::RawInputManager::devices_remove(const std::string &name) {
|
||||
}
|
||||
|
||||
void rawinput::RawInputManager::devices_register() {
|
||||
|
||||
|
||||
// check input window
|
||||
if (!this->input_hwnd) {
|
||||
log_warning("rawinput", "trying to register devices without input window");
|
||||
return;
|
||||
}
|
||||
|
||||
log_misc("rawinput", "registering raw input devices...");
|
||||
|
||||
// register keyboard
|
||||
RAWINPUTDEVICE keyboard_device{};
|
||||
@@ -1709,53 +1732,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);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
PRODUCTVERSION 1,0,0,0
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "SpiceTools"
|
||||
VALUE "FileDescription", "SpiceTools DLL Stub"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "ProductName", "SpiceTools DLL Stub"
|
||||
VALUE "ProductVersion", "1.0.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0409, 1252
|
||||
END
|
||||
END
|
||||
@@ -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(
|
||||
|
||||
@@ -9,6 +9,16 @@
|
||||
#include "peb.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/dependencies.h"
|
||||
#include "util/unique_plain_ptr.h"
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
std::filesystem::path libutils::module_file_name(HMODULE module) {
|
||||
std::wstring buf;
|
||||
@@ -361,11 +371,106 @@ void libutils::check_duplicate_dlls() {
|
||||
void libutils::warn_if_dll_exists(const std::string &file_name) {
|
||||
if (fileutils::file_exists(MODULE_PATH / file_name)) {
|
||||
log_info("libutils", "found user-supplied {} in modules directory", file_name);
|
||||
libutils::print_dll_info(MODULE_PATH / file_name);
|
||||
return;
|
||||
}
|
||||
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
|
||||
if (fileutils::file_exists(spice_bin_path / file_name)) {
|
||||
log_info("libutils", "found user-supplied {} next to spice executable path", file_name);
|
||||
libutils::print_dll_info(spice_bin_path / file_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void libutils::print_dll_info(std::filesystem::path filename) {
|
||||
DWORD handle;
|
||||
const auto size = GetFileVersionInfoSizeA(filename.string().c_str(), &handle);
|
||||
if (size == 0) {
|
||||
log_debug(
|
||||
"libutils",
|
||||
"GetFileVersionInfoSizeA failed for {}: {}",
|
||||
filename.filename().string(),
|
||||
get_last_error_string());
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = util::make_unique_plain<VOID>(size);
|
||||
if (!GetFileVersionInfoA(filename.string().c_str(), handle, size, data.get())) {
|
||||
log_debug(
|
||||
"libutils",
|
||||
"GetFileVersionInfoA failed for {}: {}",
|
||||
filename.filename().string(),
|
||||
get_last_error_string());
|
||||
return;
|
||||
}
|
||||
|
||||
struct LANGANDCODEPAGE {
|
||||
WORD wLanguage;
|
||||
WORD wCodePage;
|
||||
} *lpTranslate = nullptr;
|
||||
|
||||
UINT cbTranslate = 0;
|
||||
if (!VerQueryValueA(data.get(), "\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate)) {
|
||||
log_debug(
|
||||
"libutils",
|
||||
"VerQueryValueA failed for {}: {}",
|
||||
filename.filename().string(),
|
||||
get_last_error_string());
|
||||
return;
|
||||
}
|
||||
if (cbTranslate == 0 || lpTranslate == nullptr) {
|
||||
log_debug(
|
||||
"libutils",
|
||||
"VerQueryValueA returned invalid results for {}",
|
||||
filename.filename().string());
|
||||
return;
|
||||
}
|
||||
|
||||
// pick language - prefer English
|
||||
WORD lang = 0, codepage = 0;
|
||||
for (UINT i = 0; i < cbTranslate / sizeof(*lpTranslate); i++) {
|
||||
if (lpTranslate[i].wLanguage == 0x0409) { // en-us
|
||||
lang = lpTranslate[i].wLanguage;
|
||||
codepage = lpTranslate[i].wCodePage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lang == 0) {
|
||||
lang = lpTranslate[0].wLanguage;
|
||||
codepage = lpTranslate[0].wCodePage;
|
||||
}
|
||||
|
||||
// StringFileInfo helper
|
||||
auto query_string = [&](const char* key) -> std::string {
|
||||
std::string subBlock = fmt::format(
|
||||
"\\StringFileInfo\\{:04x}{:04x}\\{}",
|
||||
lang, codepage, key
|
||||
);
|
||||
|
||||
char* value = nullptr;
|
||||
UINT size_out = 0;
|
||||
|
||||
if (VerQueryValueA(data.get(), subBlock.c_str(), (LPVOID*)&value, &size_out) && value) {
|
||||
return value;
|
||||
}
|
||||
log_debug(
|
||||
"libutils",
|
||||
"VerQueryValueA({}) failed for {}: {}",
|
||||
subBlock,
|
||||
filename.filename().string(),
|
||||
get_last_error_string());
|
||||
return "";
|
||||
};
|
||||
|
||||
const auto company_name = query_string("CompanyName");
|
||||
const auto product_name = query_string("ProductName");
|
||||
const auto version_str = query_string("FileVersion");
|
||||
|
||||
log_info(
|
||||
"libutils",
|
||||
"DLL info for {}: CompanyName = {}, ProductName = {}, Version = {}",
|
||||
filename.filename().string(),
|
||||
company_name.empty() ? "?" : company_name,
|
||||
product_name.empty() ? "?" : product_name,
|
||||
version_str.empty() ? "?" : version_str);
|
||||
}
|
||||
|
||||
@@ -77,4 +77,6 @@ namespace libutils {
|
||||
intptr_t rva2offset(const std::filesystem::path &path, intptr_t rva);
|
||||
intptr_t offset2rva(IMAGE_NT_HEADERS *nt_headers, intptr_t offset);
|
||||
intptr_t offset2rva(const std::filesystem::path &path, intptr_t offset);
|
||||
|
||||
void print_dll_info(std::filesystem::path filename);
|
||||
}
|
||||
|
||||
@@ -62,9 +62,9 @@ void show_popup_for_crash() {
|
||||
text += "Game has crashed.\n\n";
|
||||
text += "Check log.txt and look for error messages near the end of file.\n\n";
|
||||
text += "Unsure what to do next?\n";
|
||||
text += " * update spice2x to the latest version\n";
|
||||
text += " * check the FAQ on spice2x github wiki\n";
|
||||
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
|
||||
text += " * Update spice2x to the latest version\n";
|
||||
text += " * Check the FAQ on spice2x github wiki\n";
|
||||
text += " * Do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
|
||||
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
|
||||
show_popup(text);
|
||||
}
|
||||
@@ -76,9 +76,9 @@ void show_popup_for_fatal_error(std::string message) {
|
||||
text += message;
|
||||
text += "----------------------------------------------------------\n\n";
|
||||
text += "Unsure what to do next?\n";
|
||||
text += " * update spice2x to the latest version\n";
|
||||
text += " * check the FAQ on spice2x github wiki\n";
|
||||
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
|
||||
text += " * Update spice2x to the latest version\n";
|
||||
text += " * Check the FAQ on spice2x github wiki\n";
|
||||
text += " * Do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
|
||||
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
|
||||
show_popup(text);
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
#include "sysutils.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#if 0
|
||||
#define log_dbug(module, format_str, ...) logger::push( \
|
||||
@@ -222,6 +225,21 @@ namespace sysutils {
|
||||
log_dbug("gpuinfo", "{} {} flags : 0x{:x}", prefix.c_str(), index, adapter->StateFlags);
|
||||
|
||||
if (!is_monitor) {
|
||||
// get extended info for better friendly name of monitors
|
||||
const auto &monitors = enumerate_monitors();
|
||||
for (const auto& monitor : monitors) {
|
||||
if (monitor.display_name == adapter->DeviceName) {
|
||||
const auto friendly = ws2s(monitor.friendly_name.c_str());
|
||||
log_misc(
|
||||
"gpuinfo", "{} {} friendly name : {}",
|
||||
prefix.c_str(),
|
||||
index,
|
||||
friendly);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// resolution, refresh rate
|
||||
DEVMODEA devmode = {};
|
||||
devmode.dmSize = sizeof(devmode);
|
||||
if (EnumDisplaySettingsA(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) {
|
||||
@@ -236,11 +254,13 @@ namespace sysutils {
|
||||
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
|
||||
}
|
||||
|
||||
// primary?
|
||||
log_misc(
|
||||
"gpuinfo", "{} {} is primary : {}",
|
||||
prefix.c_str(),
|
||||
index,
|
||||
(adapter->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,4 +318,92 @@ namespace sysutils {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<MonitorEntry> enumerate_monitors_internal() {
|
||||
// for WinXP, since these are Vista+ or 7+ APIs
|
||||
const auto user32 = LoadLibraryA("user32.dll");
|
||||
if (!user32) {
|
||||
log_warning("graphics", "can't find user32.dll???");
|
||||
return {};
|
||||
}
|
||||
const auto GetDisplayConfigBufferSizes_addr =
|
||||
reinterpret_cast<decltype(GetDisplayConfigBufferSizes) *>(
|
||||
GetProcAddress(user32, "GetDisplayConfigBufferSizes"));
|
||||
const auto QueryDisplayConfig_addr =
|
||||
reinterpret_cast<decltype(QueryDisplayConfig) *>(
|
||||
GetProcAddress(user32, "QueryDisplayConfig"));
|
||||
const auto DisplayConfigGetDeviceInfo_addr =
|
||||
reinterpret_cast<decltype(DisplayConfigGetDeviceInfo) *>(
|
||||
GetProcAddress(user32, "DisplayConfigGetDeviceInfo"));
|
||||
if (GetDisplayConfigBufferSizes_addr == nullptr ||
|
||||
QueryDisplayConfig_addr == nullptr ||
|
||||
DisplayConfigGetDeviceInfo_addr == nullptr) {
|
||||
log_warning("sysutils", "OS does not support display config APIs");
|
||||
return {};
|
||||
}
|
||||
|
||||
UINT32 path_count = 0;
|
||||
UINT32 mode_count = 0;
|
||||
auto status = GetDisplayConfigBufferSizes_addr(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
log_warning("sysutils", "GetDisplayConfigBufferSizes failed: {}", status);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<DISPLAYCONFIG_PATH_INFO> paths(path_count);
|
||||
std::vector<DISPLAYCONFIG_MODE_INFO> modes(mode_count);
|
||||
status = QueryDisplayConfig_addr(
|
||||
QDC_ONLY_ACTIVE_PATHS,
|
||||
&path_count,
|
||||
paths.data(),
|
||||
&mode_count,
|
||||
modes.data(),
|
||||
nullptr);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
log_warning("sysutils", "QueryDisplayConfig failed: {}", status);
|
||||
return {};
|
||||
}
|
||||
|
||||
// shrink to actual returned items
|
||||
paths.resize(path_count);
|
||||
modes.resize(mode_count);
|
||||
|
||||
std::vector<MonitorEntry> result;
|
||||
for (const auto& path : paths) {
|
||||
MonitorEntry entry;
|
||||
|
||||
// device ID (\\.\DISPLAYn)
|
||||
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
|
||||
source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
|
||||
source_name.header.size = sizeof(source_name);
|
||||
source_name.header.adapterId = path.sourceInfo.adapterId;
|
||||
source_name.header.id = path.sourceInfo.id;
|
||||
if (DisplayConfigGetDeviceInfo_addr(&source_name.header) != ERROR_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
entry.display_name = ws2s(std::wstring(source_name.viewGdiDeviceName));
|
||||
|
||||
// friendly name
|
||||
DISPLAYCONFIG_TARGET_DEVICE_NAME target_name = {};
|
||||
target_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
|
||||
target_name.header.size = sizeof(target_name);
|
||||
target_name.header.adapterId = path.targetInfo.adapterId;
|
||||
target_name.header.id = path.targetInfo.id;
|
||||
if (DisplayConfigGetDeviceInfo_addr(&target_name.header) == ERROR_SUCCESS) {
|
||||
entry.friendly_name = target_name.monitorFriendlyDeviceName;
|
||||
} else {
|
||||
entry.friendly_name = L"(unknown)";
|
||||
}
|
||||
|
||||
// done
|
||||
result.emplace_back(entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::vector<MonitorEntry>& enumerate_monitors() {
|
||||
static const std::vector<MonitorEntry> monitors = enumerate_monitors_internal();
|
||||
return monitors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace sysutils {
|
||||
void print_smbios();
|
||||
void print_gpus();
|
||||
void print_os();
|
||||
}
|
||||
|
||||
struct MonitorEntry {
|
||||
std::string display_name; // \\.\DISPLAY1
|
||||
std::wstring friendly_name; // Dell S2204T; wstring so that it can be converted to utf8 or string
|
||||
};
|
||||
|
||||
const std::vector<MonitorEntry> &enumerate_monitors();
|
||||
}
|
||||
@@ -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