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.
|
* Indents are four spaces.
|
||||||
* Try to keep under 100 characters per line. This includes comments and string literals.
|
* Try to keep under 100 characters per line. This includes comments and string literals.
|
||||||
* Avoid making unnecessary formatting or whitespace changes in a PR; e.g., watch out for text editor automatically stripping trailing whitespace. It pollutes the diff and slows down code review.
|
* Avoid making unnecessary formatting or whitespace changes in a PR; e.g., watch out for text editor automatically stripping trailing whitespace. It pollutes the diff and slows down code review.
|
||||||
|
* For signed/unsigned integer types, prefer `cstdint` types; e.g., use `int32_t`, `uint64_t` and not `int` and `unsigned long long`.
|
||||||
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
||||||
|
|
||||||
OK:
|
OK:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
> Feel free to remove this section after you have read it.
|
> Feel free to remove this section after you have read it.
|
||||||
|
|
||||||
## Link to GitHub Issue, if one exists
|
## Link to GitHub Issue or related Pull Request, if one exists
|
||||||
#0
|
#0
|
||||||
|
|
||||||
## Description of change
|
## Description of change
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|||||||
# RapidJSON does this
|
# RapidJSON does this
|
||||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||||
|
|
||||||
|
add_compile_options("-Wno-nontrivial-memcall") # imgui, rapidjson
|
||||||
|
add_compile_options("-Wno-deprecated-builtins") # robin_hood
|
||||||
|
add_compile_options("-Wno-deprecated-declarations") # rapidjson
|
||||||
|
add_compile_options("-Wno-inconsistent-missing-override") # headsocket
|
||||||
|
add_compile_options("-Wno-microsoft-exception-spec") # gcc/clang have different noexcept specifiers on d3d9 COM interfaces
|
||||||
|
|
||||||
# warnings
|
# warnings
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
||||||
|
|
||||||
@@ -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/icon.rc PROPERTIES LANGUAGE RC)
|
||||||
set_source_files_properties(cfg/Win32D.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(build/manifest64.rc PROPERTIES LANGUAGE RC)
|
||||||
|
set_source_files_properties(stubs/manifest.rc PROPERTIES LANGUAGE RC)
|
||||||
|
|
||||||
# sources
|
# sources
|
||||||
#########
|
#########
|
||||||
@@ -346,6 +353,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
cfg/configurator.cpp
|
cfg/configurator.cpp
|
||||||
cfg/configurator_wnd.cpp
|
cfg/configurator_wnd.cpp
|
||||||
cfg/screen_resize.cpp
|
cfg/screen_resize.cpp
|
||||||
|
cfg/controller_presets.cpp
|
||||||
|
|
||||||
# easrv
|
# easrv
|
||||||
easrv/easrv.cpp
|
easrv/easrv.cpp
|
||||||
@@ -559,6 +567,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
overlay/windows/screen_resize.cpp
|
overlay/windows/screen_resize.cpp
|
||||||
overlay/windows/sdvx_sub.cpp
|
overlay/windows/sdvx_sub.cpp
|
||||||
overlay/windows/config.cpp
|
overlay/windows/config.cpp
|
||||||
|
overlay/windows/controller_presets.cpp
|
||||||
overlay/windows/control.cpp
|
overlay/windows/control.cpp
|
||||||
overlay/windows/eadev.cpp
|
overlay/windows/eadev.cpp
|
||||||
overlay/windows/fps.cpp
|
overlay/windows/fps.cpp
|
||||||
@@ -805,7 +814,8 @@ endif()
|
|||||||
|
|
||||||
# nvcuda.dll
|
# nvcuda.dll
|
||||||
set(SOURCE_FILES stubs/nvcuda.cpp)
|
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 PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
|
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
|
||||||
|
|
||||||
@@ -815,7 +825,8 @@ endif()
|
|||||||
|
|
||||||
# nvcuvid.dll
|
# nvcuvid.dll
|
||||||
set(SOURCE_FILES stubs/nvcuvid.cpp)
|
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 PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
|
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
|
||||||
|
|
||||||
@@ -825,7 +836,8 @@ endif()
|
|||||||
|
|
||||||
# nvEncodeAPI64.dll
|
# nvEncodeAPI64.dll
|
||||||
set(SOURCE_FILES stubs/nvEncodeAPI64.cpp)
|
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 PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
|
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
|
||||||
|
|
||||||
@@ -835,7 +847,8 @@ endif()
|
|||||||
|
|
||||||
# cpusbxpkm.dll (32 bit)
|
# cpusbxpkm.dll (32 bit)
|
||||||
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
|
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 PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
|
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) {
|
void DRS::touch_set(Request &req, Response &res) {
|
||||||
|
|
||||||
// get all touch points
|
// get all touch points
|
||||||
games::drs::drs_touch_t touches[16];
|
auto touches = std::make_unique<games::drs::drs_touch_t[]>(req.params.Size());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (Value ¶m : req.params.GetArray()) {
|
for (Value ¶m : req.params.GetArray()) {
|
||||||
|
|
||||||
@@ -86,6 +86,6 @@ namespace api::modules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// apply touch points
|
// apply touch points
|
||||||
games::drs::fire_touches(touches, i);
|
games::drs::fire_touches(touches.get(), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-2
@@ -217,7 +217,23 @@ namespace avs {
|
|||||||
ea3_config_name = "prop/eamuse-config.xml";
|
ea3_config_name = "prop/eamuse-config.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (avs::core::file_exists(ea3_config_name)) {
|
||||||
log_info("avs-ea3", "booting (using {})", 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
|
// remember new config path
|
||||||
CFG_PATH = ea3_config_name;
|
CFG_PATH = ea3_config_name;
|
||||||
@@ -554,9 +570,17 @@ namespace avs {
|
|||||||
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
||||||
|
|
||||||
// call the game init
|
// call the game init
|
||||||
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)) {
|
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
|
// 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
|
# settings
|
||||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
||||||
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
||||||
TOOLCHAIN_32="/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"
|
TOOLCHAIN_32="${TOOLCHAIN_32:-"/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"}"
|
||||||
TOOLCHAIN_64="/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"
|
TOOLCHAIN_64="${TOOLCHAIN_64:-"/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"}"
|
||||||
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
||||||
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
||||||
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ IDR_LICENSES RCDATA "../licenses.txt"
|
|||||||
IDR_PATCHES RCDATA "../build/patches.json"
|
IDR_PATCHES RCDATA "../build/patches.json"
|
||||||
IDR_README RCDATA "../readme.txt"
|
IDR_README RCDATA "../readme.txt"
|
||||||
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
||||||
|
IDR_CONTROLLER_PRESETS_BUILTIN RCDATA "../build/controller_presets.xml"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ public:
|
|||||||
setMultiplier(1);
|
setMultiplier(1);
|
||||||
setRelativeMode(false);
|
setRelativeMode(false);
|
||||||
setDelayBufferDepth(0);
|
setDelayBufferDepth(0);
|
||||||
|
setLastState(0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void clearBindings() {
|
inline void clearBindings() {
|
||||||
|
|||||||
+44
-1
@@ -37,7 +37,11 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
|||||||
for (auto &bt : buttons) {
|
for (auto &bt : buttons) {
|
||||||
if (name == bt.getName()) {
|
if (name == bt.getName()) {
|
||||||
button_found = true;
|
button_found = true;
|
||||||
sorted.push_back(bt);
|
Button button_new = bt;
|
||||||
|
if (vkey_defaults) {
|
||||||
|
button_new.setVKeyDefault(vkey_defaults->at(index));
|
||||||
|
}
|
||||||
|
sorted.push_back(button_new);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,6 +51,7 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
|||||||
|
|
||||||
if (vkey_defaults) {
|
if (vkey_defaults) {
|
||||||
button.setVKey(vkey_defaults->at(index));
|
button.setVKey(vkey_defaults->at(index));
|
||||||
|
button.setVKeyDefault(vkey_defaults->at(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,6 +866,44 @@ std::vector<Light> GameAPI::Lights::sortLights(
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<Light> sortLightsWithCategoryInternal(
|
||||||
|
const std::vector<Light> &lights,
|
||||||
|
const std::initializer_list<GameAPI::Lights::LightAndCategory> list)
|
||||||
|
{
|
||||||
|
std::vector<Light> sorted;
|
||||||
|
|
||||||
|
bool light_found;
|
||||||
|
for (auto &name : list) {
|
||||||
|
light_found = false;
|
||||||
|
|
||||||
|
for (auto &light : lights) {
|
||||||
|
if (name.light_name == light.getName()) {
|
||||||
|
light_found = true;
|
||||||
|
Light light_new = light;
|
||||||
|
light_new.setCategory(name.category_name);
|
||||||
|
sorted.push_back(light_new);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!light_found) {
|
||||||
|
sorted.emplace_back(name.light_name, name.category_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameAPI::Lights::sortLightsWithCategory(
|
||||||
|
std::vector<Light> *lights,
|
||||||
|
const std::initializer_list<LightAndCategory> list) {
|
||||||
|
|
||||||
|
if (lights) {
|
||||||
|
*lights = sortLightsWithCategoryInternal(*lights, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
|
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
|
||||||
|
|
||||||
// check device
|
// check device
|
||||||
|
|||||||
+15
-21
@@ -6,24 +6,20 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "option.h"
|
#include "option.h"
|
||||||
|
|
||||||
|
#include "button.h"
|
||||||
|
#include "analog.h"
|
||||||
|
#include "light.h"
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
struct Device;
|
struct Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Button;
|
|
||||||
class Analog;
|
|
||||||
class Light;
|
|
||||||
class Game;
|
class Game;
|
||||||
class Option;
|
class Option;
|
||||||
|
|
||||||
namespace GameAPI {
|
namespace GameAPI {
|
||||||
namespace Buttons {
|
namespace Buttons {
|
||||||
enum State {
|
|
||||||
BUTTON_PRESSED = true,
|
|
||||||
BUTTON_NOT_PRESSED = false
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the config and returns the buttons set by the user.
|
* Parses the config and returns the buttons set by the user.
|
||||||
*
|
*
|
||||||
@@ -113,21 +109,13 @@ namespace GameAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace Lights {
|
namespace Lights {
|
||||||
|
|
||||||
std::vector<Light> getLights(const std::string &game_name);
|
std::vector<Light> getLights(const std::string &game_name);
|
||||||
|
|
||||||
std::vector<Light> sortLights(
|
std::vector<Light> sortLights(
|
||||||
const std::vector<Light> &lights,
|
const std::vector<Light> &lights,
|
||||||
const std::vector<std::string> &light_names);
|
const std::vector<std::string> &light_names);
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void sortLights(std::vector<Light> *lights, T t) {
|
|
||||||
const std::vector<std::string> light_names { t };
|
|
||||||
|
|
||||||
if (lights) {
|
|
||||||
*lights = GameAPI::Lights::sortLights(*lights, light_names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename... Rest>
|
template<typename T, typename... Rest>
|
||||||
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
|
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
|
||||||
const std::vector<std::string> light_names { t, rest... };
|
const std::vector<std::string> light_names { t, rest... };
|
||||||
@@ -137,6 +125,16 @@ namespace GameAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LightAndCategory {
|
||||||
|
const std::string category_name;
|
||||||
|
const std::string light_name;
|
||||||
|
|
||||||
|
LightAndCategory(std::string category_name, std::string light_name) :
|
||||||
|
category_name(std::move(category_name)), light_name(std::move(light_name)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void sortLightsWithCategory(std::vector<Light> *lights, const std::initializer_list<LightAndCategory> list);
|
||||||
|
|
||||||
void writeLight(rawinput::Device *device, int index, float value);
|
void writeLight(rawinput::Device *device, int index, float value);
|
||||||
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
||||||
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
||||||
@@ -152,7 +150,3 @@ namespace GameAPI {
|
|||||||
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
|
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "button.h"
|
|
||||||
#include "analog.h"
|
|
||||||
#include "light.h"
|
|
||||||
|
|||||||
+19
-17
@@ -44,7 +44,7 @@ std::string Button::getVKeyString() {
|
|||||||
case 0x0C:
|
case 0x0C:
|
||||||
return "Clear";
|
return "Clear";
|
||||||
case 0x0D:
|
case 0x0D:
|
||||||
return "Enter";
|
return "Enter/Return";
|
||||||
case 0x10:
|
case 0x10:
|
||||||
return "Shift";
|
return "Shift";
|
||||||
case 0x11:
|
case 0x11:
|
||||||
@@ -183,37 +183,37 @@ std::string Button::getVKeyString() {
|
|||||||
case 0x5D:
|
case 0x5D:
|
||||||
return "Apps";
|
return "Apps";
|
||||||
case 0x60:
|
case 0x60:
|
||||||
return "Num 0";
|
return "Numpad 0";
|
||||||
case 0x61:
|
case 0x61:
|
||||||
return "Num 1";
|
return "Numpad 1";
|
||||||
case 0x62:
|
case 0x62:
|
||||||
return "Num 2";
|
return "Numpad 2";
|
||||||
case 0x63:
|
case 0x63:
|
||||||
return "Num 3";
|
return "Numpad 3";
|
||||||
case 0x64:
|
case 0x64:
|
||||||
return "Num 4";
|
return "Numpad 4";
|
||||||
case 0x65:
|
case 0x65:
|
||||||
return "Num 5";
|
return "Numpad 5";
|
||||||
case 0x66:
|
case 0x66:
|
||||||
return "Num 6";
|
return "Numpad 6";
|
||||||
case 0x67:
|
case 0x67:
|
||||||
return "Num 7";
|
return "Numpad 7";
|
||||||
case 0x68:
|
case 0x68:
|
||||||
return "Num 8";
|
return "Numpad 8";
|
||||||
case 0x69:
|
case 0x69:
|
||||||
return "Num 9";
|
return "Numpad 9";
|
||||||
case 0x6A:
|
case 0x6A:
|
||||||
return "*";
|
return "Numpad *";
|
||||||
case 0x6B:
|
case 0x6B:
|
||||||
return "+";
|
return "Numpad +";
|
||||||
case 0x6C:
|
case 0x6C:
|
||||||
return "Seperator";
|
return "Numpad Seperator";
|
||||||
case 0x6D:
|
case 0x6D:
|
||||||
return "-";
|
return "Numpad -";
|
||||||
case 0x6E:
|
case 0x6E:
|
||||||
return ".";
|
return "Numpad .";
|
||||||
case 0x6F:
|
case 0x6F:
|
||||||
return "/";
|
return "Numpad /";
|
||||||
case 0x70:
|
case 0x70:
|
||||||
return "F1";
|
return "F1";
|
||||||
case 0x71:
|
case 0x71:
|
||||||
@@ -278,6 +278,8 @@ std::string Button::getVKeyString() {
|
|||||||
return "Left Alt";
|
return "Left Alt";
|
||||||
case 0xA5:
|
case 0xA5:
|
||||||
return "Right Alt";
|
return "Right Alt";
|
||||||
|
case INVALID_VKEY:
|
||||||
|
return "Invalid";
|
||||||
default:
|
default:
|
||||||
|
|
||||||
// check win API
|
// check win API
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api.h"
|
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameAPI {
|
||||||
|
namespace Buttons {
|
||||||
|
enum State {
|
||||||
|
BUTTON_PRESSED = true,
|
||||||
|
BUTTON_NOT_PRESSED = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ButtonAnalogType {
|
enum ButtonAnalogType {
|
||||||
BAT_NONE = 0,
|
BAT_NONE = 0,
|
||||||
BAT_POSITIVE = 1,
|
BAT_POSITIVE = 1,
|
||||||
@@ -41,6 +49,10 @@ private:
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string device_identifier = "";
|
std::string device_identifier = "";
|
||||||
unsigned short vKey = INVALID_VKEY;
|
unsigned short vKey = INVALID_VKEY;
|
||||||
|
|
||||||
|
// default bindings are always naive
|
||||||
|
unsigned short vKey_default = INVALID_VKEY;
|
||||||
|
|
||||||
ButtonAnalogType analog_type = BAT_NONE;
|
ButtonAnalogType analog_type = BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
@@ -51,10 +63,10 @@ private:
|
|||||||
float last_velocity = 0.f;
|
float last_velocity = 0.f;
|
||||||
unsigned short velocity_threshold = 0;
|
unsigned short velocity_threshold = 0;
|
||||||
|
|
||||||
std::string getVKeyString();
|
|
||||||
std::string getMidiNoteString();
|
std::string getMidiNoteString();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
std::string getVKeyString();
|
||||||
|
|
||||||
// overrides
|
// overrides
|
||||||
bool override_enabled = false;
|
bool override_enabled = false;
|
||||||
@@ -71,12 +83,20 @@ public:
|
|||||||
if (this->override_enabled) {
|
if (this->override_enabled) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// has a device
|
||||||
|
if (!this->device_identifier.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// naive, and has a valid vKey
|
||||||
if (this->vKey != INVALID_VKEY) {
|
if (this->vKey != INVALID_VKEY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &alternative : this->alternatives) {
|
for (auto &alternative : this->alternatives) {
|
||||||
if (alternative.vKey != INVALID_VKEY) {
|
if (!alternative.getDeviceIdentifier().empty() ||
|
||||||
|
alternative.getVKey() != INVALID_VKEY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,6 +137,14 @@ public:
|
|||||||
this->vKey = vKey;
|
this->vKey = vKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline unsigned short getVKeyDefault() const {
|
||||||
|
return this->vKey_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVKeyDefault(unsigned short vKey_default) {
|
||||||
|
this->vKey_default = vKey_default;
|
||||||
|
}
|
||||||
|
|
||||||
inline ButtonAnalogType getAnalogType() const {
|
inline ButtonAnalogType getAnalogType() const {
|
||||||
return this->analog_type;
|
return this->analog_type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
private:
|
||||||
std::vector<Light> alternatives;
|
std::vector<Light> alternatives;
|
||||||
std::string lightName;
|
std::string lightName;
|
||||||
|
std::string lightCategory;
|
||||||
std::string deviceIdentifier = "";
|
std::string deviceIdentifier = "";
|
||||||
unsigned int index = 0;
|
unsigned int index = 0;
|
||||||
bool is_temporary = false;
|
bool is_temporary = false;
|
||||||
@@ -23,6 +24,8 @@ public:
|
|||||||
float override_state = 0.f;
|
float override_state = 0.f;
|
||||||
|
|
||||||
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
|
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
|
||||||
|
explicit Light(std::string lightName, std::string lightCategory) :
|
||||||
|
lightName(std::move(lightName)), lightCategory(std::move(lightCategory)) {};
|
||||||
|
|
||||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||||
|
|
||||||
@@ -51,6 +54,14 @@ public:
|
|||||||
return this->lightName;
|
return this->lightName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const std::string &getCategory() const {
|
||||||
|
return this->lightCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setCategory(std::string category) {
|
||||||
|
this->lightCategory = std::move(category);
|
||||||
|
}
|
||||||
|
|
||||||
inline const std::string &getDeviceIdentifier() const {
|
inline const std::string &getDeviceIdentifier() const {
|
||||||
return this->deviceIdentifier;
|
return this->deviceIdentifier;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ enum class OptionType {
|
|||||||
Hex,
|
Hex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class OptionPickerType {
|
||||||
|
None,
|
||||||
|
AsioDriver,
|
||||||
|
EACard,
|
||||||
|
CpuAffinity,
|
||||||
|
FilePath,
|
||||||
|
DirectoryPath,
|
||||||
|
Monitor,
|
||||||
|
};
|
||||||
|
|
||||||
struct OptionDefinition {
|
struct OptionDefinition {
|
||||||
std::string title;
|
std::string title;
|
||||||
// unique identifier used for flag matching but also stored in config files
|
// unique identifier used for flag matching but also stored in config files
|
||||||
@@ -32,6 +42,10 @@ struct OptionDefinition {
|
|||||||
bool sensitive = false;
|
bool sensitive = false;
|
||||||
std::vector<std::pair<std::string, std::string>> elements = {};
|
std::vector<std::pair<std::string, std::string>> elements = {};
|
||||||
bool disabled = false;
|
bool disabled = false;
|
||||||
|
OptionPickerType picker = OptionPickerType::None;
|
||||||
|
|
||||||
|
// for OptionPickerType::FilePath
|
||||||
|
std::string file_extension = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class Option {
|
class Option {
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
#define IDR_PATCHES 132
|
#define IDR_PATCHES 132
|
||||||
#define IDR_README 133
|
#define IDR_README 133
|
||||||
#define IDR_DSEGFONT 134
|
#define IDR_DSEGFONT 134
|
||||||
|
#define IDR_CONTROLLER_PRESETS_BUILTIN 135
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+598
-222
@@ -1,16 +1,37 @@
|
|||||||
// dear imgui: Renderer for DirectX9
|
// dear imgui: Renderer Backend for DirectX9
|
||||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||||
|
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
// [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.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// https://github.com/ocornut/imgui
|
// 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
|
// CHANGELOG
|
||||||
// (minor and older changes stripped away, please see git history for details)
|
// (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-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-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().
|
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
|
||||||
@@ -22,94 +43,76 @@
|
|||||||
// 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-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.
|
// 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>
|
#include <algorithm>
|
||||||
|
|
||||||
// DirectX
|
|
||||||
#include <d3d9.h>
|
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_dx9.h"
|
||||||
|
|
||||||
// allow std::min use
|
// allow std::min use
|
||||||
#ifdef min
|
#ifdef min
|
||||||
#undef min
|
#undef min
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// DirectX data
|
// DirectX
|
||||||
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
|
#include <d3d9.h>
|
||||||
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
|
|
||||||
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
|
|
||||||
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
|
|
||||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
|
||||||
|
|
||||||
|
// 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)
|
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
|
||||||
|
|
||||||
// Render function.
|
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
#define IMGUI_COL_TO_DX9_ARGB(_COL) (_COL)
|
||||||
void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
#else
|
||||||
|
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
|
||||||
|
#endif
|
||||||
|
|
||||||
// Avoid rendering when minimized
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
return;
|
static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData()
|
||||||
|
{
|
||||||
// Create and grow buffers if needed
|
return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backup the DX9 state
|
// Forward Declarations
|
||||||
IDirect3DStateBlock9 *d3d9_state_block = NULL;
|
static void ImGui_ImplDX9_InitMultiViewportSupport();
|
||||||
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
|
static void ImGui_ImplDX9_ShutdownMultiViewportSupport();
|
||||||
return;
|
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
|
||||||
|
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
|
||||||
|
|
||||||
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
|
struct render_state_t {
|
||||||
D3DMATRIX last_world, last_view, last_projection;
|
IDirect3DSurface9 *back_buffer;
|
||||||
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
|
IDirect3DSurface9 *depth_stencil;
|
||||||
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
|
IDirect3DSurface9 *render_targets[8];
|
||||||
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
|
D3DCAPS9 caps;
|
||||||
|
};
|
||||||
|
|
||||||
// Copy all vertices into a single contiguous buffer
|
// Functions
|
||||||
ImDrawVert *vtx_dst;
|
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data, struct render_state_t *render_state)
|
||||||
ImDrawIdx *idx_dst;
|
{
|
||||||
if (g_pVB->Lock(0, (UINT) (draw_data->TotalVtxCount * sizeof(ImDrawVert)), (void **) &vtx_dst,
|
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||||
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);
|
|
||||||
|
|
||||||
// Setup viewport
|
// Setup viewport
|
||||||
D3DVIEWPORT9 vp;
|
D3DVIEWPORT9 vp;
|
||||||
@@ -118,64 +121,76 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
|||||||
vp.Height = (DWORD)draw_data->DisplaySize.y;
|
vp.Height = (DWORD)draw_data->DisplaySize.y;
|
||||||
vp.MinZ = 0.0f;
|
vp.MinZ = 0.0f;
|
||||||
vp.MaxZ = 1.0f;
|
vp.MaxZ = 1.0f;
|
||||||
g_pd3dDevice->SetViewport(&vp);
|
|
||||||
|
|
||||||
g_pd3dDevice->SetPixelShader(nullptr);
|
LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
|
||||||
g_pd3dDevice->SetVertexShader(nullptr);
|
device->SetViewport(&vp);
|
||||||
|
|
||||||
D3DCAPS9 caps {};
|
// spice ldj
|
||||||
if (FAILED(g_pd3dDevice->GetDeviceCaps(&caps))) {
|
{
|
||||||
caps.NumSimultaneousRTs = 0UL;
|
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
|
// save all previous render target state
|
||||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
for (size_t target = 0; target < std::min(8UL, render_state->caps.NumSimultaneousRTs); target++) {
|
||||||
if (FAILED(g_pd3dDevice->GetRenderTarget(target, &render_targets[target]))) {
|
if (FAILED(device->GetRenderTarget(target, &render_state->render_targets[target]))) {
|
||||||
render_targets[target] = nullptr;
|
render_state->render_targets[target] = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the previous depth stencil
|
// get the previous depth stencil
|
||||||
if (FAILED(g_pd3dDevice->GetDepthStencilSurface(&depth_stencil))) {
|
if (FAILED(device->GetDepthStencilSurface(&render_state->depth_stencil))) {
|
||||||
depth_stencil = nullptr;
|
render_state->depth_stencil = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the back buffer as the current render target
|
// set the back buffer as the current render target
|
||||||
if (SUCCEEDED(g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer))) {
|
if (SUCCEEDED(device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &render_state->back_buffer))) {
|
||||||
g_pd3dDevice->SetRenderTarget(0, back_buffer);
|
device->SetRenderTarget(0, render_state->back_buffer);
|
||||||
g_pd3dDevice->SetDepthStencilSurface(nullptr);
|
device->SetDepthStencilSurface(nullptr);
|
||||||
|
|
||||||
for (size_t target = 1; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
for (size_t target = 1; target < std::min(8UL, render_state->caps.NumSimultaneousRTs); target++) {
|
||||||
g_pd3dDevice->SetRenderTarget(target, nullptr);
|
device->SetRenderTarget(target, nullptr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
back_buffer = nullptr;
|
render_state->back_buffer = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
|
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling.
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
device->SetPixelShader(nullptr);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
|
device->SetVertexShader(nullptr);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
|
device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
|
device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
|
device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
device->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
|
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
|
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
device->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
device->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
device->SetRenderState(D3DRS_FOGENABLE, FALSE);
|
||||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
device->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
|
||||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
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
|
// 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.
|
// 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.
|
||||||
@@ -193,151 +208,365 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
|||||||
0.0f, 0.0f, 0.5f, 0.0f,
|
0.0f, 0.0f, 0.5f, 0.0f,
|
||||||
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
|
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
|
||||||
} } };
|
} } };
|
||||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
|
device->SetTransform(D3DTS_WORLD, &mat_identity);
|
||||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
|
device->SetTransform(D3DTS_VIEW, &mat_identity);
|
||||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
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
|
// Render command lists
|
||||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||||
int global_vtx_offset = 0;
|
int global_vtx_offset = 0;
|
||||||
int global_idx_offset = 0;
|
int global_idx_offset = 0;
|
||||||
ImVec2 clip_off = draw_data->DisplayPos;
|
ImVec2 clip_off = draw_data->DisplayPos;
|
||||||
for (int n = 0; n < draw_data->CmdListsCount; n++) {
|
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||||
const ImDrawList *cmd_list = draw_data->CmdLists[n];
|
{
|
||||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
|
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||||
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
|
{
|
||||||
if (pcmd->UserCallback != NULL) {
|
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||||
pcmd->UserCallback(cmd_list, pcmd);
|
if (pcmd->UserCallback != nullptr)
|
||||||
} else {
|
{
|
||||||
const RECT r = {(LONG) (pcmd->ClipRect.x - clip_off.x), (LONG) (pcmd->ClipRect.y - clip_off.y),
|
// User callback, registered via ImDrawList::AddCallback()
|
||||||
(LONG) (pcmd->ClipRect.z - clip_off.x), (LONG) (pcmd->ClipRect.w - clip_off.y)};
|
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||||
auto texture = reinterpret_cast<IDirect3DBaseTexture9 *>(pcmd->TextureId);
|
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||||
g_pd3dDevice->SetTexture(0, texture);
|
ImGui_ImplDX9_SetupRenderState(draw_data, &render_state);
|
||||||
g_pd3dDevice->SetScissorRect(&r);
|
else
|
||||||
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
|
pcmd->UserCallback(draw_list, pcmd);
|
||||||
pcmd->VtxOffset + global_vtx_offset, 0,
|
}
|
||||||
(UINT) cmd_list->VtxBuffer.Size,
|
else
|
||||||
pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
|
{
|
||||||
|
// 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_idx_offset += draw_list->IdxBuffer.Size;
|
||||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
global_vtx_offset += draw_list->VtxBuffer.Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (back_buffer) {
|
// When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows
|
||||||
back_buffer->Release();
|
// from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560)
|
||||||
back_buffer = nullptr;
|
if (global_vtx_offset == 0)
|
||||||
}
|
bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
// spice ldj
|
||||||
|
{
|
||||||
|
if (render_state.back_buffer) {
|
||||||
|
render_state.back_buffer->Release();
|
||||||
|
render_state.back_buffer = nullptr;
|
||||||
|
}
|
||||||
// restore previous depth stencil
|
// restore previous depth stencil
|
||||||
if (depth_stencil) {
|
if (render_state.depth_stencil) {
|
||||||
g_pd3dDevice->SetDepthStencilSurface(depth_stencil);
|
device->SetDepthStencilSurface(render_state.depth_stencil);
|
||||||
depth_stencil->Release();
|
render_state.depth_stencil->Release();
|
||||||
depth_stencil = nullptr;
|
render_state.depth_stencil = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore all render target state
|
// restore all render target state
|
||||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
for (size_t target = 0; target < std::min(8UL, render_state.caps.NumSimultaneousRTs); target++) {
|
||||||
auto render_target = render_targets[target];
|
auto render_target = render_state.render_targets[target];
|
||||||
|
|
||||||
if (render_target) {
|
if (render_target) {
|
||||||
g_pd3dDevice->SetRenderTarget(target, render_target);
|
device->SetRenderTarget(target, render_target);
|
||||||
render_target->Release();
|
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 state
|
|
||||||
d3d9_state_block->Apply();
|
|
||||||
d3d9_state_block->Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImGui_ImplDX9_Init(IDirect3DDevice9 *device) {
|
// Restore the DX9 transform
|
||||||
|
device->SetTransform(D3DTS_WORLD, &last_world);
|
||||||
|
device->SetTransform(D3DTS_VIEW, &last_view);
|
||||||
|
device->SetTransform(D3DTS_PROJECTION, &last_projection);
|
||||||
|
|
||||||
// Setup back-end capabilities flags
|
// Restore the DX9 state
|
||||||
auto &io = ImGui::GetIO();
|
state_block->Apply();
|
||||||
io.BackendRendererName = "imgui_impl_dx9";
|
state_block->Release();
|
||||||
|
|
||||||
// We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
|
||||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
|
|
||||||
|
|
||||||
g_pd3dDevice = device;
|
|
||||||
g_pd3dDevice->AddRef();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGui_ImplDX9_Shutdown() {
|
static bool ImGui_ImplDX9_CheckFormatSupport(LPDIRECT3DDEVICE9 pDevice, D3DFORMAT format)
|
||||||
ImGui_ImplDX9_InvalidateDeviceObjects();
|
{
|
||||||
|
LPDIRECT3D9 pd3d = nullptr;
|
||||||
if (g_pd3dDevice) {
|
if (pDevice->GetDirect3D(&pd3d) != D3D_OK)
|
||||||
g_pd3dDevice->Release();
|
return false;
|
||||||
g_pd3dDevice = NULL;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ImGui_ImplDX9_CreateFontsTexture() {
|
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
|
||||||
// Build texture atlas
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
unsigned char *pixels;
|
IMGUI_CHECKVERSION();
|
||||||
int width, height, bytes_per_pixel;
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
|
|
||||||
|
|
||||||
// Upload texture to graphics system
|
// Setup backend capabilities flags
|
||||||
g_FontTexture = NULL;
|
ImGui_ImplDX9_Data* bd = IM_NEW(ImGui_ImplDX9_Data)();
|
||||||
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
|
io.BackendRendererUserData = (void*)bd;
|
||||||
D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
|
io.BackendRendererName = "imgui_impl_dx9";
|
||||||
return false;
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
D3DLOCKED_RECT tex_locked_rect;
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||||
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
|
||||||
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
|
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||||
io.Fonts->TexID = (ImTextureID) g_FontTexture;
|
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();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImGui_ImplDX9_CreateDeviceObjects() {
|
void ImGui_ImplDX9_Shutdown()
|
||||||
if (!g_pd3dDevice) {
|
{
|
||||||
return false;
|
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||||
}
|
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||||
return ImGui_ImplDX9_CreateFontsTexture();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||||
|
|
||||||
|
ImGui_ImplDX9_ShutdownMultiViewportSupport();
|
||||||
|
ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||||
|
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||||
|
|
||||||
|
io.BackendRendererName = nullptr;
|
||||||
|
io.BackendRendererUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
|
||||||
|
platform_io.ClearRendererHandlers();
|
||||||
|
IM_DELETE(bd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGui_ImplDX9_InvalidateDeviceObjects() {
|
// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
|
||||||
if (!g_pd3dDevice)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||||
|
|
||||||
|
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;
|
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() {
|
D3DLOCKED_RECT locked_rect;
|
||||||
if (!g_FontTexture) {
|
if (dx_tex->LockRect(0, &locked_rect, nullptr, 0) == D3D_OK)
|
||||||
ImGui_ImplDX9_CreateDeviceObjects();
|
{
|
||||||
|
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;
|
IDirect3DSwapChain9 *swap_chain = nullptr;
|
||||||
if (SUCCEEDED(g_pd3dDevice->GetSwapChain(0, &swap_chain))) {
|
if (SUCCEEDED(bd->pd3dDevice->GetSwapChain(0, &swap_chain))) {
|
||||||
auto &io = ImGui::GetIO();
|
auto &io = ImGui::GetIO();
|
||||||
|
|
||||||
D3DPRESENT_PARAMETERS present_params {};
|
D3DPRESENT_PARAMETERS present_params {};
|
||||||
@@ -358,3 +587,150 @@ void ImGui_ImplDX9_NewFrame() {
|
|||||||
swap_chain->Release();
|
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
|
||||||
|
|||||||
+23
-10
@@ -1,25 +1,38 @@
|
|||||||
// dear imgui: Renderer for DirectX9
|
// dear imgui: Renderer Backend for DirectX9
|
||||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||||
|
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
// [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.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// https://github.com/ocornut/imgui
|
// 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
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
#include "imgui.h"
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
struct IDirect3DDevice9;
|
struct IDirect3DDevice9;
|
||||||
|
|
||||||
|
// 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 bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
|
||||||
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
|
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
|
||||||
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
|
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 bool ImGui_ImplDX9_CreateDeviceObjects();
|
||||||
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();
|
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
|
#pragma once
|
||||||
|
|
||||||
//---- Define assertion handler. Defaults to calling assert().
|
//---- 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) MyAssert(_EXPR)
|
||||||
#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
#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.
|
//---- 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_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.
|
//---- 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.
|
// 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_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_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_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
|
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||||
|
|
||||||
//---- Enable Test Engine / Automation features.
|
//---- Enable Test Engine / Automation features.
|
||||||
@@ -59,12 +60,15 @@
|
|||||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_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
|
#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
//---- Performance optimization for DX9
|
//---- Performance optimization for DX9
|
||||||
#define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
#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...)
|
//---- 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
|
//#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)
|
//---- 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).
|
// 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'.
|
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||||
//#define IMGUI_ENABLE_FREETYPE
|
//#define IMGUI_ENABLE_FREETYPE
|
||||||
|
|
||||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
// - 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.
|
||||||
// - 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.
|
|
||||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
// - 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)
|
// - (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
|
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||||
@@ -130,6 +134,10 @@
|
|||||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||||
//#define IM_DEBUG_BREAK __debugbreak()
|
//#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
|
//---- Debug Tools: Enable slower asserts
|
||||||
//#define IMGUI_DEBUG_PARANOID
|
//#define IMGUI_DEBUG_PARANOID
|
||||||
|
|
||||||
|
|||||||
Vendored
+3309
-1823
File diff suppressed because it is too large
Load Diff
Vendored
+1003
-497
File diff suppressed because it is too large
Load Diff
+2780
-2034
File diff suppressed because it is too large
Load Diff
+3177
-1114
File diff suppressed because it is too large
Load Diff
+770
-330
File diff suppressed because it is too large
Load Diff
+255
-161
@@ -1,6 +1,7 @@
|
|||||||
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
||||||
// Get latest version at http://www.github.com/ocornut/imgui_club
|
// 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!
|
// Right-click anywhere to access the Options menu!
|
||||||
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
||||||
// The code assume a mono-space font for simplicity!
|
// 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.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.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.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.
|
// - 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.
|
// - 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.
|
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
||||||
|
|
||||||
@@ -51,15 +64,18 @@
|
|||||||
#include <stdio.h> // sprintf, scanf
|
#include <stdio.h> // sprintf, scanf
|
||||||
#include <stdint.h> // uint8_t, etc.
|
#include <stdint.h> // uint8_t, etc.
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) && !defined(snprintf)
|
||||||
#define _PRISizeT "I"
|
|
||||||
#define ImSnprintf _snprintf
|
#define ImSnprintf _snprintf
|
||||||
#else
|
#else
|
||||||
#define _PRISizeT "z"
|
|
||||||
#define ImSnprintf snprintf
|
#define ImSnprintf snprintf
|
||||||
#endif
|
#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 (push)
|
||||||
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||||
#endif
|
#endif
|
||||||
@@ -88,9 +104,17 @@ struct MemoryEditor
|
|||||||
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
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
|
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
|
||||||
ImU32 HighlightColor; // // background color of highlighted bytes.
|
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.
|
// Function handlers
|
||||||
bool (*HighlightFn)(const ImU8* data, size_t off);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
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]
|
// [Internal State]
|
||||||
bool ContentsWidthChanged;
|
bool ContentsWidthChanged;
|
||||||
@@ -101,7 +125,7 @@ struct MemoryEditor
|
|||||||
char AddrInputBuf[32];
|
char AddrInputBuf[32];
|
||||||
size_t GotoAddr;
|
size_t GotoAddr;
|
||||||
size_t HighlightMin, HighlightMax;
|
size_t HighlightMin, HighlightMax;
|
||||||
int PreviewEndianess;
|
int PreviewEndianness;
|
||||||
ImGuiDataType PreviewDataType;
|
ImGuiDataType PreviewDataType;
|
||||||
|
|
||||||
MemoryEditor()
|
MemoryEditor()
|
||||||
@@ -120,9 +144,11 @@ struct MemoryEditor
|
|||||||
OptAddrDigitsCount = 0;
|
OptAddrDigitsCount = 0;
|
||||||
OptFooterExtraHeight = 0.0f;
|
OptFooterExtraHeight = 0.0f;
|
||||||
HighlightColor = IM_COL32(255, 255, 255, 50);
|
HighlightColor = IM_COL32(255, 255, 255, 50);
|
||||||
ReadFn = NULL;
|
ReadFn = nullptr;
|
||||||
WriteFn = NULL;
|
WriteFn = nullptr;
|
||||||
HighlightFn = NULL;
|
HighlightFn = nullptr;
|
||||||
|
BgColorFn = nullptr;
|
||||||
|
UserData = nullptr;
|
||||||
|
|
||||||
// State/Internals
|
// State/Internals
|
||||||
ContentsWidthChanged = false;
|
ContentsWidthChanged = false;
|
||||||
@@ -131,8 +157,10 @@ struct MemoryEditor
|
|||||||
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
||||||
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
||||||
GotoAddr = (size_t)-1;
|
GotoAddr = (size_t)-1;
|
||||||
|
MouseHovered = false;
|
||||||
|
MouseHoveredAddr = 0;
|
||||||
HighlightMin = HighlightMax = (size_t)-1;
|
HighlightMin = HighlightMax = (size_t)-1;
|
||||||
PreviewEndianess = 0;
|
PreviewEndianness = 0;
|
||||||
PreviewDataType = ImGuiDataType_S32;
|
PreviewDataType = ImGuiDataType_S32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,16 +173,16 @@ struct MemoryEditor
|
|||||||
|
|
||||||
struct Sizes
|
struct Sizes
|
||||||
{
|
{
|
||||||
int AddrDigitsCount;
|
int AddrDigitsCount; // Number of digits required to represent maximum address.
|
||||||
float LineHeight;
|
float LineHeight; // Height of each line (no spacing).
|
||||||
float GlyphWidth;
|
float GlyphWidth; // Glyph width (assume mono-space).
|
||||||
float HexCellWidth;
|
float HexCellWidth; // Width of a hex edit cell ~2.5f * GlypHWidth.
|
||||||
float SpacingBetweenMidCols;
|
float SpacingBetweenMidCols; // Spacing between each columns section (OptMidColsCount).
|
||||||
float PosHexStart;
|
float OffsetHexMinX;
|
||||||
float PosHexEnd;
|
float OffsetHexMaxX;
|
||||||
float PosAsciiStart;
|
float OffsetAsciiMinX;
|
||||||
float PosAsciiEnd;
|
float OffsetAsciiMaxX;
|
||||||
float WindowWidth;
|
float WindowWidth; // Ideal window width.
|
||||||
|
|
||||||
Sizes() { memset(this, 0, sizeof(*this)); }
|
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.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.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.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.OffsetHexMinX = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||||
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
s.OffsetHexMaxX = s.OffsetHexMinX + (s.HexCellWidth * Cols);
|
||||||
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
s.OffsetAsciiMinX = s.OffsetAsciiMaxX = s.OffsetHexMaxX;
|
||||||
if (OptShowAscii)
|
if (OptShowAscii)
|
||||||
{
|
{
|
||||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
s.OffsetAsciiMinX = s.OffsetHexMaxX + s.GlyphWidth * 1;
|
||||||
if (OptMidColsCount > 0)
|
if (OptMidColsCount > 0)
|
||||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
s.OffsetAsciiMinX += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
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
|
// Standalone Memory Editor window
|
||||||
@@ -194,8 +222,6 @@ struct MemoryEditor
|
|||||||
Open = true;
|
Open = true;
|
||||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
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);
|
DrawContents(mem_data, mem_size, base_display_addr);
|
||||||
if (ContentsWidthChanged)
|
if (ContentsWidthChanged)
|
||||||
{
|
{
|
||||||
@@ -217,6 +243,8 @@ struct MemoryEditor
|
|||||||
CalcSizes(s, mem_size, base_display_addr);
|
CalcSizes(s, mem_size, base_display_addr);
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
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.
|
// 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.
|
// 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;
|
const float height_separator = style.ItemSpacing.y;
|
||||||
@@ -225,20 +253,21 @@ struct MemoryEditor
|
|||||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||||
if (OptShowDataPreview)
|
if (OptShowDataPreview)
|
||||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
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();
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, 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.
|
// 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);
|
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||||
ImGuiListClipper clipper;
|
ImGuiListClipper clipper;
|
||||||
clipper.Begin(line_total_count, s.LineHeight);
|
clipper.Begin(line_total_count, s.LineHeight);
|
||||||
|
|
||||||
bool data_next = false;
|
bool data_next = false;
|
||||||
|
|
||||||
if (ReadOnly || DataEditingAddr >= mem_size)
|
if (DataEditingAddr >= mem_size)
|
||||||
DataEditingAddr = (size_t)-1;
|
DataEditingAddr = (size_t)-1;
|
||||||
if (DataPreviewAddr >= mem_size)
|
if (DataPreviewAddr >= mem_size)
|
||||||
DataPreviewAddr = (size_t)-1;
|
DataPreviewAddr = (size_t)-1;
|
||||||
@@ -249,16 +278,16 @@ struct MemoryEditor
|
|||||||
if (DataEditingAddr != (size_t)-1)
|
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)
|
// 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; }
|
if (ImGui::IsKeyPressed(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(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(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; }
|
else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw vertical separator
|
// Draw vertical separator
|
||||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||||
if (OptShowAscii)
|
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_text = ImGui::GetColorU32(ImGuiCol_Text);
|
||||||
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_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 = OptUpperCaseHex ? "%02X" : "%02x";
|
||||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||||
|
|
||||||
|
MouseHovered = false;
|
||||||
|
MouseHoveredAddr = 0;
|
||||||
|
|
||||||
while (clipper.Step())
|
while (clipper.Step())
|
||||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
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);
|
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||||
|
|
||||||
// Draw Hexadecimal
|
// Draw Hexadecimal
|
||||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
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)
|
if (OptMidColsCount > 0)
|
||||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
ImGui::SameLine(byte_pos_x);
|
ImGui::SameLine(byte_pos_x);
|
||||||
|
|
||||||
// Draw highlight
|
// Draw highlight or custom background color
|
||||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr));
|
const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData));
|
||||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
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)
|
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||||
{
|
{
|
||||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
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));
|
||||||
float highlight_width = s.GlyphWidth * 2;
|
bg_color = HighlightColor;
|
||||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1)));
|
}
|
||||||
|
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))
|
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)
|
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)
|
if (DataEditingAddr == addr)
|
||||||
@@ -308,17 +352,21 @@ struct MemoryEditor
|
|||||||
if (DataEditingTakeFocus)
|
if (DataEditingTakeFocus)
|
||||||
{
|
{
|
||||||
ImGui::SetKeyboardFocusHere(0);
|
ImGui::SetKeyboardFocusHere(0);
|
||||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[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.
|
// 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)
|
static int Callback(ImGuiInputTextCallbackData* data)
|
||||||
{
|
{
|
||||||
UserData* user_data = (UserData*)data->UserData;
|
InputTextUserData* user_data = (InputTextUserData*)data->UserData;
|
||||||
if (!data->HasSelection())
|
if (!data->HasSelection())
|
||||||
user_data->CursorPos = data->CursorPos;
|
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)
|
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||||
{
|
{
|
||||||
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
|
// 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
|
char CurrentBufOverwrite[3]; // Input
|
||||||
int CursorPos; // Output
|
int CursorPos; // Output
|
||||||
};
|
};
|
||||||
UserData user_data;
|
InputTextUserData input_text_user_data;
|
||||||
user_data.CursorPos = -1;
|
input_text_user_data.CursorPos = -1;
|
||||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
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;
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
|
||||||
#if IMGUI_VERSION_NUM >= 18104
|
if (ReadOnly)
|
||||||
flags |= ImGuiInputTextFlags_AlwaysOverwrite;
|
flags |= ImGuiInputTextFlags_ReadOnly;
|
||||||
#else
|
flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode
|
||||||
flags |= ImGuiInputTextFlags_AlwaysInsertMode;
|
|
||||||
#endif
|
|
||||||
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
|
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;
|
data_write = data_next = true;
|
||||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||||
DataEditingTakeFocus = false;
|
DataEditingTakeFocus = false;
|
||||||
if (user_data.CursorPos >= 2)
|
if (input_text_user_data.CursorPos >= 2)
|
||||||
data_write = data_next = true;
|
data_write = data_next = true;
|
||||||
if (data_editing_addr_next != (size_t)-1)
|
if (data_editing_addr_next != (size_t)-1)
|
||||||
data_write = data_next = false;
|
data_write = data_next = false;
|
||||||
unsigned int data_input_value = 0;
|
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)
|
if (WriteFn)
|
||||||
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);
|
||||||
else
|
else
|
||||||
mem_data[addr] = (ImU8)data_input_value;
|
mem_data[addr] = (ImU8)data_input_value;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
{
|
||||||
|
MouseHovered = true;
|
||||||
|
MouseHoveredAddr = addr;
|
||||||
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
// 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)
|
if (OptShowHexII)
|
||||||
{
|
{
|
||||||
@@ -386,26 +437,40 @@ struct MemoryEditor
|
|||||||
else
|
else
|
||||||
ImGui::Text(format_byte_space, b);
|
ImGui::Text(format_byte_space, b);
|
||||||
}
|
}
|
||||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
|
if (ImGui::IsItemHovered())
|
||||||
|
{
|
||||||
|
MouseHovered = true;
|
||||||
|
MouseHoveredAddr = addr;
|
||||||
|
if (ImGui::IsMouseClicked(0))
|
||||||
{
|
{
|
||||||
DataEditingTakeFocus = true;
|
DataEditingTakeFocus = true;
|
||||||
data_editing_addr_next = addr;
|
data_editing_addr_next = addr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (OptShowAscii)
|
if (OptShowAscii)
|
||||||
{
|
{
|
||||||
// Draw ASCII values
|
// Draw ASCII values
|
||||||
ImGui::SameLine(s.PosAsciiStart);
|
ImGui::SameLine(s.OffsetAsciiMinX);
|
||||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
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);
|
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;
|
DataEditingTakeFocus = true;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
{
|
||||||
|
MouseHovered = true;
|
||||||
|
MouseHoveredAddr = mouse_addr;
|
||||||
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
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_FrameBg));
|
||||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
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;
|
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||||
pos.x += s.GlyphWidth;
|
pos.x += s.GlyphWidth;
|
||||||
@@ -422,10 +491,14 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
|
const float child_width = ImGui::GetWindowSize().x;
|
||||||
ImGui::EndChild();
|
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)
|
// 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::SetCursorPosX(s.WindowWidth);
|
||||||
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||||
|
ImGui::SetCursorScreenPos(backup_pos);
|
||||||
|
|
||||||
if (data_next && DataEditingAddr + 1 < mem_size)
|
if (data_next && DataEditingAddr + 1 < mem_size)
|
||||||
{
|
{
|
||||||
@@ -450,18 +523,28 @@ struct MemoryEditor
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
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));
|
||||||
IM_UNUSED(mem_data);
|
if (OptShowOptions)
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end))
|
||||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||||
|
ImGui::OpenPopup("OptionsPopup");
|
||||||
|
|
||||||
// Options menu
|
if (ImGui::BeginPopup("OptionsPopup"))
|
||||||
if (ImGui::Button("Options"))
|
|
||||||
ImGui::OpenPopup("context");
|
|
||||||
if (ImGui::BeginPopup("context"))
|
|
||||||
{
|
{
|
||||||
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
|
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; }
|
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();
|
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::SameLine();
|
||||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
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 (MouseHovered)
|
||||||
{
|
//{
|
||||||
if (GotoAddr < mem_size)
|
// ImGui::SameLine();
|
||||||
{
|
// ImGui::Text("Hovered: %p", MouseHoveredAddr);
|
||||||
ImGui::BeginChild("##scrolling");
|
//}
|
||||||
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());
|
|
||||||
ImGui::EndChild();
|
|
||||||
DataEditingAddr = DataPreviewAddr = GotoAddr;
|
|
||||||
DataEditingTakeFocus = true;
|
|
||||||
}
|
|
||||||
GotoAddr = (size_t)-1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
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::Text("Preview as:");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
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))
|
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
||||||
{
|
{
|
||||||
for (int n = 0; n < ImGuiDataType_COUNT; n++)
|
for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++)
|
||||||
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
|
{
|
||||||
PreviewDataType = (ImGuiDataType)n;
|
ImGuiDataType data_type = supported_data_types[n];
|
||||||
|
if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))
|
||||||
|
PreviewDataType = data_type;
|
||||||
|
}
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
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] = "";
|
char buf[128] = "";
|
||||||
float x = s.GlyphWidth * 6.0f;
|
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");
|
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* DataTypeGetDesc(ImGuiDataType data_type) const
|
||||||
{
|
{
|
||||||
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
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];
|
return descs[data_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
||||||
{
|
{
|
||||||
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
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];
|
return sizes[data_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,18 +654,18 @@ struct MemoryEditor
|
|||||||
|
|
||||||
bool IsBigEndian() const
|
bool IsBigEndian() const
|
||||||
{
|
{
|
||||||
uint16_t x = 1;
|
ImU16 x = 1;
|
||||||
char c[2];
|
char c[2];
|
||||||
memcpy(c, &x, 2);
|
memcpy(c, &x, 2);
|
||||||
return c[0] != 0;
|
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)
|
if (is_little_endian)
|
||||||
{
|
{
|
||||||
uint8_t* dst = (uint8_t*)_dst;
|
ImU8* dst = (ImU8*)_dst;
|
||||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
ImU8* src = (ImU8*)_src + s - 1;
|
||||||
for (int i = 0, n = (int)s; i < n; ++i)
|
for (int i = 0, n = (int)s; i < n; ++i)
|
||||||
memcpy(dst++, src--, 1);
|
memcpy(dst++, src--, 1);
|
||||||
return _dst;
|
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)
|
if (is_little_endian)
|
||||||
{
|
{
|
||||||
@@ -591,23 +684,23 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t* dst = (uint8_t*)_dst;
|
ImU8* dst = (ImU8*)_dst;
|
||||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
ImU8* src = (ImU8*)_src + s - 1;
|
||||||
for (int i = 0, n = (int)s; i < n; ++i)
|
for (int i = 0, n = (int)s; i < n; ++i)
|
||||||
memcpy(dst++, src--, 1);
|
memcpy(dst++, src--, 1);
|
||||||
return _dst;
|
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;
|
static void* (*fp)(void*, void*, size_t, int) = nullptr;
|
||||||
if (fp == NULL)
|
if (fp == nullptr)
|
||||||
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
|
fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;
|
||||||
return fp(dst, src, size, PreviewEndianess);
|
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);
|
IM_ASSERT(width <= 64);
|
||||||
size_t out_n = 0;
|
size_t out_n = 0;
|
||||||
@@ -627,19 +720,19 @@ struct MemoryEditor
|
|||||||
// [Internal]
|
// [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
|
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 elem_size = DataTypeGetSize(data_type);
|
||||||
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
||||||
if (ReadFn)
|
if (ReadFn)
|
||||||
for (int i = 0, n = (int)size; i < n; ++i)
|
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
|
else
|
||||||
memcpy(buf, mem_data + addr, size);
|
memcpy(buf, mem_data + addr, size);
|
||||||
|
|
||||||
if (data_format == DataFormat_Bin)
|
if (data_format == DataFormat_Bin)
|
||||||
{
|
{
|
||||||
uint8_t binbuf[8];
|
ImU8 binbuf[8];
|
||||||
EndianessCopy(binbuf, buf, size);
|
EndiannessCopy(binbuf, buf, size);
|
||||||
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -649,84 +742,85 @@ struct MemoryEditor
|
|||||||
{
|
{
|
||||||
case ImGuiDataType_S8:
|
case ImGuiDataType_S8:
|
||||||
{
|
{
|
||||||
int8_t int8 = 0;
|
ImS8 data = 0;
|
||||||
EndianessCopy(&int8, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", int8); return; }
|
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", int8 & 0xFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U8:
|
case ImGuiDataType_U8:
|
||||||
{
|
{
|
||||||
uint8_t uint8 = 0;
|
ImU8 data = 0;
|
||||||
EndianessCopy(&uint8, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", uint8); return; }
|
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", uint8 & 0XFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_S16:
|
case ImGuiDataType_S16:
|
||||||
{
|
{
|
||||||
int16_t int16 = 0;
|
ImS16 data = 0;
|
||||||
EndianessCopy(&int16, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", int16); return; }
|
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", int16 & 0xFFFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U16:
|
case ImGuiDataType_U16:
|
||||||
{
|
{
|
||||||
uint16_t uint16 = 0;
|
ImU16 data = 0;
|
||||||
EndianessCopy(&uint16, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", uint16); return; }
|
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", uint16 & 0xFFFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_S32:
|
case ImGuiDataType_S32:
|
||||||
{
|
{
|
||||||
int32_t int32 = 0;
|
ImS32 data = 0;
|
||||||
EndianessCopy(&int32, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", int32); return; }
|
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", int32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U32:
|
case ImGuiDataType_U32:
|
||||||
{
|
{
|
||||||
uint32_t uint32 = 0;
|
ImU32 data = 0;
|
||||||
EndianessCopy(&uint32, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", uint32); return; }
|
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", uint32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_S64:
|
case ImGuiDataType_S64:
|
||||||
{
|
{
|
||||||
int64_t int64 = 0;
|
ImS64 data = 0;
|
||||||
EndianessCopy(&int64, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64); return; }
|
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)int64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U64:
|
case ImGuiDataType_U64:
|
||||||
{
|
{
|
||||||
uint64_t uint64 = 0;
|
ImU64 data = 0;
|
||||||
EndianessCopy(&uint64, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)uint64); return; }
|
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)uint64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_Float:
|
case ImGuiDataType_Float:
|
||||||
{
|
{
|
||||||
float float32 = 0.0f;
|
float data = 0.0f;
|
||||||
EndianessCopy(&float32, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float32); return; }
|
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", float32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_Double:
|
case ImGuiDataType_Double:
|
||||||
{
|
{
|
||||||
double float64 = 0.0;
|
double data = 0.0;
|
||||||
EndianessCopy(&float64, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float64); return; }
|
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", float64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
case ImGuiDataType_COUNT:
|
case ImGuiDataType_COUNT:
|
||||||
break;
|
break;
|
||||||
} // Switch
|
} // Switch
|
||||||
|
|||||||
+351
-175
File diff suppressed because it is too large
Load Diff
+1354
-818
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) {
|
if (node->y > min_y) {
|
||||||
// raise min_y higher.
|
// raise min_y higher.
|
||||||
// we've accounted for all waste up to min_y,
|
// 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);
|
waste_area += visited_width * (node->y - min_y);
|
||||||
min_y = node->y;
|
min_y = node->y;
|
||||||
// the first time through, visited_width might be reduced
|
// 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
|
// insert the new node into the right starting point, and
|
||||||
// let 'cur' point to the remaining nodes needing to be
|
// let 'cur' point to the remaining nodes needing to be
|
||||||
// stiched back in
|
// stitched back in
|
||||||
|
|
||||||
cur = *res.prev_link;
|
cur = *res.prev_link;
|
||||||
if (cur->x < res.x) {
|
if (cur->x < res.x) {
|
||||||
|
|||||||
+112
-54
@@ -5,7 +5,8 @@
|
|||||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
// - 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 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)
|
// - 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_*
|
// - 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
|
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
//
|
//
|
||||||
// VERSION HISTORY
|
// 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.14 (2021-07-11) page up/down, various fixes
|
||||||
// 1.13 (2019-02-07) fix bug in undo size management
|
// 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
|
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||||
@@ -141,12 +143,14 @@
|
|||||||
// with previous char)
|
// with previous char)
|
||||||
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
||||||
// (return type is int, -1 means not valid to insert)
|
// (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_GETCHAR(obj,i) returns the i'th character of obj, 0-based
|
||||||
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
||||||
// as manually wordwrapping for end-of-line positioning
|
// as manually wordwrapping for end-of-line positioning
|
||||||
//
|
//
|
||||||
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
|
// 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
|
// 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_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
|
// 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
|
// 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
|
// 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,
|
// 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
|
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||||
// transformed into text and stb_textedit_text() is automatically called.
|
// transformed into text and stb_textedit_text() is automatically called.
|
||||||
//
|
//
|
||||||
// text: [DEAR IMGUI] added 2024-09
|
// text: (added 2025)
|
||||||
// call this to text inputs sent to the textfield.
|
// 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
|
// When rendering, you can read the cursor position and selection state from
|
||||||
@@ -400,6 +413,16 @@ typedef struct
|
|||||||
#define IMSTB_TEXTEDIT_memmove memmove
|
#define IMSTB_TEXTEDIT_memmove memmove
|
||||||
#endif
|
#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
|
// 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;
|
StbTexteditRow r;
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
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.x0 = r.x1 = 0;
|
||||||
r.ymin = r.ymax = 0;
|
r.ymin = r.ymax = 0;
|
||||||
r.num_chars = 0;
|
r.num_chars = 0;
|
||||||
|
*out_side_on_line = 0;
|
||||||
|
|
||||||
// search rows to find one that straddles 'y'
|
// search rows to find one that straddles 'y'
|
||||||
while (i < n) {
|
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
|
// below all text, return 'after' last character
|
||||||
if (i >= n)
|
if (i >= n)
|
||||||
|
{
|
||||||
|
*out_side_on_line = 1;
|
||||||
return n;
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// check if it's before the beginning of the line
|
// check if it's before the beginning of the line
|
||||||
if (x < r.x0)
|
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) {
|
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||||
if (x < prev_x+w) {
|
if (x < prev_x+w) {
|
||||||
|
*out_side_on_line = (k == 0) ? 0 : 1;
|
||||||
if (x < prev_x+w/2)
|
if (x < prev_x+w/2)
|
||||||
return k+i;
|
return k+i;
|
||||||
else
|
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
|
// 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)
|
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
|
||||||
return i+r.num_chars-1;
|
return i+r.num_chars-1;
|
||||||
else
|
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
|
// 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
|
// goes off the top or bottom of the text
|
||||||
|
int side_on_line;
|
||||||
if( state->single_line )
|
if( state->single_line )
|
||||||
{
|
{
|
||||||
StbTexteditRow r;
|
StbTexteditRow r;
|
||||||
@@ -478,16 +508,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
|||||||
y = r.ymin;
|
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_start = state->cursor;
|
||||||
state->select_end = state->cursor;
|
state->select_end = state->cursor;
|
||||||
state->has_preferred_x = 0;
|
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
|
// 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)
|
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||||
{
|
{
|
||||||
int p = 0;
|
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
|
// 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
|
// 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)
|
if (state->select_start == state->select_end)
|
||||||
state->select_start = state->cursor;
|
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;
|
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);
|
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||||
if (n < i + r.num_chars)
|
if (n < i + r.num_chars)
|
||||||
break;
|
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
|
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]
|
break; // [DEAR IMGUI]
|
||||||
prev_start = i;
|
prev_start = i;
|
||||||
@@ -648,15 +683,33 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [DEAR IMGUI]
|
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
|
||||||
// Functions must be implemented for UTF8 support
|
#ifndef STB_TEXTEDIT_MOVELINESTART
|
||||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
{
|
||||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
if (state->single_line)
|
||||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
|
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
|
#endif
|
||||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
#ifndef STB_TEXTEDIT_MOVELINEEND
|
||||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
|
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
|
#endif
|
||||||
|
|
||||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||||
@@ -668,9 +721,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
|||||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||||
{
|
{
|
||||||
--c; // always move at least one character
|
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
|
||||||
while (c >= 0 && !is_word_boundary(str, c))
|
while (c >= 0 && !is_word_boundary(str, c))
|
||||||
--c;
|
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
|
||||||
|
|
||||||
if( c < 0 )
|
if( c < 0 )
|
||||||
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 )
|
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||||
{
|
{
|
||||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
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 ) )
|
while( c < len && !is_word_boundary( str, c ) )
|
||||||
++c;
|
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
|
||||||
|
|
||||||
if( c > len )
|
if( c > len )
|
||||||
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_clamp(str, state);
|
||||||
stb_textedit_delete_selection(str,state);
|
stb_textedit_delete_selection(str,state);
|
||||||
// try to insert the characters
|
// 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);
|
stb_text_makeundo_insert(state, state->cursor, len);
|
||||||
state->cursor += len;
|
state->cursor += len;
|
||||||
state->has_preferred_x = 0;
|
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
|
#define STB_TEXTEDIT_KEYTYPE int
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// API key: process text input
|
||||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
// [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)
|
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)) {
|
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_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 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->cursor += text_len;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
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);
|
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||||
state->cursor += text_len;
|
state->cursor += text_len;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
@@ -771,6 +827,7 @@ retry:
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
default: {
|
default: {
|
||||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||||
|
// This is not suitable for UTF-8 support.
|
||||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||||
if (c > 0) {
|
if (c > 0) {
|
||||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||||
@@ -911,15 +968,16 @@ retry:
|
|||||||
|
|
||||||
// [DEAR IMGUI]
|
// [DEAR IMGUI]
|
||||||
// going down while being on the last line shouldn't bring us to that line end
|
// 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)
|
//if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||||
break;
|
// break;
|
||||||
|
|
||||||
// now find character position down a row
|
// now find character position down a row
|
||||||
state->cursor = start;
|
state->cursor = start;
|
||||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||||
x = row.x0;
|
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);
|
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||||
|
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||||
break;
|
break;
|
||||||
@@ -927,10 +985,13 @@ retry:
|
|||||||
x += dx;
|
x += dx;
|
||||||
if (x > goal_x)
|
if (x > goal_x)
|
||||||
break;
|
break;
|
||||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
i += next - state->cursor;
|
||||||
|
state->cursor = next;
|
||||||
}
|
}
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
|
|
||||||
|
if (state->cursor == find.first_char + find.length)
|
||||||
|
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||||
state->has_preferred_x = 1;
|
state->has_preferred_x = 1;
|
||||||
state->preferred_x = goal_x;
|
state->preferred_x = goal_x;
|
||||||
|
|
||||||
@@ -980,8 +1041,9 @@ retry:
|
|||||||
state->cursor = find.prev_first;
|
state->cursor = find.prev_first;
|
||||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||||
x = row.x0;
|
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);
|
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||||
|
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||||
break;
|
break;
|
||||||
@@ -989,10 +1051,15 @@ retry:
|
|||||||
x += dx;
|
x += dx;
|
||||||
if (x > goal_x)
|
if (x > goal_x)
|
||||||
break;
|
break;
|
||||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
i += next - state->cursor;
|
||||||
|
state->cursor = next;
|
||||||
}
|
}
|
||||||
stb_textedit_clamp(str, state);
|
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->has_preferred_x = 1;
|
||||||
state->preferred_x = goal_x;
|
state->preferred_x = goal_x;
|
||||||
|
|
||||||
@@ -1002,10 +1069,15 @@ retry:
|
|||||||
// go to previous line
|
// go to previous line
|
||||||
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
// (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;
|
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)
|
while (prev_scan > 0)
|
||||||
--prev_scan;
|
{
|
||||||
|
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.first_char = find.prev_first;
|
||||||
find.prev_first = prev_scan;
|
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1079,10 +1151,7 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINESTART:
|
case STB_TEXTEDIT_K_LINESTART:
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_move_to_first(state);
|
stb_textedit_move_to_first(state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||||
state->cursor = 0;
|
|
||||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
--state->cursor;
|
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1090,13 +1159,9 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINEEND2:
|
case STB_TEXTEDIT_K_LINEEND2:
|
||||||
#endif
|
#endif
|
||||||
case STB_TEXTEDIT_K_LINEEND: {
|
case STB_TEXTEDIT_K_LINEEND: {
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_move_to_first(state);
|
stb_textedit_move_to_last(str, state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||||
state->cursor = n;
|
|
||||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
++state->cursor;
|
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1107,10 +1172,7 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
|
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_prep_selection_at_cursor(state);
|
stb_textedit_prep_selection_at_cursor(state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||||
state->cursor = 0;
|
|
||||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
--state->cursor;
|
|
||||||
state->select_end = state->cursor;
|
state->select_end = state->cursor;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
@@ -1119,13 +1181,9 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
|
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
|
||||||
#endif
|
#endif
|
||||||
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
|
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_prep_selection_at_cursor(state);
|
stb_textedit_prep_selection_at_cursor(state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||||
state->cursor = n;
|
|
||||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
++state->cursor;
|
|
||||||
state->select_end = state->cursor;
|
state->select_end = state->cursor;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
@@ -1300,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||||||
// check type of recorded action:
|
// check type of recorded action:
|
||||||
if (u.insert_length) {
|
if (u.insert_length) {
|
||||||
// easy case: was a deletion, so we need to insert n characters
|
// 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;
|
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) {
|
if (r.insert_length) {
|
||||||
// easy case: need to insert n characters
|
// 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;
|
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
|
// 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);
|
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
|
// 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);
|
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[0] = (float)x2;
|
||||||
q2[1] = (float)y2;
|
q2[1] = (float)y2;
|
||||||
if (equal(q0,q1) || equal(q1,q2)) {
|
if (equal(q0,q1) || equal(q1,q2)) {
|
||||||
x0 = (int)verts[i-1].x;
|
x0 = (int)verts[i-1].x; //-V1048
|
||||||
y0 = (int)verts[i-1].y;
|
y0 = (int)verts[i-1].y; //-V1048
|
||||||
x1 = (int)verts[i ].x;
|
x1 = (int)verts[i ].x;
|
||||||
y1 = (int)verts[i ].y;
|
y1 = (int)verts[i ].y;
|
||||||
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
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("libaio-iob2_video.dll");
|
||||||
libutils::load_library("win10actlog.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),
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||||
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
||||||
|
|
||||||
|
|||||||
+113
-109
@@ -41,136 +41,140 @@ std::vector<Light> &games::ddr::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"P1 Foot Left",
|
{
|
||||||
"P1 Foot Up",
|
{"General", "P1 Foot Left"},
|
||||||
"P1 Foot Right",
|
{"General", "P1 Foot Up"},
|
||||||
"P1 Foot Down",
|
{"General", "P1 Foot Right"},
|
||||||
"P2 Foot Left",
|
{"General", "P1 Foot Down"},
|
||||||
"P2 Foot Up",
|
{"General", "P2 Foot Left"},
|
||||||
"P2 Foot Right",
|
{"General", "P2 Foot Up"},
|
||||||
"P2 Foot Down",
|
{"General", "P2 Foot Right"},
|
||||||
"Spot Red",
|
{"General", "P2 Foot Down"},
|
||||||
"Spot Blue",
|
|
||||||
"Top Spot Red",
|
|
||||||
"Top Spot Blue",
|
|
||||||
"P1 Halogen Upper",
|
|
||||||
"P1 Halogen Lower",
|
|
||||||
"P2 Halogen Upper",
|
|
||||||
"P2 Halogen Lower",
|
|
||||||
"P1 Button",
|
|
||||||
"P2 Button",
|
|
||||||
"Neon",
|
|
||||||
"HD P1 Start",
|
|
||||||
"HD P1 Menu Left-Right",
|
|
||||||
"HD P1 Menu Up-Down",
|
|
||||||
"HD P2 Start",
|
|
||||||
"HD P2 Menu Left-Right",
|
|
||||||
"HD P2 Menu Up-Down",
|
|
||||||
"HD P1 Speaker F R",
|
|
||||||
"HD P1 Speaker F G",
|
|
||||||
"HD P1 Speaker F B",
|
|
||||||
"HD P1 Speaker W R",
|
|
||||||
"HD P1 Speaker W G",
|
|
||||||
"HD P1 Speaker W B",
|
|
||||||
"HD P2 Speaker F R",
|
|
||||||
"HD P2 Speaker F G",
|
|
||||||
"HD P2 Speaker F B",
|
|
||||||
"HD P2 Speaker W R",
|
|
||||||
"HD P2 Speaker W G",
|
|
||||||
"HD P2 Speaker W B",
|
|
||||||
|
|
||||||
"WHITE Speaker Top R",
|
{"SD", "Spot Red"},
|
||||||
"WHITE Speaker Top G",
|
{"SD", "Spot Blue"},
|
||||||
"WHITE Speaker Top B",
|
{"SD", "Top Spot Red"},
|
||||||
"WHITE Speaker Bottom R",
|
{"SD", "Top Spot Blue"},
|
||||||
"WHITE Speaker Bottom G",
|
{"SD", "P1 Halogen Upper"},
|
||||||
"WHITE Speaker Bottom B",
|
{"SD", "P1 Halogen Lower"},
|
||||||
"WHITE Woofer R",
|
{"SD", "P2 Halogen Upper"},
|
||||||
"WHITE Woofer G",
|
{"SD", "P2 Halogen Lower"},
|
||||||
"WHITE Woofer B",
|
{"SD", "P1 Button"},
|
||||||
|
{"SD", "P2 Button"},
|
||||||
|
{"SD", "Neon"},
|
||||||
|
|
||||||
"GOLD P1 Menu Start",
|
{"HD", "HD P1 Start"},
|
||||||
"GOLD P1 Menu Up",
|
{"HD", "HD P1 Menu Left-Right"},
|
||||||
"GOLD P1 Menu Down",
|
{"HD", "HD P1 Menu Up-Down"},
|
||||||
"GOLD P1 Menu Left",
|
{"HD", "HD P2 Start"},
|
||||||
"GOLD P1 Menu Right",
|
{"HD", "HD P2 Menu Left-Right"},
|
||||||
|
{"HD", "HD P2 Menu Up-Down"},
|
||||||
|
{"HD", "HD P1 Speaker F R"},
|
||||||
|
{"HD", "HD P1 Speaker F G"},
|
||||||
|
{"HD", "HD P1 Speaker F B"},
|
||||||
|
{"HD", "HD P1 Speaker W R"},
|
||||||
|
{"HD", "HD P1 Speaker W G"},
|
||||||
|
{"HD", "HD P1 Speaker W B"},
|
||||||
|
{"HD", "HD P2 Speaker F R"},
|
||||||
|
{"HD", "HD P2 Speaker F G"},
|
||||||
|
{"HD", "HD P2 Speaker F B"},
|
||||||
|
{"HD", "HD P2 Speaker W R"},
|
||||||
|
{"HD", "HD P2 Speaker W G"},
|
||||||
|
{"HD", "HD P2 Speaker W B"},
|
||||||
|
|
||||||
"GOLD P2 Menu Start",
|
{"WHITE", "WHITE Speaker Top R"},
|
||||||
"GOLD P2 Menu Up",
|
{"WHITE", "WHITE Speaker Top G"},
|
||||||
"GOLD P2 Menu Down",
|
{"WHITE", "WHITE Speaker Top B"},
|
||||||
"GOLD P2 Menu Left",
|
{"WHITE", "WHITE Speaker Bottom R"},
|
||||||
"GOLD P2 Menu Right",
|
{"WHITE", "WHITE Speaker Bottom G"},
|
||||||
|
{"WHITE", "WHITE Speaker Bottom B"},
|
||||||
|
{"WHITE", "WHITE Woofer R"},
|
||||||
|
{"WHITE", "WHITE Woofer G"},
|
||||||
|
{"WHITE", "WHITE Woofer B"},
|
||||||
|
|
||||||
"GOLD Title Panel Left",
|
{"GOLD", "GOLD P1 Menu Start"},
|
||||||
"GOLD Title Panel Center",
|
{"GOLD", "GOLD P1 Menu Up"},
|
||||||
"GOLD Title Panel Right",
|
{"GOLD", "GOLD P1 Menu Down"},
|
||||||
|
{"GOLD", "GOLD P1 Menu Left"},
|
||||||
|
{"GOLD", "GOLD P1 Menu Right"},
|
||||||
|
|
||||||
"GOLD P1 Woofer Corner",
|
{"GOLD", "GOLD P2 Menu Start"},
|
||||||
"GOLD P2 Woofer Corner",
|
{"GOLD", "GOLD P2 Menu Up"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Down"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Left"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Right"},
|
||||||
|
|
||||||
"GOLD P1 Card Unit R",
|
{"GOLD", "GOLD Title Panel Left"},
|
||||||
"GOLD P1 Card Unit G",
|
{"GOLD", "GOLD Title Panel Center"},
|
||||||
"GOLD P1 Card Unit B",
|
{"GOLD", "GOLD Title Panel Right"},
|
||||||
|
|
||||||
"GOLD P2 Card Unit R",
|
{"GOLD", "GOLD P1 Woofer Corner"},
|
||||||
"GOLD P2 Card Unit G",
|
{"GOLD", "GOLD P2 Woofer Corner"},
|
||||||
"GOLD P2 Card Unit B",
|
|
||||||
|
|
||||||
"GOLD Top Panel Avg R",
|
{"GOLD", "GOLD P1 Card Unit R"},
|
||||||
"GOLD Top Panel Avg G",
|
{"GOLD", "GOLD P1 Card Unit G"},
|
||||||
"GOLD Top Panel Avg B",
|
{"GOLD", "GOLD P1 Card Unit B"},
|
||||||
|
|
||||||
"GOLD Monitor Side Left Avg R",
|
{"GOLD", "GOLD P2 Card Unit R"},
|
||||||
"GOLD Monitor Side Left Avg G",
|
{"GOLD", "GOLD P2 Card Unit G"},
|
||||||
"GOLD Monitor Side Left Avg B",
|
{"GOLD", "GOLD P2 Card Unit B"},
|
||||||
|
|
||||||
"GOLD Monitor Side Right Avg R",
|
{"GOLD", "GOLD Top Panel Avg R"},
|
||||||
"GOLD Monitor Side Right Avg G",
|
{"GOLD", "GOLD Top Panel Avg G"},
|
||||||
"GOLD Monitor Side Right Avg B",
|
{"GOLD", "GOLD Top Panel Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Up Avg R",
|
{"GOLD", "GOLD Monitor Side Left Avg R"},
|
||||||
"GOLD P1 Foot Up Avg G",
|
{"GOLD", "GOLD Monitor Side Left Avg G"},
|
||||||
"GOLD P1 Foot Up Avg B",
|
{"GOLD", "GOLD Monitor Side Left Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Down Avg R",
|
{"GOLD", "GOLD Monitor Side Right Avg R"},
|
||||||
"GOLD P1 Foot Down Avg G",
|
{"GOLD", "GOLD Monitor Side Right Avg G"},
|
||||||
"GOLD P1 Foot Down Avg B",
|
{"GOLD", "GOLD Monitor Side Right Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Left Avg R",
|
{"GOLD", "GOLD P1 Foot Up Avg R"},
|
||||||
"GOLD P1 Foot Left Avg G",
|
{"GOLD", "GOLD P1 Foot Up Avg G"},
|
||||||
"GOLD P1 Foot Left Avg B",
|
{"GOLD", "GOLD P1 Foot Up Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Right Avg R",
|
{"GOLD", "GOLD P1 Foot Down Avg R"},
|
||||||
"GOLD P1 Foot Right Avg G",
|
{"GOLD", "GOLD P1 Foot Down Avg G"},
|
||||||
"GOLD P1 Foot Right Avg B",
|
{"GOLD", "GOLD P1 Foot Down Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Up Avg R",
|
{"GOLD", "GOLD P1 Foot Left Avg R"},
|
||||||
"GOLD P2 Foot Up Avg G",
|
{"GOLD", "GOLD P1 Foot Left Avg G"},
|
||||||
"GOLD P2 Foot Up Avg B",
|
{"GOLD", "GOLD P1 Foot Left Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Down Avg R",
|
{"GOLD", "GOLD P1 Foot Right Avg R"},
|
||||||
"GOLD P2 Foot Down Avg G",
|
{"GOLD", "GOLD P1 Foot Right Avg G"},
|
||||||
"GOLD P2 Foot Down Avg B",
|
{"GOLD", "GOLD P1 Foot Right Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Left Avg R",
|
{"GOLD", "GOLD P2 Foot Up Avg R"},
|
||||||
"GOLD P2 Foot Left Avg G",
|
{"GOLD", "GOLD P2 Foot Up Avg G"},
|
||||||
"GOLD P2 Foot Left Avg B",
|
{"GOLD", "GOLD P2 Foot Up Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Right Avg R",
|
{"GOLD", "GOLD P2 Foot Down Avg R"},
|
||||||
"GOLD P2 Foot Right Avg G",
|
{"GOLD", "GOLD P2 Foot Down Avg G"},
|
||||||
"GOLD P2 Foot Right Avg B",
|
{"GOLD", "GOLD P2 Foot Down Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Stage Corner Up-Left",
|
{"GOLD", "GOLD P2 Foot Left Avg R"},
|
||||||
"GOLD P1 Stage Corner Up-Right",
|
{"GOLD", "GOLD P2 Foot Left Avg G"},
|
||||||
"GOLD P1 Stage Corner Down-Left",
|
{"GOLD", "GOLD P2 Foot Left Avg B"},
|
||||||
"GOLD P1 Stage Corner Down-Right",
|
|
||||||
|
|
||||||
"GOLD P2 Stage Corner Up-Left",
|
{"GOLD", "GOLD P2 Foot Right Avg R"},
|
||||||
"GOLD P2 Stage Corner Up-Right",
|
{"GOLD", "GOLD P2 Foot Right Avg G"},
|
||||||
"GOLD P2 Stage Corner Down-Left",
|
{"GOLD", "GOLD P2 Foot Right Avg B"},
|
||||||
"GOLD P2 Stage Corner Down-Right"
|
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Up-Left"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Up-Right"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Down-Left"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Down-Right"},
|
||||||
|
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Up-Left"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Up-Right"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Down-Left"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Down-Right"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ int games::ddr::DDRP3IOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use both sat spots for a neon pulse
|
// use both sat spots for a neon pulse
|
||||||
float value_neon = (light_bits & hd_mapping_bits[0] && hd_mapping_bits[1]) ? 1.f : 0.f;
|
float value_neon = (light_bits & hd_mapping_bits[0] && light_bits & hd_mapping_bits[1]) ? 1.f : 0.f;
|
||||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,100 +158,102 @@ std::vector<Light> &games::gitadora::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("GitaDora");
|
lights = GameAPI::Lights::getLights("GitaDora");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(&lights,
|
GameAPI::Lights::sortLightsWithCategory(&lights,
|
||||||
"Guitar P1 Motor",
|
{
|
||||||
"Guitar P2 Motor",
|
{"Guitar", "Guitar P1 Motor"},
|
||||||
|
{"Guitar", "Guitar P2 Motor"},
|
||||||
|
|
||||||
"P1 Start",
|
{"Menu", "P1 Start"},
|
||||||
"P1 Menu Up Down (DX)",
|
{"Menu", "P1 Menu Up Down (DX)"},
|
||||||
"P1 Menu Left Right (DX)",
|
{"Menu", "P1 Menu Left Right (DX)"},
|
||||||
"P1 Help (DX)",
|
{"Menu", "P1 Help (DX)"},
|
||||||
|
|
||||||
"P2 Start",
|
{"Menu", "P2 Start"},
|
||||||
"P2 Menu Up Down (DX)",
|
{"Menu", "P2 Menu Up Down (DX)"},
|
||||||
"P2 Menu Left Right (DX)",
|
{"Menu", "P2 Menu Left Right (DX)"},
|
||||||
"P2 Help (DX)",
|
{"Menu", "P2 Help (DX)"},
|
||||||
|
|
||||||
"Drum Left Cymbal",
|
{"Drums", "Drum Left Cymbal"},
|
||||||
"Drum Hi-Hat",
|
{"Drums", "Drum Hi-Hat"},
|
||||||
"Drum Snare",
|
{"Drums", "Drum Snare"},
|
||||||
"Drum High Tom",
|
{"Drums", "Drum High Tom"},
|
||||||
"Drum Low Tom",
|
{"Drums", "Drum Low Tom"},
|
||||||
"Drum Floor Tom",
|
{"Drums", "Drum Floor Tom"},
|
||||||
"Drum Right Cymbal",
|
{"Drums", "Drum Right Cymbal"},
|
||||||
|
|
||||||
"Drum Woofer R",
|
{"DX/SD", "Drum Woofer R"},
|
||||||
"Drum Woofer G",
|
{"DX/SD", "Drum Woofer G"},
|
||||||
"Drum Woofer B",
|
{"DX/SD", "Drum Woofer B"},
|
||||||
|
|
||||||
"Drum Stage R (DX)",
|
{"DX/SD", "Drum Stage R (DX)"},
|
||||||
"Drum Stage G (DX)",
|
{"DX/SD", "Drum Stage G (DX)"},
|
||||||
"Drum Stage B (DX)",
|
{"DX/SD", "Drum Stage B (DX)"},
|
||||||
|
|
||||||
"Spot Left (DX)",
|
{"DX/SD", "Spot Left (DX)"},
|
||||||
"Spot Right (DX)",
|
{"DX/SD", "Spot Right (DX)"},
|
||||||
"Spot Center Left (DX)",
|
{"DX/SD", "Spot Center Left (DX)"},
|
||||||
"Spot Center Right (DX)",
|
{"DX/SD", "Spot Center Right (DX)"},
|
||||||
|
|
||||||
"Drum Spot Rear Left (DX)",
|
{"DX/SD", "Drum Spot Rear Left (DX)"},
|
||||||
"Drum Spot Rear Right (DX)",
|
{"DX/SD", "Drum Spot Rear Right (DX)"},
|
||||||
|
|
||||||
"Guitar Lower Left R (DX)",
|
{"DX/SD", "Guitar Lower Left R (DX)"},
|
||||||
"Guitar Lower Left G (DX)",
|
{"DX/SD", "Guitar Lower Left G (DX)"},
|
||||||
"Guitar Lower Left B (DX)",
|
{"DX/SD", "Guitar Lower Left B (DX)"},
|
||||||
"Guitar Lower Right R (DX)",
|
{"DX/SD", "Guitar Lower Right R (DX)"},
|
||||||
"Guitar Lower Right G (DX)",
|
{"DX/SD", "Guitar Lower Right G (DX)"},
|
||||||
"Guitar Lower Right B (DX)",
|
{"DX/SD", "Guitar Lower Right B (DX)"},
|
||||||
|
|
||||||
"Guitar Left Speaker Upper R (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper R (DX)"},
|
||||||
"Guitar Left Speaker Upper G (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper G (DX)"},
|
||||||
"Guitar Left Speaker Upper B (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper B (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left R (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left G (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left B (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right R (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right G (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right B (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left R (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left G (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left B (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right R (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right G (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right B (DX)"},
|
||||||
"Guitar Left Speaker Lower R (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower R (DX)"},
|
||||||
"Guitar Left Speaker Lower G (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower G (DX)"},
|
||||||
"Guitar Left Speaker Lower B (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower B (DX)"},
|
||||||
|
|
||||||
"Guitar Right Speaker Upper R (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper R (DX)"},
|
||||||
"Guitar Right Speaker Upper G (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper G (DX)"},
|
||||||
"Guitar Right Speaker Upper B (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper B (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left R (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left G (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left B (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right R (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right G (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right B (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left R (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left G (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left B (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right R (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right G (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right B (DX)"},
|
||||||
"Guitar Right Speaker Lower R (DX)",
|
{"DX/SD", "Guitar Right Speaker Lower R (DX)"},
|
||||||
"Guitar Right Speaker Lower G (DX)",
|
{"DX/SD", "Guitar Right Speaker Lower G (DX)"},
|
||||||
"Guitar Right Speaker Lower B (DX)",
|
{"DX/SD", "Guitar Right Speaker Lower B (DX)"},
|
||||||
|
|
||||||
"Title Average R (Arena)",
|
{"Arena Model", "Title Average R (Arena)"},
|
||||||
"Title Average G (Arena)",
|
{"Arena Model", "Title Average G (Arena)"},
|
||||||
"Title Average B (Arena)",
|
{"Arena Model", "Title Average B (Arena)"},
|
||||||
|
|
||||||
"Woofer R (Arena)",
|
{"Arena Model", "Woofer R (Arena)"},
|
||||||
"Woofer G (Arena)",
|
{"Arena Model", "Woofer G (Arena)"},
|
||||||
"Woofer B (Arena)",
|
{"Arena Model", "Woofer B (Arena)"},
|
||||||
|
|
||||||
"Card Reader R (Arena)",
|
{"Arena Model", "Card Reader R (Arena)"},
|
||||||
"Card Reader G (Arena)",
|
{"Arena Model", "Card Reader G (Arena)"},
|
||||||
"Card Reader B (Arena)"
|
{"Arena Model", "Card Reader B (Arena)"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ namespace games::iidx {
|
|||||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||||
std::mutex IIDX_LED_TICKER_LOCK;
|
std::mutex IIDX_LED_TICKER_LOCK;
|
||||||
bool IIDX_TDJ_MONITOR_WARNING = false;
|
bool IIDX_TDJ_MONITOR_WARNING = false;
|
||||||
|
iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE = iidx_aio_emulation_state::unknown;
|
||||||
|
|
||||||
// io
|
// io
|
||||||
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
||||||
@@ -453,7 +454,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// windowed subscreen, enabled by default, unless turned off by user
|
// windowed subscreen, enabled by default, unless turned off by user
|
||||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
|
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool() && TDJ_MODE) {
|
||||||
GRAPHICS_IIDX_WSUB = true;
|
GRAPHICS_IIDX_WSUB = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,7 +818,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_tdj_fhd() {
|
bool is_tdj_fhd() {
|
||||||
return TDJ_MODE && avs::game::is_ext(2022101900, MAXINT);
|
return TDJ_MODE && avs::game::is_ext(2022101900, INT_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_audio_hacks() {
|
void apply_audio_hacks() {
|
||||||
@@ -885,7 +886,7 @@ namespace games::iidx {
|
|||||||
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
||||||
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
||||||
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
||||||
if (avs::game::is_ext(2024090100, MAXINT) &&
|
if (avs::game::is_ext(2024090100, INT_MAX) &&
|
||||||
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
||||||
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
||||||
|
|
||||||
@@ -934,6 +935,8 @@ namespace games::iidx {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CURRENT_IO_EMULATION_STATE = state;
|
||||||
|
|
||||||
// log_warning below could be turned into log_fatal in the future once we gain confidence
|
// log_warning below could be turned into log_fatal in the future once we gain confidence
|
||||||
// that this isn't triggering when it's not supposed to
|
// that this isn't triggering when it's not supposed to
|
||||||
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
||||||
|
|||||||
@@ -11,6 +11,12 @@
|
|||||||
|
|
||||||
namespace games::iidx {
|
namespace games::iidx {
|
||||||
|
|
||||||
|
enum class iidx_aio_emulation_state {
|
||||||
|
unknown,
|
||||||
|
bi2a_com2,
|
||||||
|
bi2x_hook
|
||||||
|
};
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
|
|
||||||
extern bool FLIP_CAMS;
|
extern bool FLIP_CAMS;
|
||||||
@@ -38,6 +44,7 @@ namespace games::iidx {
|
|||||||
constexpr int IIDX_TAPELED_TOTAL = 17;
|
constexpr int IIDX_TAPELED_TOTAL = 17;
|
||||||
// data mapping
|
// data mapping
|
||||||
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
||||||
|
extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE;
|
||||||
|
|
||||||
class IIDXGame : public games::Game {
|
class IIDXGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
@@ -64,9 +71,5 @@ namespace games::iidx {
|
|||||||
bool is_tdj_fhd();
|
bool is_tdj_fhd();
|
||||||
void apply_audio_hacks();
|
void apply_audio_hacks();
|
||||||
|
|
||||||
enum class iidx_aio_emulation_state {
|
|
||||||
bi2a_com2,
|
|
||||||
bi2x_hook
|
|
||||||
};
|
|
||||||
void update_io_emulation_state(iidx_aio_emulation_state state);
|
void update_io_emulation_state(iidx_aio_emulation_state state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,103 +83,105 @@ std::vector<Light> &games::iidx::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"P1 1",
|
{
|
||||||
"P1 2",
|
{"Common", "P1 1"},
|
||||||
"P1 3",
|
{"Common", "P1 2"},
|
||||||
"P1 4",
|
{"Common", "P1 3"},
|
||||||
"P1 5",
|
{"Common", "P1 4"},
|
||||||
"P1 6",
|
{"Common", "P1 5"},
|
||||||
"P1 7",
|
{"Common", "P1 6"},
|
||||||
"P2 1",
|
{"Common", "P1 7"},
|
||||||
"P2 2",
|
{"Common", "P2 1"},
|
||||||
"P2 3",
|
{"Common", "P2 2"},
|
||||||
"P2 4",
|
{"Common", "P2 3"},
|
||||||
"P2 5",
|
{"Common", "P2 4"},
|
||||||
"P2 6",
|
{"Common", "P2 5"},
|
||||||
"P2 7",
|
{"Common", "P2 6"},
|
||||||
"P1 Start",
|
{"Common", "P2 7"},
|
||||||
"P2 Start",
|
{"Common", "P1 Start"},
|
||||||
"VEFX",
|
{"Common", "P2 Start"},
|
||||||
"Effect",
|
{"Common", "VEFX"},
|
||||||
"Spot Light 1",
|
{"Common", "Effect"},
|
||||||
"Spot Light 2",
|
{"DX", "Spot Light 1"},
|
||||||
"Spot Light 3",
|
{"DX", "Spot Light 2"},
|
||||||
"Spot Light 4",
|
{"DX", "Spot Light 3"},
|
||||||
"Spot Light 5",
|
{"DX", "Spot Light 4"},
|
||||||
"Spot Light 6",
|
{"DX", "Spot Light 5"},
|
||||||
"Spot Light 7",
|
{"DX", "Spot Light 6"},
|
||||||
"Spot Light 8",
|
{"DX", "Spot Light 7"},
|
||||||
"Neon Lamp",
|
{"DX", "Spot Light 8"},
|
||||||
"Woofer R",
|
{"DX", "Neon Lamp"},
|
||||||
"Woofer G",
|
{"Lightning Model", "Woofer R"},
|
||||||
"Woofer B",
|
{"Lightning Model", "Woofer G"},
|
||||||
"IC Card Reader P1 R",
|
{"Lightning Model", "Woofer B"},
|
||||||
"IC Card Reader P1 G",
|
{"Lightning Model", "IC Card Reader P1 R"},
|
||||||
"IC Card Reader P1 B",
|
{"Lightning Model", "IC Card Reader P1 G"},
|
||||||
"IC Card Reader P2 R",
|
{"Lightning Model", "IC Card Reader P1 B"},
|
||||||
"IC Card Reader P2 G",
|
{"Lightning Model", "IC Card Reader P2 R"},
|
||||||
"IC Card Reader P2 B",
|
{"Lightning Model", "IC Card Reader P2 G"},
|
||||||
"Turntable P1 R",
|
{"Lightning Model", "IC Card Reader P2 B"},
|
||||||
"Turntable P1 G",
|
{"Lightning Model", "Turntable P1 R"},
|
||||||
"Turntable P1 B",
|
{"Lightning Model", "Turntable P1 G"},
|
||||||
"Turntable P2 R",
|
{"Lightning Model", "Turntable P1 B"},
|
||||||
"Turntable P2 G",
|
{"Lightning Model", "Turntable P2 R"},
|
||||||
"Turntable P2 B",
|
{"Lightning Model", "Turntable P2 G"},
|
||||||
"Turntable P1 Resistance",
|
{"Lightning Model", "Turntable P2 B"},
|
||||||
"Turntable P2 Resistance",
|
{"Lightning Model", "Turntable P1 Resistance"},
|
||||||
"Stage Left Avg R",
|
{"Lightning Model", "Turntable P2 Resistance"},
|
||||||
"Stage Left Avg G",
|
{"Lightning Model", "Stage Left Avg R"},
|
||||||
"Stage Left Avg B",
|
{"Lightning Model", "Stage Left Avg G"},
|
||||||
"Stage Right Avg R",
|
{"Lightning Model", "Stage Left Avg B"},
|
||||||
"Stage Right Avg G",
|
{"Lightning Model", "Stage Right Avg R"},
|
||||||
"Stage Right Avg B",
|
{"Lightning Model", "Stage Right Avg G"},
|
||||||
"Cabinet Left Avg R",
|
{"Lightning Model", "Stage Right Avg B"},
|
||||||
"Cabinet Left Avg G",
|
{"Lightning Model", "Cabinet Left Avg R"},
|
||||||
"Cabinet Left Avg B",
|
{"Lightning Model", "Cabinet Left Avg G"},
|
||||||
"Cabinet Right Avg R",
|
{"Lightning Model", "Cabinet Left Avg B"},
|
||||||
"Cabinet Right Avg G",
|
{"Lightning Model", "Cabinet Right Avg R"},
|
||||||
"Cabinet Right Avg B",
|
{"Lightning Model", "Cabinet Right Avg G"},
|
||||||
"Control Panel Under Avg R",
|
{"Lightning Model", "Cabinet Right Avg B"},
|
||||||
"Control Panel Under Avg G",
|
{"Lightning Model", "Control Panel Under Avg R"},
|
||||||
"Control Panel Under Avg B",
|
{"Lightning Model", "Control Panel Under Avg G"},
|
||||||
"Ceiling Left Avg R",
|
{"Lightning Model", "Control Panel Under Avg B"},
|
||||||
"Ceiling Left Avg G",
|
{"Lightning Model", "Ceiling Left Avg R"},
|
||||||
"Ceiling Left Avg B",
|
{"Lightning Model", "Ceiling Left Avg G"},
|
||||||
"Title Left Avg R",
|
{"Lightning Model", "Ceiling Left Avg B"},
|
||||||
"Title Left Avg G",
|
{"Lightning Model", "Title Left Avg R"},
|
||||||
"Title Left Avg B",
|
{"Lightning Model", "Title Left Avg G"},
|
||||||
"Title Right Avg R",
|
{"Lightning Model", "Title Left Avg B"},
|
||||||
"Title Right Avg G",
|
{"Lightning Model", "Title Right Avg R"},
|
||||||
"Title Right Avg B",
|
{"Lightning Model", "Title Right Avg G"},
|
||||||
"Ceiling Right Avg R",
|
{"Lightning Model", "Title Right Avg B"},
|
||||||
"Ceiling Right Avg G",
|
{"Lightning Model", "Ceiling Right Avg R"},
|
||||||
"Ceiling Right Avg B",
|
{"Lightning Model", "Ceiling Right Avg G"},
|
||||||
"Touch Panel Left Avg R",
|
{"Lightning Model", "Ceiling Right Avg B"},
|
||||||
"Touch Panel Left Avg G",
|
{"Lightning Model", "Touch Panel Left Avg R"},
|
||||||
"Touch Panel Left Avg B",
|
{"Lightning Model", "Touch Panel Left Avg G"},
|
||||||
"Touch Panel Right Avg R",
|
{"Lightning Model", "Touch Panel Left Avg B"},
|
||||||
"Touch Panel Right Avg G",
|
{"Lightning Model", "Touch Panel Right Avg R"},
|
||||||
"Touch Panel Right Avg B",
|
{"Lightning Model", "Touch Panel Right Avg G"},
|
||||||
"Side Panel Left Inner Avg R",
|
{"Lightning Model", "Touch Panel Right Avg B"},
|
||||||
"Side Panel Left Inner Avg G",
|
{"Lightning Model", "Side Panel Left Inner Avg R"},
|
||||||
"Side Panel Left Inner Avg B",
|
{"Lightning Model", "Side Panel Left Inner Avg G"},
|
||||||
"Side Panel Left Outer Avg R",
|
{"Lightning Model", "Side Panel Left Inner Avg B"},
|
||||||
"Side Panel Left Outer Avg G",
|
{"Lightning Model", "Side Panel Left Outer Avg R"},
|
||||||
"Side Panel Left Outer Avg B",
|
{"Lightning Model", "Side Panel Left Outer Avg G"},
|
||||||
"Side Panel Left Avg R",
|
{"Lightning Model", "Side Panel Left Outer Avg B"},
|
||||||
"Side Panel Left Avg G",
|
{"Lightning Model", "Side Panel Left Avg R"},
|
||||||
"Side Panel Left Avg B",
|
{"Lightning Model", "Side Panel Left Avg G"},
|
||||||
"Side Panel Right Outer Avg R",
|
{"Lightning Model", "Side Panel Left Avg B"},
|
||||||
"Side Panel Right Outer Avg G",
|
{"Lightning Model", "Side Panel Right Outer Avg R"},
|
||||||
"Side Panel Right Outer Avg B",
|
{"Lightning Model", "Side Panel Right Outer Avg G"},
|
||||||
"Side Panel Right Inner Avg R",
|
{"Lightning Model", "Side Panel Right Outer Avg B"},
|
||||||
"Side Panel Right Inner Avg G",
|
{"Lightning Model", "Side Panel Right Inner Avg R"},
|
||||||
"Side Panel Right Inner Avg B",
|
{"Lightning Model", "Side Panel Right Inner Avg G"},
|
||||||
"Side Panel Right Avg R",
|
{"Lightning Model", "Side Panel Right Inner Avg B"},
|
||||||
"Side Panel Right Avg G",
|
{"Lightning Model", "Side Panel Right Avg R"},
|
||||||
"Side Panel Right Avg B"
|
{"Lightning Model", "Side Panel Right Avg G"},
|
||||||
|
{"Lightning Model", "Side Panel Right Avg B"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace games::iidx {
|
|||||||
protected:
|
protected:
|
||||||
virtual ~IIDXLocalCamera() {};
|
virtual ~IIDXLocalCamera() {};
|
||||||
|
|
||||||
ULONG m_nRefCount;
|
LONG m_nRefCount;
|
||||||
CRITICAL_SECTION m_critsec;
|
CRITICAL_SECTION m_critsec;
|
||||||
|
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ namespace games {
|
|||||||
const std::string otoca("Otoca D'or");
|
const std::string otoca("Otoca D'or");
|
||||||
games.push_back(otoca);
|
games.push_back(otoca);
|
||||||
buttons.insert({ otoca, otoca::get_buttons() });
|
buttons.insert({ otoca, otoca::get_buttons() });
|
||||||
|
buttons_help.insert({ otoca, otoca::get_buttons_help() });
|
||||||
file_hints[otoca].emplace_back("arkkep.dll");
|
file_hints[otoca].emplace_back("arkkep.dll");
|
||||||
|
|
||||||
// winning eleven
|
// winning eleven
|
||||||
@@ -400,7 +401,8 @@ namespace games {
|
|||||||
vkey_defaults.push_back(unit == 0 ? VK_NUMPAD9 : 0xFF);
|
vkey_defaults.push_back(unit == 0 ? VK_NUMPAD9 : 0xFF);
|
||||||
|
|
||||||
names.emplace_back(prefix + "00");
|
names.emplace_back(prefix + "00");
|
||||||
vkey_defaults.push_back(unit == 0 ? VK_RETURN : 0xFF);
|
// this used to be VK_RETURN, but that shares a value with normal Enter key
|
||||||
|
vkey_defaults.push_back(unit == 0 ? VK_SUBTRACT : 0xFF);
|
||||||
|
|
||||||
names.emplace_back(prefix + "Decimal");
|
names.emplace_back(prefix + "Decimal");
|
||||||
vkey_defaults.push_back(unit == 0 ? VK_DECIMAL : 0xFF);
|
vkey_defaults.push_back(unit == 0 ? VK_DECIMAL : 0xFF);
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ std::vector<Button> &games::otoca::get_buttons() {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string games::otoca::get_buttons_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return " BT_Left Lever BT_Right";
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Light> &games::otoca::get_lights() {
|
std::vector<Light> &games::otoca::get_lights() {
|
||||||
static std::vector<Light> lights;
|
static std::vector<Light> lights;
|
||||||
|
|
||||||
|
|||||||
@@ -30,5 +30,6 @@ namespace games::otoca {
|
|||||||
|
|
||||||
// getters
|
// getters
|
||||||
std::vector<Button> &get_buttons();
|
std::vector<Button> &get_buttons();
|
||||||
|
std::string get_buttons_help();
|
||||||
std::vector<Light> &get_lights();
|
std::vector<Light> &get_lights();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,121 @@
|
|||||||
#include "otoca.h"
|
#include "otoca.h"
|
||||||
#include "p4io.h"
|
#include "p4io.h"
|
||||||
#include "games/shared/printer.h"
|
#include "games/shared/printer.h"
|
||||||
|
#include "util/detour.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
#define OTOCA_DEBUG_VERBOSE 0
|
||||||
|
#if OTOCA_DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace games::otoca {
|
namespace games::otoca {
|
||||||
|
|
||||||
|
bool BYPASS_CAMERA = false;
|
||||||
|
|
||||||
|
// for dev only
|
||||||
|
static bool CAMLIB_CALL_ORIGINAL = false;
|
||||||
|
|
||||||
OtocaGame::OtocaGame() : Game("Otoca D'or") {
|
OtocaGame::OtocaGame() : Game("Otoca D'or") {
|
||||||
}
|
}
|
||||||
|
|
||||||
OtocaGame::~OtocaGame() {
|
OtocaGame::~OtocaGame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef void (__cdecl *LibCameraInit_t)();
|
||||||
|
static LibCameraInit_t LibCameraInit_orig = nullptr;
|
||||||
|
static void __cdecl LibCameraInit_hook() {
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
LibCameraInit_orig();
|
||||||
|
log_debug("otoca::cam", "LibCameraInit returned");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraOpen_t)(double, int, int);
|
||||||
|
static LibCameraOpen_t LibCameraOpen_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraOpen_hook(double a1, int a2, int a3) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (15, 2, 0) returned 0
|
||||||
|
ret = LibCameraOpen_orig(a1, a2, a3);
|
||||||
|
log_debug("otoca::cam", "LibCameraOpen({}, {}, {}) returned {}", a1, a2, a3, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraStop_t)(int);
|
||||||
|
static LibCameraStop_t LibCameraStop_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraStop_hook(int a1) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam,
|
||||||
|
ret = LibCameraStop_orig(a1);
|
||||||
|
log_debug("otoca::cam", "LibCameraStop({}) returned {}", a1, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (__cdecl *LibCameraGetCameraNr_t)();
|
||||||
|
static LibCameraGetCameraNr_t LibCameraGetCameraNr_orig = nullptr;
|
||||||
|
static void __cdecl LibCameraGetCameraNr_hook() {
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
LibCameraGetCameraNr_orig();
|
||||||
|
log_debug("otoca::cam", "LibCameraGetCameraNr returned");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraRun_t)(int);
|
||||||
|
static LibCameraRun_t LibCameraRun_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraRun_hook(int a1) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (0) returned 0
|
||||||
|
ret = LibCameraRun_orig(a1);
|
||||||
|
log_debug("otoca::cam", "LibCameraRun({}) returned {}", a1, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraGetImage_t)(int, void *);
|
||||||
|
static LibCameraGetImage_t LibCameraGetImage_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraGetImage_hook(int a1, void *a2) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// not sure if this is called
|
||||||
|
ret = LibCameraGetImage_orig(a1, a2);
|
||||||
|
log_debug("otoca::cam", "LibCameraGetImage({}, {}) returned {}", a1, fmt::ptr(a2), ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraGetBrightness_t)(int *, int);
|
||||||
|
static LibCameraGetBrightness_t LibCameraGetBrightness_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraGetBrightness_hook(int *a1, int a2) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (ptr,) returned
|
||||||
|
ret = LibCameraGetBrightness_orig(a1, a2);
|
||||||
|
}
|
||||||
|
log_debug("otoca::cam", "LibCameraGetBrightness({}, {}) returned {}", fmt::ptr(a1), a2, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (__cdecl *LibCameraGetBrightnessRange_t)(int *, int *, int);
|
||||||
|
static LibCameraGetBrightnessRange_t LibCameraGetBrightnessRange_orig = nullptr;
|
||||||
|
static int __cdecl LibCameraGetBrightnessRange_hook(int *a1, int *a2, int a3) {
|
||||||
|
int ret = 0;
|
||||||
|
if (CAMLIB_CALL_ORIGINAL) {
|
||||||
|
// with webcam, (ptr, ptr, 0) returned 0
|
||||||
|
ret = LibCameraGetBrightnessRange_orig(a1, a2, a3);
|
||||||
|
log_debug("otoca::cam", "LibCameraGetBrightnessRange({}, {}, {}) returned {}", fmt::ptr(a1), fmt::ptr(a2), a3, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void OtocaGame::attach() {
|
void OtocaGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
@@ -23,5 +128,42 @@ namespace games::otoca {
|
|||||||
// initialize IO hooks
|
// initialize IO hooks
|
||||||
p4io_hook();
|
p4io_hook();
|
||||||
games::shared::printer_attach();
|
games::shared::printer_attach();
|
||||||
|
|
||||||
|
if (BYPASS_CAMERA) {
|
||||||
|
libutils::try_library("libcamera.dll");
|
||||||
|
const auto libcamera = "libcamera.dll";
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraInit@@YAXXZ",
|
||||||
|
LibCameraInit_hook, &LibCameraInit_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraOpen@@YA?AW4LIBCAMERA_STATUS@@NHPAUt_libcamera_open_param@@@Z",
|
||||||
|
LibCameraOpen_hook, &LibCameraOpen_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraStop@@YA?AW4LIBCAMERA_STATUS@@H@Z",
|
||||||
|
LibCameraStop_hook, &LibCameraStop_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetCameraNr@@YAHXZ",
|
||||||
|
LibCameraGetCameraNr_hook, &LibCameraGetCameraNr_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraRun@@YA?AW4LIBCAMERA_STATUS@@H@Z",
|
||||||
|
LibCameraRun_hook, &LibCameraRun_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetImage@@YA?AW4LIBCAMERA_STATUS@@HPAX@Z",
|
||||||
|
LibCameraGetImage_hook, &LibCameraGetImage_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetBrightness@@YA?AW4LIBCAMERA_STATUS@@PAJH@Z",
|
||||||
|
LibCameraGetBrightness_hook, &LibCameraGetBrightness_orig);
|
||||||
|
detour::trampoline_try(
|
||||||
|
libcamera,
|
||||||
|
"?LibCameraGetBrightnessRange@@YA?AW4LIBCAMERA_STATUS@@PAJ0H@Z",
|
||||||
|
LibCameraGetBrightnessRange_hook, &LibCameraGetBrightnessRange_orig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
namespace games::otoca {
|
namespace games::otoca {
|
||||||
|
|
||||||
|
extern bool BYPASS_CAMERA;
|
||||||
|
|
||||||
class OtocaGame : public games::Game {
|
class OtocaGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
OtocaGame();
|
OtocaGame();
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// QueryDisplayConfig
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
#include "pc.h"
|
#include "pc.h"
|
||||||
|
|
||||||
#include <format>
|
#include <format>
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ namespace games::qma {
|
|||||||
// create quiz directory
|
// create quiz directory
|
||||||
static bool dirs_created = false;
|
static bool dirs_created = false;
|
||||||
if (!dirs_created && _wcsnicmp(lpFileName, L"E:\\LMA", 6) == 0) {
|
if (!dirs_created && _wcsnicmp(lpFileName, L"E:\\LMA", 6) == 0) {
|
||||||
|
dirs_created = true;
|
||||||
fileutils::dir_create_recursive("E:\\LMA\\quiz11");
|
fileutils::dir_create_recursive("E:\\LMA\\quiz11");
|
||||||
fileutils::dir_create_recursive("E:\\LMA\\quiz13");
|
fileutils::dir_create_recursive("E:\\LMA\\quiz13");
|
||||||
fileutils::dir_create_recursive("E:\\LMA\\quiz15");
|
fileutils::dir_create_recursive("E:\\LMA\\quiz15");
|
||||||
dirs_created = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return result
|
// return result
|
||||||
|
|||||||
@@ -65,76 +65,78 @@ std::vector<Light> &games::sdvx::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Sound Voltex");
|
lights = GameAPI::Lights::getLights("Sound Voltex");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"BT-A",
|
{
|
||||||
"BT-B",
|
{"Common", "BT-A"},
|
||||||
"BT-C",
|
{"Common", "BT-B"},
|
||||||
"BT-D",
|
{"Common", "BT-C"},
|
||||||
"FX-L",
|
{"Common", "BT-D"},
|
||||||
"FX-R",
|
{"Common", "FX-L"},
|
||||||
"Start",
|
{"Common", "FX-R"},
|
||||||
"Wing Left Up R",
|
{"Common", "Start"},
|
||||||
"Wing Left Up G",
|
{"Nemsys", "Wing Left Up R"},
|
||||||
"Wing Left Up B",
|
{"Nemsys", "Wing Left Up G"},
|
||||||
"Wing Right Up R",
|
{"Nemsys", "Wing Left Up B"},
|
||||||
"Wing Right Up G",
|
{"Nemsys", "Wing Right Up R"},
|
||||||
"Wing Right Up B",
|
{"Nemsys", "Wing Right Up G"},
|
||||||
"Wing Left Low R",
|
{"Nemsys", "Wing Right Up B"},
|
||||||
"Wing Left Low G",
|
{"Nemsys", "Wing Left Low R"},
|
||||||
"Wing Left Low B",
|
{"Nemsys", "Wing Left Low G"},
|
||||||
"Wing Right Low R",
|
{"Nemsys", "Wing Left Low B"},
|
||||||
"Wing Right Low G",
|
{"Nemsys", "Wing Right Low R"},
|
||||||
"Wing Right Low B",
|
{"Nemsys", "Wing Right Low G"},
|
||||||
"Woofer R",
|
{"Nemsys", "Wing Right Low B"},
|
||||||
"Woofer G",
|
{"Nemsys", "Woofer R"},
|
||||||
"Woofer B",
|
{"Nemsys", "Woofer G"},
|
||||||
"Controller R",
|
{"Nemsys", "Woofer B"},
|
||||||
"Controller G",
|
{"Nemsys", "Controller R"},
|
||||||
"Controller B",
|
{"Nemsys", "Controller G"},
|
||||||
"Generator R",
|
{"Nemsys", "Controller B"},
|
||||||
"Generator G",
|
{"Nemsys", "Generator R"},
|
||||||
"Generator B",
|
{"Nemsys", "Generator G"},
|
||||||
"Pop",
|
{"Nemsys", "Generator B"},
|
||||||
"Title Left",
|
{"Nemsys", "Pop"},
|
||||||
"Title Right",
|
{"Nemsys", "Title Left"},
|
||||||
"Volume Sound",
|
{"Nemsys", "Title Right"},
|
||||||
"Volume Headphone",
|
{"System", "Volume Sound"},
|
||||||
"Volume External",
|
{"System", "Volume Headphone"},
|
||||||
"Volume Woofer",
|
{"System", "Volume External"},
|
||||||
"IC Card Reader R",
|
{"System", "Volume Woofer"},
|
||||||
"IC Card Reader G",
|
{"Valkyrie", "IC Card Reader R"},
|
||||||
"IC Card Reader B",
|
{"Valkyrie", "IC Card Reader G"},
|
||||||
"Title Avg R",
|
{"Valkyrie", "IC Card Reader B"},
|
||||||
"Title Avg G",
|
{"Valkyrie", "Title Avg R"},
|
||||||
"Title Avg B",
|
{"Valkyrie", "Title Avg G"},
|
||||||
"Upper Left Speaker Avg R",
|
{"Valkyrie", "Title Avg B"},
|
||||||
"Upper Left Speaker Avg G",
|
{"Valkyrie", "Upper Left Speaker Avg R"},
|
||||||
"Upper Left Speaker Avg B",
|
{"Valkyrie", "Upper Left Speaker Avg G"},
|
||||||
"Upper Right Speaker Avg R",
|
{"Valkyrie", "Upper Left Speaker Avg B"},
|
||||||
"Upper Right Speaker Avg G",
|
{"Valkyrie", "Upper Right Speaker Avg R"},
|
||||||
"Upper Right Speaker Avg B",
|
{"Valkyrie", "Upper Right Speaker Avg G"},
|
||||||
"Left Wing Avg R",
|
{"Valkyrie", "Upper Right Speaker Avg B"},
|
||||||
"Left Wing Avg G",
|
{"Valkyrie", "Left Wing Avg R"},
|
||||||
"Left Wing Avg B",
|
{"Valkyrie", "Left Wing Avg G"},
|
||||||
"Right Wing Avg R",
|
{"Valkyrie", "Left Wing Avg B"},
|
||||||
"Right Wing Avg G",
|
{"Valkyrie", "Right Wing Avg R"},
|
||||||
"Right Wing Avg B",
|
{"Valkyrie", "Right Wing Avg G"},
|
||||||
"Lower Left Speaker Avg R",
|
{"Valkyrie", "Right Wing Avg B"},
|
||||||
"Lower Left Speaker Avg G",
|
{"Valkyrie", "Lower Left Speaker Avg R"},
|
||||||
"Lower Left Speaker Avg B",
|
{"Valkyrie", "Lower Left Speaker Avg G"},
|
||||||
"Lower Right Speaker Avg R",
|
{"Valkyrie", "Lower Left Speaker Avg B"},
|
||||||
"Lower Right Speaker Avg G",
|
{"Valkyrie", "Lower Right Speaker Avg R"},
|
||||||
"Lower Right Speaker Avg B",
|
{"Valkyrie", "Lower Right Speaker Avg G"},
|
||||||
"Control Panel Avg R",
|
{"Valkyrie", "Lower Right Speaker Avg B"},
|
||||||
"Control Panel Avg G",
|
{"Valkyrie", "Control Panel Avg R"},
|
||||||
"Control Panel Avg B",
|
{"Valkyrie", "Control Panel Avg G"},
|
||||||
"Woofer Avg R",
|
{"Valkyrie", "Control Panel Avg B"},
|
||||||
"Woofer Avg G",
|
{"Valkyrie", "Woofer Avg R"},
|
||||||
"Woofer Avg B",
|
{"Valkyrie", "Woofer Avg G"},
|
||||||
"V Unit Avg R",
|
{"Valkyrie", "Woofer Avg B"},
|
||||||
"V Unit Avg G",
|
{"Valkyrie", "V Unit Avg R"},
|
||||||
"V Unit Avg B"
|
{"Valkyrie", "V Unit Avg G"},
|
||||||
|
{"Valkyrie", "V Unit Avg B"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,9 @@
|
|||||||
#include "util/socd_cleaner.h"
|
#include "util/socd_cleaner.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "misc/wintouchemu.h"
|
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "misc/nativetouchhook.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
@@ -233,6 +234,27 @@ namespace games::sdvx {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool logged_missing_layer_error = false;
|
||||||
|
// this is raw SHIFT-JIS text for
|
||||||
|
// W:BM2D: CreateLayer() 指定したレイヤーは存在しません
|
||||||
|
if (!logged_missing_layer_error &&
|
||||||
|
data.find(
|
||||||
|
"\x57\x3A\x42\x4D\x32\x44\x3A\x20\x43\x72\x65\x61\x74\x65\x4C\x61\x79\x65\x72\x28\x29\x20"
|
||||||
|
"\x8E\x77\x92\xE8\x82\xB5\x82\xBD\x83\x8C\x83\x43\x83\x84\x81\x5B\x82\xCD\x91\xB6\x8D\xDD"
|
||||||
|
"\x82\xB5\x82\xDC\x82\xB9\x82\xF1") != std::string::npos) {
|
||||||
|
|
||||||
|
logged_missing_layer_error = true;
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"sdvx game engine detected missing or corrupt file(s); raw SHIFT-JIS error was:",
|
||||||
|
std::string(" ") + data,
|
||||||
|
" this will almost certainly cause your game to crash",
|
||||||
|
" this is often caused by mismatched game DLLs or missing resources from game updates",
|
||||||
|
" double check the integrity of your game data and try again"
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,9 +438,11 @@ namespace games::sdvx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_valkyrie_model()) {
|
if (is_valkyrie_model()) {
|
||||||
|
if (NATIVETOUCH) {
|
||||||
|
nativetouchhook::hook(avs::game::DLL_INSTANCE);
|
||||||
|
} else if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||||
// hook touch window
|
// hook touch window
|
||||||
// in windowed mode, game can accept mouse input on the second screen
|
// in windowed mode, game can accept mouse input on the second screen
|
||||||
if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
|
||||||
wintouchemu::FORCE = true;
|
wintouchemu::FORCE = true;
|
||||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
wintouchemu::hook_title_ends(
|
wintouchemu::hook_title_ends(
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ namespace games::shared {
|
|||||||
image_data[index + 2] = tmp;
|
image_data[index + 2] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avs::game::is_model({"KLP", "KFC"})) {
|
if (avs::game::is_model({"KLP", "KFC", "NCG"})) {
|
||||||
// rotate image clockwise
|
// rotate image clockwise
|
||||||
log_misc("printer", "rotate clockwise...");
|
log_misc("printer", "rotate clockwise...");
|
||||||
auto rotated = new uint8_t[image_width * image_height * 3];
|
auto rotated = new uint8_t[image_width * image_height * 3];
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ namespace hooks::audio {
|
|||||||
bool USE_DUMMY = false;
|
bool USE_DUMMY = false;
|
||||||
WAVEFORMATEXTENSIBLE FORMAT {};
|
WAVEFORMATEXTENSIBLE FORMAT {};
|
||||||
std::optional<Backend> BACKEND = std::nullopt;
|
std::optional<Backend> BACKEND = std::nullopt;
|
||||||
size_t ASIO_DRIVER_ID = 0;
|
std::optional<size_t> ASIO_DRIVER_ID = std::nullopt;
|
||||||
|
std::string ASIO_DRIVER_NAME = "";
|
||||||
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
||||||
|
|
||||||
// private globals
|
// private globals
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmreg.h>
|
#include <mmreg.h>
|
||||||
@@ -20,7 +21,8 @@ namespace hooks::audio {
|
|||||||
extern bool USE_DUMMY;
|
extern bool USE_DUMMY;
|
||||||
extern WAVEFORMATEXTENSIBLE FORMAT;
|
extern WAVEFORMATEXTENSIBLE FORMAT;
|
||||||
extern std::optional<Backend> BACKEND;
|
extern std::optional<Backend> BACKEND;
|
||||||
extern size_t ASIO_DRIVER_ID;
|
extern std::optional<size_t> ASIO_DRIVER_ID;
|
||||||
|
extern std::string ASIO_DRIVER_NAME;
|
||||||
extern bool ASIO_FORCE_UNLOAD_ON_STOP;
|
extern bool ASIO_FORCE_UNLOAD_ON_STOP;
|
||||||
extern bool LOW_LATENCY_SHARED_WASAPI;
|
extern bool LOW_LATENCY_SHARED_WASAPI;
|
||||||
|
|
||||||
|
|||||||
@@ -218,13 +218,40 @@ void AsioBackend::set_thread_state(AsioThreadState state) {
|
|||||||
bool AsioBackend::load_driver() {
|
bool AsioBackend::load_driver() {
|
||||||
AsioDriverList asio_driver_list;
|
AsioDriverList asio_driver_list;
|
||||||
|
|
||||||
for (const auto &driver : asio_driver_list.driver_list) {
|
size_t driver_id = 0;
|
||||||
log_info("audio::asio", "Driver {}", driver.id);
|
std::string driver_name = "";
|
||||||
log_info("audio::asio", "... Name : {}", driver.name);
|
if (hooks::audio::ASIO_DRIVER_ID.has_value()) {
|
||||||
log_info("audio::asio", "... Path : {}", driver.dll_path);
|
driver_id = hooks::audio::ASIO_DRIVER_ID.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto driver_id = hooks::audio::ASIO_DRIVER_ID;
|
for (const auto &driver : asio_driver_list.driver_list) {
|
||||||
|
log_info("audio::asio", " Driver ID : {}", driver.id);
|
||||||
|
log_info("audio::asio", " ... Name : {}", driver.name);
|
||||||
|
log_info("audio::asio", " ... Path : {}", driver.dll_path);
|
||||||
|
|
||||||
|
if (!hooks::audio::ASIO_DRIVER_NAME.empty()) {
|
||||||
|
// match on driver name
|
||||||
|
if (driver.name == hooks::audio::ASIO_DRIVER_NAME) {
|
||||||
|
driver_id = driver.id;
|
||||||
|
driver_name = driver.name;
|
||||||
|
}
|
||||||
|
} else if (driver.id == driver_id) {
|
||||||
|
// match on driver index (0 for default or user-specified)
|
||||||
|
driver_name = driver.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver_name.empty()) {
|
||||||
|
if (!hooks::audio::ASIO_DRIVER_NAME.empty()) {
|
||||||
|
log_warning(
|
||||||
|
"audio::asio",
|
||||||
|
"tried to look for ASIO driver but failed: {}", hooks::audio::ASIO_DRIVER_NAME);
|
||||||
|
}
|
||||||
|
log_fatal(
|
||||||
|
"audio::asio",
|
||||||
|
"could not find ASIO driver to use for conversion, fix your audio options and try again");
|
||||||
|
}
|
||||||
|
log_info("audio::asio", "will attempt to use the following ASIO driver: ID = {}, Name = {}", driver_id, driver_name);
|
||||||
|
|
||||||
IAsio *driver = nullptr;
|
IAsio *driver = nullptr;
|
||||||
auto ret = asio_driver_list.open_driver(driver_id, reinterpret_cast<void **>(&driver));
|
auto ret = asio_driver_list.open_driver(driver_id, reinterpret_cast<void **>(&driver));
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ static bool is_dx9_on_12_enabled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
|
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
|
||||||
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, MAXINT)) {
|
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, INT_MAX)) {
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
"dx9on12 was force enabled by user (-dx9on12)",
|
"dx9on12 was force enabled by user (-dx9on12)",
|
||||||
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
|
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
|
||||||
@@ -552,18 +552,6 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
|||||||
modified = true;
|
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);
|
CHECK_RESULT(ret);
|
||||||
@@ -945,7 +933,8 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
|
|
||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
auto *params = &pPresentationParameters[i];
|
auto *params = &pPresentationParameters[i];
|
||||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
if (!GRAPHICS_WINDOWED){
|
||||||
|
if (i == 0) {
|
||||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||||
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||||
@@ -966,6 +955,16 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
params->BackBufferHeight, params->BackBufferWidth);
|
params->BackBufferHeight, params->BackBufferWidth);
|
||||||
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
||||||
}
|
}
|
||||||
|
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"use custom sub resolution {}x{} => {}x{}",
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("graphics::d3d9",
|
log_info("graphics::d3d9",
|
||||||
@@ -992,13 +991,18 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
for (size_t i = 0; i < num_adapters; i++) {
|
for (size_t i = 0; i < num_adapters; i++) {
|
||||||
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||||
|
|
||||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
if (i == 0) {
|
||||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("graphics::d3d9",
|
log_info("graphics::d3d9",
|
||||||
|
|||||||
@@ -593,7 +593,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::UpdateTexture(
|
|||||||
WRAP_DEBUG;
|
WRAP_DEBUG;
|
||||||
|
|
||||||
if (CUSTOM_RESET) {
|
if (CUSTOM_RESET) {
|
||||||
#ifdef __GNUC__
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
|
||||||
// Do a pointer compare rather than `QueryInterface` because it is a lot cheaper than incrementing
|
// Do a pointer compare rather than `QueryInterface` because it is a lot cheaper than incrementing
|
||||||
// the reference count
|
// the reference count
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ bool GRAPHICS_CAPTURE_CURSOR = false;
|
|||||||
bool GRAPHICS_LOG_HRESULT = false;
|
bool GRAPHICS_LOG_HRESULT = false;
|
||||||
bool GRAPHICS_SDVX_FORCE_720 = false;
|
bool GRAPHICS_SDVX_FORCE_720 = false;
|
||||||
bool GRAPHICS_SHOW_CURSOR = false;
|
bool GRAPHICS_SHOW_CURSOR = false;
|
||||||
graphics_orientation GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_NORMAL;
|
|
||||||
bool GRAPHICS_WINDOWED = false;
|
bool GRAPHICS_WINDOWED = false;
|
||||||
std::vector<HWND> GRAPHICS_WINDOWS;
|
std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
UINT GRAPHICS_FORCE_REFRESH = 0;
|
UINT GRAPHICS_FORCE_REFRESH = 0;
|
||||||
@@ -71,6 +70,7 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
|
|||||||
bool SUBSCREEN_FORCE_REDRAW = false;
|
bool SUBSCREEN_FORCE_REDRAW = false;
|
||||||
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
||||||
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||||
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
||||||
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
||||||
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
||||||
@@ -300,30 +300,6 @@ static BOOL WINAPI ClipCursor_hook(const RECT *lpRect) {
|
|||||||
return ClipCursor_orig(lpRect);
|
return ClipCursor_orig(lpRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fix_up_rb_auto_rotate_dimensions(DWORD &w, DWORD &h) {
|
|
||||||
// reflec beat
|
|
||||||
// for whatever reason, rb creates the first window it weird dimensions (774x1389 for reflesia, for example)
|
|
||||||
// so doing ChangeDisplaySettings as-is ends up with DISP_CHANGE_BADMODE
|
|
||||||
// while a second window (with title D3DProxyWindow) does get created with correct dimensions, it's
|
|
||||||
// too late to rotate the window then; as a workaround force the display change now with patched up dimensions
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
if (w == 774) {
|
|
||||||
// LBR, MBR reflesia
|
|
||||||
w = 768;
|
|
||||||
h = 1360;
|
|
||||||
changed = true;
|
|
||||||
} else if (w == 1086) {
|
|
||||||
// MBR (before reflesia)
|
|
||||||
w = 1080;
|
|
||||||
h = 1920;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
log_misc("graphics", "fix auto-rotate dimensions for reflec beat: w={}, h={}", w, h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName,
|
static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName,
|
||||||
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
|
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
|
||||||
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
|
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
|
||||||
@@ -345,62 +321,6 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
fmt::ptr(hInstance),
|
fmt::ptr(hInstance),
|
||||||
fmt::ptr(lpParam));
|
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
|
// gfdm
|
||||||
if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
||||||
// set window name
|
// 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_width = capture.width;
|
||||||
auto capture_height = capture.height;
|
auto capture_height = capture.height;
|
||||||
auto capture_timestamp = capture.timestamp;
|
auto capture_timestamp = capture.timestamp;
|
||||||
|
capture.data = nullptr;
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// validate data
|
// validate data
|
||||||
@@ -1147,3 +1068,259 @@ std::string graphics_screenshot_genpath() {
|
|||||||
id++;
|
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_CW = 0,
|
||||||
ORIENTATION_CCW = 1,
|
ORIENTATION_CCW = 1,
|
||||||
ORIENTATION_NORMAL = 2,
|
ORIENTATION_NORMAL = 2,
|
||||||
|
ORIENTATION_FLIPPED = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
enum graphics_dx9on12_state {
|
enum graphics_dx9on12_state {
|
||||||
@@ -29,7 +30,6 @@ extern bool GRAPHICS_LOG_HRESULT;
|
|||||||
extern bool GRAPHICS_SDVX_FORCE_720;
|
extern bool GRAPHICS_SDVX_FORCE_720;
|
||||||
extern bool GRAPHICS_SHOW_CURSOR;
|
extern bool GRAPHICS_SHOW_CURSOR;
|
||||||
extern bool GRAPHICS_WINDOWED;
|
extern bool GRAPHICS_WINDOWED;
|
||||||
extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
|
||||||
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
||||||
extern UINT GRAPHICS_FORCE_REFRESH;
|
extern UINT GRAPHICS_FORCE_REFRESH;
|
||||||
extern std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
extern std::optional<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_FORCE_SINGLE_ADAPTER;
|
||||||
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
|
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
|
||||||
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||||
|
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||||
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
||||||
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
||||||
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||||
@@ -106,3 +107,6 @@ bool graphics_window_decoration_change_crashes_game();
|
|||||||
bool graphics_window_resize_breaks_game();
|
bool graphics_window_resize_breaks_game();
|
||||||
bool graphics_window_move_and_resize_breaks_game();
|
bool graphics_window_move_and_resize_breaks_game();
|
||||||
void graphics_load_windowed_subscreen_parameters();
|
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_x = result.first;
|
||||||
cfg::SCREENRESIZE->window_offset_y = result.second;
|
cfg::SCREENRESIZE->window_offset_y = result.second;
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -windowpos");
|
log_fatal("graphics-windowed", "failed to parse -windowpos");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
|||||||
GRAPHICS_WSUB_WIDTH = result.first;
|
GRAPHICS_WSUB_WIDTH = result.first;
|
||||||
GRAPHICS_WSUB_HEIGHT = result.second;
|
GRAPHICS_WSUB_HEIGHT = result.second;
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
log_fatal("graphics-windowed", "failed to parse -iidxwsubsize / -sdvxwsubsize");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
|||||||
GRAPHICS_WSUB_X = result.first;
|
GRAPHICS_WSUB_X = result.first;
|
||||||
GRAPHICS_WSUB_Y = result.second;
|
GRAPHICS_WSUB_Y = result.second;
|
||||||
} else {
|
} else {
|
||||||
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
log_fatal("graphics-windowed", "failed to parse -iidxwsubpos / -sdvxwsubpos");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// GetLocaleInfoEx
|
||||||
|
#define _WIN32_WINNT 0x0600
|
||||||
|
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
|
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// DisplayConfigSetDeviceInfo
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
#include "winuser.h"
|
#include "winuser.h"
|
||||||
|
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
|
|||||||
@@ -351,9 +351,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::DisplayAdapter].is_active() &&
|
if (options[launcher::Options::DX9DisplayAdapter].is_active() &&
|
||||||
options[launcher::Options::DisplayAdapter].value_uint32() != D3DADAPTER_DEFAULT) {
|
options[launcher::Options::DX9DisplayAdapter].value_uint32() != D3DADAPTER_DEFAULT) {
|
||||||
D3D9_ADAPTER = options[launcher::Options::DisplayAdapter].value_uint32();
|
D3D9_ADAPTER = options[launcher::Options::DX9DisplayAdapter].value_uint32();
|
||||||
|
|
||||||
// when we fix up adapter numbers, we only fix the first adapter, and not any subsequent
|
// 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
|
// 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()) {
|
if (options[launcher::Options::AllowEA3Verbose].value_bool()) {
|
||||||
avs::ea3::EA3_DEBUG_VERBOSE = true;
|
avs::ea3::EA3_DEBUG_VERBOSE = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<graphics_orientation> monitor_orientation;
|
||||||
if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
|
if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
|
||||||
GRAPHICS_ADJUST_ORIENTATION =
|
monitor_orientation =
|
||||||
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
|
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
|
||||||
} else if (options[launcher::Options::AdjustOrientation].value_bool()) {
|
} 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()) {
|
if (options[launcher::Options::spice2x_NoD3D9DeviceHook].value_bool()) {
|
||||||
D3D9_DEVICE_HOOK_DISABLE = true;
|
D3D9_DEVICE_HOOK_DISABLE = true;
|
||||||
// touch emulation gets disabled, might as well turn these on
|
// 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;
|
nvenc_hook::FORCE_DISABLE = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (options[launcher::Options::OtocaCamHook].value_bool()) {
|
||||||
|
games::otoca::BYPASS_CAMERA = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
|
if (options[launcher::Options::LoadJubeatModule].value_bool()) {
|
||||||
attach_jb = true;
|
attach_jb = true;
|
||||||
}
|
}
|
||||||
@@ -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
|
// do not explicitly set GRAPHICS_9_ON_12_STATE to default here; must respect
|
||||||
// legacy Graphics9On12 option from above if set
|
// 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;
|
rawinput::NOLEGACY = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::RichPresence].value_bool()) {
|
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()) {
|
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
|
||||||
hooks::audio::VOLUME_HOOK_ENABLED = false;
|
hooks::audio::VOLUME_HOOK_ENABLED = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::AudioBackend].is_active()) {
|
if (options[launcher::Options::AudioBackend].is_active()) {
|
||||||
auto &name = options[launcher::Options::AudioBackend].value_text();
|
auto &name = options[launcher::Options::AudioBackend].value_text();
|
||||||
auto backend = hooks::audio::name_to_backend(name.c_str());
|
auto backend = hooks::audio::name_to_backend(name.c_str());
|
||||||
if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) {
|
if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_fatal("launcher", "invalid audio backend: {}", name);
|
log_fatal("launcher", "invalid audio backend: {}", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks::audio::BACKEND = backend;
|
hooks::audio::BACKEND = backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::AsioDriverId].is_active()) {
|
if (options[launcher::Options::AsioDriverId].is_active()) {
|
||||||
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32();
|
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::AsioDriverName].is_active()) {
|
||||||
|
hooks::audio::BACKEND = hooks::audio::name_to_backend("asio");
|
||||||
|
hooks::audio::ASIO_DRIVER_NAME = options[launcher::Options::AsioDriverName].value_text();
|
||||||
|
// this new option overrides the old one
|
||||||
|
hooks::audio::ASIO_DRIVER_ID.reset();
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::AudioDummy].value_bool()) {
|
if (options[launcher::Options::AudioDummy].value_bool()) {
|
||||||
hooks::audio::USE_DUMMY = true;
|
hooks::audio::USE_DUMMY = true;
|
||||||
}
|
}
|
||||||
@@ -1083,8 +1098,8 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
std::pair<uint32_t, uint32_t> result;
|
std::pair<uint32_t, uint32_t> result;
|
||||||
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
||||||
GRAPHICS_WINDOW_SIZE = result;
|
GRAPHICS_WINDOW_SIZE = result;
|
||||||
} else {
|
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_warning("launcher", "failed to parse -windowsize");
|
log_fatal("launcher", "failed to parse -windowsize");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
|
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
|
||||||
@@ -1401,8 +1416,17 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
std::pair<uint32_t, uint32_t> result;
|
std::pair<uint32_t, uint32_t> result;
|
||||||
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
||||||
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
||||||
} else {
|
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_warning("launcher", "failed to parse -forceres");
|
log_fatal("launcher", "failed to parse -forceres");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options[launcher::Options::FullscreenSubResolution].is_active()) {
|
||||||
|
std::pair<uint32_t, uint32_t> result;
|
||||||
|
if (parse_width_height(options[launcher::Options::FullscreenSubResolution].value_text(), result)) {
|
||||||
|
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB = result;
|
||||||
|
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
log_fatal("launcher", "failed to parse -forceressub");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1486,6 +1510,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
// early hooks
|
// early hooks
|
||||||
for (auto &hook : early_hooks) {
|
for (auto &hook : early_hooks) {
|
||||||
log_info("launcher", "loading early hook DLL {}", hook);
|
log_info("launcher", "loading early hook DLL {}", hook);
|
||||||
|
libutils::print_dll_info(hook);
|
||||||
HMODULE module;
|
HMODULE module;
|
||||||
if (!(module = libutils::try_library(hook))) {
|
if (!(module = libutils::try_library(hook))) {
|
||||||
log_warning("launcher", "failed to load early hook {}: {}", hook, get_last_error_string());
|
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();
|
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
|
// initialize raw input
|
||||||
RI_MGR = std::make_unique<rawinput::RawInputManager>();
|
RI_MGR = std::make_unique<rawinput::RawInputManager>();
|
||||||
for (const auto &device : sextet_devices) {
|
for (const auto &device : sextet_devices) {
|
||||||
@@ -2286,6 +2317,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
// load hooks
|
// load hooks
|
||||||
for (auto &hook : game_hooks) {
|
for (auto &hook : game_hooks) {
|
||||||
log_info("launcher", "loading hook DLL {}", hook);
|
log_info("launcher", "loading hook DLL {}", hook);
|
||||||
|
libutils::print_dll_info(hook);
|
||||||
HMODULE module;
|
HMODULE module;
|
||||||
if (!(module = libutils::try_library(hook))) {
|
if (!(module = libutils::try_library(hook))) {
|
||||||
log_warning("launcher", "failed to load hook {}: {}", hook, get_last_error_string());
|
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()) {
|
if (button->isNaive()) {
|
||||||
log_misc(
|
log_misc(
|
||||||
"rawinput", " [{}] dev=Naive, vkey={}",
|
"rawinput", " [{}] dev=Naive, vkey={} ({})",
|
||||||
button->getName(),
|
button->getName(),
|
||||||
button->getVKey()
|
button->getVKey(),
|
||||||
|
button->getVKeyString()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log_misc(
|
log_misc(
|
||||||
@@ -2561,19 +2594,28 @@ void dump_button_bindings(std::vector<Button> *buttons) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto& alt : button->getAlternatives()) {
|
for (auto& alt : button->getAlternatives()) {
|
||||||
if (alt.getVKey() == INVALID_VKEY) {
|
if (!alt.isValid()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (alt.isNaive()) {
|
||||||
|
log_misc(
|
||||||
|
"rawinput", " [{}] (alt) dev=Naive, vkey={} ({})",
|
||||||
|
alt.getName(),
|
||||||
|
alt.getVKey(),
|
||||||
|
alt.getVKeyString()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
log_misc(
|
log_misc(
|
||||||
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
|
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
|
||||||
button->getName(),
|
alt.getName(),
|
||||||
alt.isNaive() ? "Naive" : alt.getDeviceIdentifier(),
|
alt.getDeviceIdentifier(),
|
||||||
alt.getVKey(),
|
alt.getVKey(),
|
||||||
static_cast<uint32_t>(alt.getAnalogType())
|
static_cast<uint32_t>(alt.getAnalogType())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dump_analog_bindings() {
|
void dump_analog_bindings() {
|
||||||
auto analogs = games::get_analogs(eamuse_get_game());
|
auto analogs = games::get_analogs(eamuse_get_game());
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
|
|||||||
"Game Options",
|
"Game Options",
|
||||||
"Common",
|
"Common",
|
||||||
"Network",
|
"Network",
|
||||||
|
"Monitor",
|
||||||
"Graphics (Common)",
|
"Graphics (Common)",
|
||||||
"Graphics (Full Screen)",
|
"Graphics (Full Screen)",
|
||||||
"Graphics (Windowed)",
|
"Graphics (Windowed)",
|
||||||
@@ -71,6 +72,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.setting_name = "*.dll",
|
.setting_name = "*.dll",
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
// intentionally not setting a file picker here to discourage people setting this without a good reason
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Open Configurator",
|
.title = "Open Configurator",
|
||||||
@@ -121,6 +123,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.setting_name = "E004010000000000",
|
.setting_name = "E004010000000000",
|
||||||
.category = "Network",
|
.category = "Network",
|
||||||
.sensitive = true,
|
.sensitive = true,
|
||||||
|
.picker = OptionPickerType::EACard,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Player 2 Card",
|
.title = "Player 2 Card",
|
||||||
@@ -130,6 +133,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.setting_name = "E004010000000000",
|
.setting_name = "E004010000000000",
|
||||||
.category = "Network",
|
.category = "Network",
|
||||||
.sensitive = true,
|
.sensitive = true,
|
||||||
|
.picker = OptionPickerType::EACard,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Player1PinMacro
|
// Player1PinMacro
|
||||||
@@ -203,9 +207,24 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Common",
|
.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",
|
.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"
|
"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. "
|
"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.",
|
"Disable Full Screen Optimizations for best results.",
|
||||||
@@ -213,21 +232,19 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Graphics (Full Screen)",
|
.category = "Graphics (Full Screen)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Only Use One Monitor",
|
.title = "Only Use Main Monitor For Full Screen",
|
||||||
.name = "graphics-force-single-adapter",
|
.name = "graphics-force-single-adapter",
|
||||||
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
|
.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!",
|
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Graphics (Full Screen)",
|
.category = "Monitor",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Force Monitor Refresh Rate",
|
.title = "Monitor Refresh Rate",
|
||||||
.name = "graphics-force-refresh",
|
.name = "graphics-force-refresh",
|
||||||
.desc =
|
.desc = "Change the refresh rate for the primary monitor before launching the game. It will be restored on exit.",
|
||||||
"Attempt to change the refresh rate for the primary monitor before the game boots; "
|
|
||||||
"works in both full screen and windowed modes, but known to fail for some games.",
|
|
||||||
.type = OptionType::Integer,
|
.type = OptionType::Integer,
|
||||||
.category = "Graphics (Common)",
|
.category = "Monitor",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// FullscreenResolution
|
// FullscreenResolution
|
||||||
@@ -256,9 +273,21 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.hidden = true,
|
.hidden = true,
|
||||||
.category = "Graphics (Full Screen)"
|
.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
|
// FullscreenSubRefreshRate
|
||||||
.title = "Force Submonitor Refresh Rate (EXPERIMENTAL)",
|
.title = "Force FS Subscreen Refresh Rate (EXPERIMENTAL)",
|
||||||
.name = "graphics-force-refresh-sub",
|
.name = "graphics-force-refresh-sub",
|
||||||
.desc =
|
.desc =
|
||||||
"Override fullscreen refresh rate requested by the game for second monitor, "
|
"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,
|
.type = OptionType::Text,
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
|
.picker = OptionPickerType::AsioDriver,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "IIDX BIO2 Firmware Update",
|
.title = "IIDX BIO2 Firmware Update",
|
||||||
@@ -766,6 +796,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
|
.picker = OptionPickerType::DirectoryPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "SDVX Printer Output Clear",
|
.title = "SDVX Printer Output Clear",
|
||||||
@@ -814,7 +845,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "SDVX Native Touch Handling",
|
.title = "SDVX FS Subscreen Native Touch Handling",
|
||||||
.name = "sdvxnativetouch",
|
.name = "sdvxnativetouch",
|
||||||
.desc = "Disables touch hooks and lets the game access a touch screen directly. "
|
.desc = "Disables touch hooks and lets the game access a touch screen directly. "
|
||||||
"Requires a touch screen to be connected as a secondary monitor. "
|
"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",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.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
|
// spice2x_SDVXDigitalKnobSensitivity
|
||||||
.title = "SDVX Digital Knob Sensitivity",
|
.title = "SDVX Digital Knob Sensitivity",
|
||||||
@@ -860,11 +903,12 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.display_name = "sdvxasio",
|
.display_name = "sdvxasio",
|
||||||
.aliases= "sdvxasio",
|
.aliases= "sdvxasio",
|
||||||
.desc = "ASIO driver name to use, replacing XONAR SOUND CARD(64). "
|
.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!",
|
"SDVX is EXTREMELY picky about ASIO devices it can support!",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
|
.picker = OptionPickerType::AsioDriver,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// spice2x_SDVXSubPos
|
// spice2x_SDVXSubPos
|
||||||
@@ -884,18 +928,6 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{"bottomright", "for landscape"},
|
{"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",
|
.title = "Force Load DDR Module",
|
||||||
.name = "ddr",
|
.name = "ddr",
|
||||||
@@ -1272,6 +1304,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Sets a custom path to the modules folder.",
|
.desc = "Sets a custom path to the modules folder.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
// intentionally not setting a folder picker here to discourage people setting this without a good reason
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Screenshot Folder Override",
|
.title = "Screenshot Folder Override",
|
||||||
@@ -1279,6 +1312,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Sets a custom path to the screenshots folder.",
|
.desc = "Sets a custom path to the screenshots folder.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::DirectoryPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Configuration Path Override",
|
.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.",
|
"If left empty, %appdata%\\spice2x\\spicetools_screen_resize.json will be used.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::FilePath,
|
||||||
|
.file_extension = "JSON",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// PatchManagerConfigPath
|
// PatchManagerConfigPath
|
||||||
@@ -1307,6 +1343,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
"If left empty, %appdata%\\spice2x\\spicetools_patch_manager.json will be used.",
|
"If left empty, %appdata%\\spice2x\\spicetools_patch_manager.json will be used.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::FilePath,
|
||||||
|
.file_extension = "JSON",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Intel SDE",
|
.title = "Intel SDE",
|
||||||
@@ -1314,6 +1352,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Path to Intel SDE kit path for automatic attaching.",
|
.desc = "Path to Intel SDE kit path for automatic attaching.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Development",
|
.category = "Development",
|
||||||
|
.picker = OptionPickerType::DirectoryPath
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "ea3-config.xml Override",
|
.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.",
|
.desc = "Sets a custom path to ea3-config.xml.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::FilePath,
|
||||||
|
.file_extension = "XML",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "app-config.xml Override",
|
.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.",
|
.desc = "Sets a custom path to app-config.xml.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::FilePath,
|
||||||
|
.file_extension = "XML",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "avs-config.xml Override",
|
.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.",
|
.desc = "Sets a custom path to avs-config.xml.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::FilePath,
|
||||||
|
.file_extension = "XML",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "bootstrap.xml Override",
|
.title = "bootstrap.xml Override",
|
||||||
@@ -1342,6 +1387,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Sets a custom path to bootstrap.xml.",
|
.desc = "Sets a custom path to bootstrap.xml.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::FilePath,
|
||||||
|
.file_extension = "XML",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "log.txt Override",
|
.title = "log.txt Override",
|
||||||
@@ -1349,6 +1396,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Sets a custom path to log.txt.",
|
.desc = "Sets a custom path to log.txt.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.category = "Path Overrides",
|
.category = "Path Overrides",
|
||||||
|
.picker = OptionPickerType::FilePath,
|
||||||
|
.file_extension = "TXT",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "API TCP Port",
|
.title = "API TCP Port",
|
||||||
@@ -1497,9 +1546,9 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Touch Parameters",
|
.category = "Touch Parameters",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Invert Raw Input Touch",
|
.title = "Invert Touch",
|
||||||
.name = "touchinvert",
|
.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.",
|
"and not Windows Touch API.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Touch Parameters",
|
.category = "Touch Parameters",
|
||||||
@@ -1658,6 +1707,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
"Must provide a hexadecimal mask (e.g., 0x1ff00).",
|
"Must provide a hexadecimal mask (e.g., 0x1ff00).",
|
||||||
.type = OptionType::Hex,
|
.type = OptionType::Hex,
|
||||||
.category = "Performance",
|
.category = "Performance",
|
||||||
|
.picker = OptionPickerType::CpuAffinity,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// spice2x_ProcessorEfficiencyClass
|
// spice2x_ProcessorEfficiencyClass
|
||||||
@@ -1730,11 +1780,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Audio (Hacks)",
|
.category = "Audio (Hacks)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Spice Audio Hook Backend",
|
// AudioBackend
|
||||||
|
.title = "Spice Audio Hook Backend (DEPRECATED - use -asioconvert instead)",
|
||||||
.name = "audiobackend",
|
.name = "audiobackend",
|
||||||
.desc = "Selects the audio backend to use when spice audio hook is enabled, overriding exclusive WASAPI. "
|
.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.",
|
"Does nothing for games that do not output to exclusive WASAPI.",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
|
.hidden = true,
|
||||||
.category = "Audio",
|
.category = "Audio",
|
||||||
.elements = {
|
.elements = {
|
||||||
{"asio", "ASIO"},
|
{"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",
|
.name = "asiodriverid",
|
||||||
.desc = "Selects the ASIO driver id to use when Spice Audio Backend is set to ASIO.",
|
.desc = "Selects the ASIO driver id to use when Spice Audio Backend is set to ASIO.",
|
||||||
.type = OptionType::Integer,
|
.type = OptionType::Integer,
|
||||||
|
.hidden = true,
|
||||||
.category = "Audio",
|
.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",
|
.title = "WASAPI Dummy Context",
|
||||||
.name = "audiodummy",
|
.name = "audiodummy",
|
||||||
@@ -1795,17 +1861,20 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// spice2x_AutoOrientation
|
// spice2x_AutoOrientation
|
||||||
.title = "Auto-rotate Display",
|
.title = "Rotate Monitor",
|
||||||
.name = "sp2x-autoorientation",
|
.name = "sp2x-autoorientation",
|
||||||
.display_name = "autoorientation",
|
.display_name = "autoorientation",
|
||||||
.aliases= "autoorientation",
|
.aliases= "autoorientation",
|
||||||
.desc = "Automatically adjust the orientation of your display when launched. "
|
.desc = "Change the orientation of the primary display before launching the game. It will be restored on exit",
|
||||||
"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.",
|
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
.category = "Graphics (Common)",
|
.category = "Monitor",
|
||||||
// match graphics_orientation
|
// match graphics_orientation enum
|
||||||
.elements = {{"0", "90 (CW)"}, {"1", "270 (CCW)"}},
|
.elements = {
|
||||||
|
{"0", "Portrait"},
|
||||||
|
{"1", "Portrait, Flipped"},
|
||||||
|
{"2", "Landscape"},
|
||||||
|
{"3", "Landscape, Flipped"}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "AVS Log Level",
|
.title = "AVS Log Level",
|
||||||
@@ -2251,7 +2320,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// spice2x_IIDXNativeTouch
|
// spice2x_IIDXNativeTouch
|
||||||
.title = "IIDX Native Touch Handling",
|
.title = "IIDX TDJ Subscreen Native Touch Handling",
|
||||||
.name = "sp2x-iidxnativetouch",
|
.name = "sp2x-iidxnativetouch",
|
||||||
.display_name = "iidxnativetouch",
|
.display_name = "iidxnativetouch",
|
||||||
.aliases= "iidxnativetouch",
|
.aliases= "iidxnativetouch",
|
||||||
@@ -2580,6 +2649,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
|
.picker = OptionPickerType::DirectoryPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "LovePlus Printer Output Clear",
|
.title = "LovePlus Printer Output Clear",
|
||||||
@@ -2625,6 +2695,17 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Development",
|
.category = "Development",
|
||||||
.disabled = true,
|
.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,
|
ExecuteScript,
|
||||||
CaptureCursor,
|
CaptureCursor,
|
||||||
ShowCursor,
|
ShowCursor,
|
||||||
DisplayAdapter,
|
PrimaryMonitor,
|
||||||
|
DX9DisplayAdapter,
|
||||||
GraphicsForceSingleAdapter,
|
GraphicsForceSingleAdapter,
|
||||||
GraphicsForceRefresh,
|
GraphicsForceRefresh,
|
||||||
FullscreenResolution,
|
FullscreenResolution,
|
||||||
FullscreenOrientationFlip,
|
FullscreenOrientationFlip,
|
||||||
|
FullscreenSubResolution,
|
||||||
FullscreenSubRefreshRate,
|
FullscreenSubRefreshRate,
|
||||||
Graphics9On12,
|
Graphics9On12,
|
||||||
spice2x_Dx9On12,
|
spice2x_Dx9On12,
|
||||||
@@ -85,11 +87,11 @@ namespace launcher {
|
|||||||
SDVXPrinterJPGQuality,
|
SDVXPrinterJPGQuality,
|
||||||
SDVXDisableCameras,
|
SDVXDisableCameras,
|
||||||
SDVXNativeTouch,
|
SDVXNativeTouch,
|
||||||
|
spice2x_SDVXSubRedraw,
|
||||||
spice2x_SDVXDigitalKnobSensitivity,
|
spice2x_SDVXDigitalKnobSensitivity,
|
||||||
SDVXDigitalKnobSocd,
|
SDVXDigitalKnobSocd,
|
||||||
spice2x_SDVXAsioDriver,
|
spice2x_SDVXAsioDriver,
|
||||||
spice2x_SDVXSubPos,
|
spice2x_SDVXSubPos,
|
||||||
spice2x_SDVXSubRedraw,
|
|
||||||
LoadDDRModule,
|
LoadDDRModule,
|
||||||
DDR43Mode,
|
DDR43Mode,
|
||||||
DDRSkipCodecRegisteration,
|
DDRSkipCodecRegisteration,
|
||||||
@@ -186,6 +188,7 @@ namespace launcher {
|
|||||||
spice2x_DisableVolumeHook,
|
spice2x_DisableVolumeHook,
|
||||||
AudioBackend,
|
AudioBackend,
|
||||||
AsioDriverId,
|
AsioDriverId,
|
||||||
|
AsioDriverName,
|
||||||
AudioDummy,
|
AudioDummy,
|
||||||
DelayBy5Seconds,
|
DelayBy5Seconds,
|
||||||
spice2x_DelayByNSeconds,
|
spice2x_DelayByNSeconds,
|
||||||
@@ -269,6 +272,7 @@ namespace launcher {
|
|||||||
LovePlusPrinterOutputFormat,
|
LovePlusPrinterOutputFormat,
|
||||||
LovePlusPrinterJPGQuality,
|
LovePlusPrinterJPGQuality,
|
||||||
OptionConflictResolution,
|
OptionConflictResolution,
|
||||||
|
OtocaCamHook,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class OptionsCategory {
|
enum class OptionsCategory {
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
// mingw otherwise doesn't load touch stuff
|
// mingw otherwise doesn't load touch stuff
|
||||||
#define _WIN32_WINNT 0x0601
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
#include "wintouchemu.h"
|
#include "wintouchemu.h"
|
||||||
|
#include "rawinput/touch.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
|
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -63,6 +66,11 @@ namespace nativetouchhook {
|
|||||||
point->cyContact = 0;
|
point->cyContact = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void flip_touch_points(PTOUCHINPUT point) {
|
||||||
|
point->x = rawinput::touch::DISPLAY_SIZE_X * 100 - point->x;
|
||||||
|
point->y = rawinput::touch::DISPLAY_SIZE_Y * 100 - point->y;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL WINAPI GetTouchInputInfoHook(
|
static BOOL WINAPI GetTouchInputInfoHook(
|
||||||
HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||||
|
|
||||||
@@ -72,11 +80,34 @@ namespace nativetouchhook {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool flip_values = false;
|
||||||
|
if (avs::game::is_model("KFC") && rawinput::touch::DISPLAY_INITIALIZED) {
|
||||||
|
log_debug(
|
||||||
|
"touch::native", "DISPLAY_ORIENTATION = {}, DISPLAY_SIZE_X = {}, DISPLAY_SIZE_Y = {}",
|
||||||
|
rawinput::touch::DISPLAY_ORIENTATION,
|
||||||
|
rawinput::touch::DISPLAY_SIZE_X,
|
||||||
|
rawinput::touch::DISPLAY_SIZE_Y);
|
||||||
|
if (rawinput::touch::DISPLAY_ORIENTATION == DMDO_270) {
|
||||||
|
flip_values = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawinput::touch::INVERTED) {
|
||||||
|
flip_values = !flip_values;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < cInputs; i++) {
|
for (size_t i = 0; i < cInputs; i++) {
|
||||||
PTOUCHINPUT point = &pInputs[i];
|
PTOUCHINPUT point = &pInputs[i];
|
||||||
|
|
||||||
|
if (avs::game::is_model("LDJ")) {
|
||||||
strip_contact_size(point);
|
strip_contact_size(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flip_values) {
|
||||||
|
flip_touch_points(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "rawinput/touch.h"
|
||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
|
||||||
@@ -126,6 +127,11 @@ namespace wintouchemu {
|
|||||||
WINDOW_TITLE_END = window_title_end;
|
WINDOW_TITLE_END = window_title_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void flip_touch_points(PTOUCHINPUT point) {
|
||||||
|
point->x = rawinput::touch::DISPLAY_SIZE_X * 100 - point->x;
|
||||||
|
point->y = rawinput::touch::DISPLAY_SIZE_Y * 100 - point->y;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL WINAPI GetTouchInputInfoHook(HANDLE hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
static BOOL WINAPI GetTouchInputInfoHook(HANDLE hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||||
|
|
||||||
// check if original should be called
|
// check if original should be called
|
||||||
@@ -220,6 +226,12 @@ namespace wintouchemu {
|
|||||||
touch_input->cxContact = 0;
|
touch_input->cxContact = 0;
|
||||||
touch_input->cyContact = 0;
|
touch_input->cyContact = 0;
|
||||||
|
|
||||||
|
if (avs::game::is_model("KFC") &&
|
||||||
|
rawinput::touch::DISPLAY_INITIALIZED &&
|
||||||
|
rawinput::touch::DISPLAY_ORIENTATION == DMDO_270) {
|
||||||
|
flip_touch_points(touch_input);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (USE_MOUSE && !mouse_used) {
|
} else if (USE_MOUSE && !mouse_used) {
|
||||||
|
|
||||||
// disable further mouse inputs this call
|
// disable further mouse inputs this call
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ namespace ImGui {
|
|||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::HelpTooltip(tooltip.c_str());
|
ImGui::HelpTooltip(tooltip.c_str());
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
@@ -159,10 +158,22 @@ namespace ImGui {
|
|||||||
bool clicked = ImGui::Button("\u00D7"); // multiplication sign (×)
|
bool clicked = ImGui::Button("\u00D7"); // multiplication sign (×)
|
||||||
ImGui::PopStyleColor(4);
|
ImGui::PopStyleColor(4);
|
||||||
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::HelpTooltip(tooltip.c_str());
|
ImGui::HelpTooltip(tooltip.c_str());
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
return clicked;
|
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);
|
void TextTruncated(const std::string& p_text, float p_truncated_width);
|
||||||
bool DeleteButton(const std::string& tooltip);
|
bool DeleteButton(const std::string& tooltip);
|
||||||
bool ClearButton(const std::string& tooltip);
|
bool ClearButton(const std::string& tooltip);
|
||||||
|
void InvisibleTableRowSelectable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,19 @@
|
|||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
|
|
||||||
|
#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) || \
|
#if !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) || \
|
||||||
!defined(IMGUI_DISABLE_DEFAULT_ALLOCATORS) || \
|
!defined(IMGUI_DISABLE_DEFAULT_ALLOCATORS) || \
|
||||||
!defined(IMGUI_USE_BGRA_PACKED_COLOR) || \
|
!defined(IMGUI_USE_BGRA_PACKED_COLOR) || \
|
||||||
@@ -29,6 +39,97 @@ static HWND g_hWnd = nullptr;
|
|||||||
static INT64 g_Time = 0;
|
static INT64 g_Time = 0;
|
||||||
static INT64 g_TicksPerSecond = 0;
|
static INT64 g_TicksPerSecond = 0;
|
||||||
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||||
|
static double g_LastMouseMovement = 0.f;
|
||||||
|
static bool g_MouseCursorAutoHide = false;
|
||||||
|
|
||||||
|
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) {
|
bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||||
log_misc("imgui_impl_spice", "init");
|
log_misc("imgui_impl_spice", "init");
|
||||||
@@ -56,33 +157,15 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
|
|||||||
io.BackendPlatformName = "imgui_impl_spice";
|
io.BackendPlatformName = "imgui_impl_spice";
|
||||||
io.ConfigErrorRecoveryEnableTooltip = true;
|
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
|
// get display size
|
||||||
ImGui_ImplSpice_UpdateDisplaySize();
|
ImGui_ImplSpice_UpdateDisplaySize();
|
||||||
|
|
||||||
|
// only enable mouse cursor auto hiding if we want ImGui to draw the cursor
|
||||||
|
// (if OS is drawing it, we don't auto-hide)
|
||||||
|
if (io.MouseDrawCursor && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
g_MouseCursorAutoHide = true;
|
||||||
|
}
|
||||||
|
|
||||||
// return success
|
// return success
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -239,7 +322,7 @@ static void ImGui_ImplSpice_UpdateMousePos() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// delay press
|
// 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) {
|
if (last_touch_id == ~0u) {
|
||||||
last_touch_id = tp.id;
|
last_touch_id = tp.id;
|
||||||
}
|
}
|
||||||
@@ -266,38 +349,26 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
g_Time = current_time;
|
g_Time = current_time;
|
||||||
|
|
||||||
// remember old state
|
// remember old state
|
||||||
BYTE KeysDownOld[sizeof(io.KeysDown)];
|
std::array<BYTE, VKEY_MAX> KeysDownOld;
|
||||||
for (size_t i = 0; i < sizeof(io.KeysDown); i++) {
|
for (size_t i = 0; i < VKEY_MAX; i++) {
|
||||||
KeysDownOld[i] = io.KeysDown[i] ? ~0 : 0;
|
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
|
// reset keys state
|
||||||
io.MouseWheel = 0;
|
g_MouseDown.fill(false);
|
||||||
io.KeyCtrl = false;
|
g_KeysDown.fill(false);
|
||||||
io.KeyShift = false;
|
|
||||||
io.KeyAlt = false;
|
|
||||||
io.KeySuper = false;
|
|
||||||
memset(io.KeysDown, false, sizeof(io.KeysDown));
|
|
||||||
memset(io.MouseDown, false, sizeof(io.MouseDown));
|
|
||||||
|
|
||||||
// early quit if window not in focus
|
// early quit if window not in focus
|
||||||
if (!superexit::has_focus()) {
|
if (!superexit::has_focus() || rawinput::OS_WINDOW_ACTIVE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// apply windows mouse buttons
|
||||||
io.MouseDown[0] |= (get_async_primary_mouse()) != 0;
|
g_MouseDown[ImGuiMouseButton_Left] |= (get_async_primary_mouse()) != 0;
|
||||||
io.MouseDown[1] |= (get_async_secondary_mouse()) != 0;
|
g_MouseDown[ImGuiMouseButton_Right] |= (get_async_secondary_mouse()) != 0;
|
||||||
io.MouseDown[2] |= (GetAsyncKeyState(VK_MBUTTON)) != 0;
|
g_MouseDown[ImGuiMouseButton_Middle] |= (GetAsyncKeyState(VK_MBUTTON)) != 0;
|
||||||
|
|
||||||
// read new keys state
|
// read new keys state
|
||||||
static long mouse_wheel_last = 0;
|
static long mouse_wheel_last = 0;
|
||||||
@@ -312,21 +383,21 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
// mouse button triggers
|
// mouse button triggers
|
||||||
if (GetSystemMetrics(SM_SWAPBUTTON)) {
|
if (GetSystemMetrics(SM_SWAPBUTTON)) {
|
||||||
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
|
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
|
||||||
io.MouseDown[0] = true;
|
g_MouseDown[ImGuiMouseButton_Left] = true;
|
||||||
}
|
}
|
||||||
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
|
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
|
||||||
io.MouseDown[1] = true;
|
g_MouseDown[ImGuiMouseButton_Right] = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
|
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
|
||||||
io.MouseDown[0] = true;
|
g_MouseDown[ImGuiMouseButton_Left] = true;
|
||||||
}
|
}
|
||||||
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
|
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
|
||||||
io.MouseDown[1] = true;
|
g_MouseDown[ImGuiMouseButton_Right] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mouse->key_states[rawinput::MOUSEBTN_MIDDLE]) {
|
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
|
// final mouse wheel value should be all devices combined
|
||||||
@@ -337,33 +408,11 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
case rawinput::KEYBOARD: {
|
case rawinput::KEYBOARD: {
|
||||||
|
|
||||||
// iterate all virtual key codes
|
// 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)
|
// get state (combined from all pages)
|
||||||
auto &key_states = device.keyboardInfo->key_states;
|
auto &key_states = device.keyboardInfo->key_states;
|
||||||
bool state = false;
|
|
||||||
for (size_t page_index = 0; page_index < 1024; page_index += 256) {
|
for (size_t page_index = 0; page_index < 1024; page_index += 256) {
|
||||||
state |= key_states[page_index + vKey];
|
g_KeysDown[vKey] |= 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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
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
|
// set mouse wheel
|
||||||
auto mouse_diff = mouse_wheel - mouse_wheel_last;
|
auto wheel_diff = mouse_wheel - mouse_wheel_last;
|
||||||
mouse_wheel_last = mouse_wheel;
|
mouse_wheel_last = mouse_wheel;
|
||||||
io.MouseWheel = mouse_diff;
|
io.AddMouseWheelEvent(0, wheel_diff);
|
||||||
|
|
||||||
// update OS mouse position
|
// update OS mouse position
|
||||||
|
const auto old_mouse_pos = io.MousePos;
|
||||||
ImGui_ImplSpice_UpdateMousePos();
|
ImGui_ImplSpice_UpdateMousePos();
|
||||||
|
const auto new_mouse_pos = io.MousePos;
|
||||||
|
|
||||||
|
// 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 (cfg::CONFIGURATOR_STANDALONE) {
|
||||||
// if cursor is inside the client area, always set the OS cursor to what ImGui wants
|
// if cursor is inside the client area, always set the OS cursor to what ImGui wants
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ void paint_draw_cmd(
|
|||||||
const SwOptions& options,
|
const SwOptions& options,
|
||||||
Stats* stats)
|
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;
|
const auto offset = pcmd.IdxOffset;
|
||||||
|
|
||||||
// ImGui uses the first pixel for "white".
|
// ImGui uses the first pixel for "white".
|
||||||
@@ -678,7 +678,7 @@ void bind_imgui_painting()
|
|||||||
int font_width, font_height;
|
int font_width, font_height;
|
||||||
io.Fonts->GetTexDataAsAlpha8(&tex_data, &font_width, &font_height);
|
io.Fonts->GetTexDataAsAlpha8(&tex_data, &font_width, &font_height);
|
||||||
const auto texture = new Texture{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?
|
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()
|
void unbind_imgui_painting()
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
delete reinterpret_cast<Texture*>(io.Fonts->TexID);
|
delete reinterpret_cast<Texture*>(io.Fonts->TexID.GetTexID());
|
||||||
io.Fonts = nullptr;
|
io.Fonts = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,74 +185,76 @@ void overlay::SpiceOverlay::init() {
|
|||||||
if (UI_SCALE_PERCENT.has_value()) {
|
if (UI_SCALE_PERCENT.has_value()) {
|
||||||
const auto scale = static_cast<float>(UI_SCALE_PERCENT.value()) / 100.f;
|
const auto scale = static_cast<float>(UI_SCALE_PERCENT.value()) / 100.f;
|
||||||
ImGui::GetStyle().ScaleAllSizes(scale);
|
ImGui::GetStyle().ScaleAllSizes(scale);
|
||||||
ImGui::GetIO().FontGlobalScale = scale;
|
ImGui::GetStyle().FontScaleMain = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
// red theme based on:
|
// based on CrimsonVesuvius theme from
|
||||||
// https://github.com/ocornut/imgui/issues/707#issuecomment-760220280
|
// https://github.com/ocornut/imgui/issues/707#issuecomment-4107169777
|
||||||
// r, g, b, a
|
|
||||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||||
|
|
||||||
// note: also see bg_alpha value in each overlay window
|
// Text
|
||||||
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 1.0f);
|
colors[ImGuiCol_Text] = ImVec4(1.00f, 0.90f, 0.90f, 1.00f); // Slight pinkish tint to off-white
|
||||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 1.0f);
|
colors[ImGuiCol_TextDisabled] = ImVec4(0.35f, 0.26f, 0.26f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
|
// 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);
|
||||||
|
|
||||||
|
// 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_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_FrameBg] = ImVec4(0.18f, 0.18f, 0.18f, 0.54f);
|
// Frames
|
||||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.18f, 0.18f, 0.18f, 0.80f);
|
colors[ImGuiCol_FrameBg] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
||||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 0.80f);
|
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_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
|
// Title Bars
|
||||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
colors[ImGuiCol_TitleBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
||||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.25f, 0.10f, 0.10f, 1.00f);
|
||||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
|
||||||
colors[ImGuiCol_CheckMark] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
|
||||||
colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
|
||||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
|
||||||
|
|
||||||
colors[ImGuiCol_Button] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
// Menus
|
||||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.17f, 0.00f, 1.00f);
|
colors[ImGuiCol_MenuBarBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
||||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.17f, 0.00f, 0.90f);
|
|
||||||
|
|
||||||
colors[ImGuiCol_Header] = ImVec4(0.33f, 0.35f, 0.36f, 0.53f);
|
// Scrollbars
|
||||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.67f);
|
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.47f, 0.47f, 0.47f, 0.67f);
|
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.25f, 0.12f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.35f, 0.15f, 0.15f, 1.00f);
|
||||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.45f, 0.20f, 0.20f, 1.00f);
|
||||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
|
||||||
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.85f);
|
|
||||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
|
|
||||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
|
|
||||||
|
|
||||||
colors[ImGuiCol_Tab] = colors[ImGuiCol_Button];
|
// Interactables (The High-Intensity Red)
|
||||||
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_ButtonHovered];
|
colors[ImGuiCol_CheckMark] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f); // Sharp Red
|
||||||
colors[ImGuiCol_TabActive] = colors[ImGuiCol_ButtonActive];
|
colors[ImGuiCol_SliderGrab] = ImVec4(0.60f, 0.12f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_TabUnfocused] = colors[ImGuiCol_Tab] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f);
|
||||||
colors[ImGuiCol_TabUnfocusedActive] = colors[ImGuiCol_TabActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
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);
|
||||||
|
|
||||||
colors[ImGuiCol_DockingPreview] = ImVec4(0.47f, 0.47f, 0.47f, 0.47f);
|
// Tabs
|
||||||
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
colors[ImGuiCol_Tab] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
colors[ImGuiCol_TabHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
||||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
colors[ImGuiCol_TabActive] = ImVec4(0.35f, 0.12f, 0.12f, 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);
|
// Misc
|
||||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
|
colors[ImGuiCol_PlotLines] = ImVec4(0.85f, 0.20f, 0.20f, 1.00f);
|
||||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.85f, 0.15f, 0.15f, 0.35f);
|
||||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
|
colors[ImGuiCol_NavHighlight] = ImVec4(0.85f, 0.15f, 0.15f, 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);
|
// spice custom
|
||||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.025f);
|
||||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.22f, 0.22f, 1.00f);
|
||||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.42f, 0.22f, 0.22f, 1.00f);
|
||||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
colors[ImGuiCol_SeparatorActive] = ImVec4(0.52f, 0.22f, 0.22f, 1.00f);
|
||||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
|
||||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
#ifdef IMGUI_HAS_DOCK
|
||||||
|
colors[ImGuiCol_DockingPreview] = ImVec4(0.85f, 0.15f, 0.15f, 0.40f);
|
||||||
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.08f, 0.07f, 0.07f, 1.00f);
|
||||||
|
#endif
|
||||||
|
|
||||||
// configure IO
|
// configure IO
|
||||||
auto &io = ImGui::GetIO();
|
auto &io = ImGui::GetIO();
|
||||||
@@ -281,7 +283,7 @@ void overlay::SpiceOverlay::init() {
|
|||||||
io.FontAllowUserScaling = true;
|
io.FontAllowUserScaling = true;
|
||||||
|
|
||||||
// add default font
|
// add default font
|
||||||
io.Fonts->AddFontDefault();
|
io.Fonts->AddFontDefaultBitmap();
|
||||||
|
|
||||||
// add fallback fonts for missing glyph ranges
|
// add fallback fonts for missing glyph ranges
|
||||||
ImFontConfig config {};
|
ImFontConfig config {};
|
||||||
|
|||||||
+2687
-114
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "cfg/game.h"
|
#include "cfg/game.h"
|
||||||
@@ -7,6 +8,7 @@
|
|||||||
#include "rawinput/device.h"
|
#include "rawinput/device.h"
|
||||||
#include "external/imgui/imgui_filebrowser.h"
|
#include "external/imgui/imgui_filebrowser.h"
|
||||||
#include "patch_manager.h"
|
#include "patch_manager.h"
|
||||||
|
#include "controller_presets.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
@@ -22,9 +24,18 @@ namespace overlay::windows {
|
|||||||
CONFIG_TAB_OPTIONS,
|
CONFIG_TAB_OPTIONS,
|
||||||
CONFIG_TAB_ADVANCED,
|
CONFIG_TAB_ADVANCED,
|
||||||
CONFIG_TAB_DEV,
|
CONFIG_TAB_DEV,
|
||||||
|
CONFIG_TAB_PRESETS,
|
||||||
CONFIG_TAB_SEARCH,
|
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 {
|
class Config : public Window {
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -58,13 +69,60 @@ namespace overlay::windows {
|
|||||||
int lights_devices_selected = -1;
|
int lights_devices_selected = -1;
|
||||||
int lights_devices_control_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] {};
|
int keypads_selected[2] {};
|
||||||
char keypads_card_path[2][1024] {};
|
char keypads_card_path[2][1024] {};
|
||||||
std::thread *keypads_card_select = nullptr;
|
std::thread *keypads_card_select = nullptr;
|
||||||
bool keypads_card_select_done = false;
|
bool keypads_card_select_done = false;
|
||||||
ImGui::FileBrowser keypads_card_select_browser[2];
|
ImGui::FileBrowser keypads_card_select_browser[2];
|
||||||
char keypads_card_number[2][18] {};
|
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
|
// patches tab
|
||||||
std::unique_ptr<PatchManager> patch_manager;
|
std::unique_ptr<PatchManager> patch_manager;
|
||||||
@@ -76,6 +134,12 @@ namespace overlay::windows {
|
|||||||
std::string search_filter = "";
|
std::string search_filter = "";
|
||||||
std::string search_filter_in_lower_case = "";
|
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_buttons(const std::string &name, std::vector<Button> *buttons, int min = 0, int max = -1);
|
||||||
void build_button(
|
void build_button(
|
||||||
const std::string &name,
|
const std::string &name,
|
||||||
@@ -93,17 +157,38 @@ namespace overlay::windows {
|
|||||||
Button *button,
|
Button *button,
|
||||||
const float button_velocity,
|
const float button_velocity,
|
||||||
const int alt_index);
|
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 build_analogs(const std::string &name, std::vector<Analog> *analogs);
|
||||||
void edit_analog_popup(Analog &analog);
|
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_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 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 clear_light(Light *light, const int alt_index);
|
||||||
void edit_light_popup(Light &primary_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();
|
void build_cards();
|
||||||
|
std::string build_option_value_picker_title(const OptionDefinition& option);
|
||||||
|
void build_option_value_picker(Option& option);
|
||||||
void build_options(
|
void build_options(
|
||||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||||
void build_about();
|
void build_about();
|
||||||
@@ -115,12 +200,13 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
void set_alternating_row_colors(const int row_index);
|
void set_alternating_row_colors(const int row_index);
|
||||||
|
|
||||||
|
bool validate_ea_card(char card_number[16]);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Config(SpiceOverlay *overlay);
|
Config(SpiceOverlay *overlay);
|
||||||
~Config() override;
|
~Config() override;
|
||||||
|
|
||||||
void read_card(int player = -1);
|
void read_card(int player = -1);
|
||||||
void write_card(int player);
|
|
||||||
void build_content() override;
|
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::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
||||||
|
void GenericSubScreen::check_for_errors() {}
|
||||||
|
|
||||||
void GenericSubScreen::build_content() {
|
void GenericSubScreen::build_content() {
|
||||||
this->flags |= ImGuiWindowFlags_NoBackground;
|
this->flags |= ImGuiWindowFlags_NoBackground;
|
||||||
|
|
||||||
|
check_for_errors();
|
||||||
if (this->disabled_message.has_value()) {
|
if (this->disabled_message.has_value()) {
|
||||||
this->draws_window = true;
|
this->draws_window = true;
|
||||||
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out);
|
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_top_left;
|
||||||
ImVec2 overlay_content_size;
|
ImVec2 overlay_content_size;
|
||||||
std::optional<std::string> disabled_message = std::nullopt;
|
std::optional<std::string> disabled_message = std::nullopt;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
static const size_t TICKER_SIZE = 9;
|
static const size_t TICKER_SIZE = 9;
|
||||||
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
|
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 ImVec4 RED(1.f, 0.f, 0.f, 1.f);
|
||||||
static const float PADDING_Y = 8.f;
|
static const float PADDING_Y = 8.f;
|
||||||
static const float PADDING_X = 4.f;
|
static const float PADDING_X = 4.f;
|
||||||
@@ -91,6 +92,23 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IIDXSegmentDisplay::build_content() {
|
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];
|
char input_ticker[TICKER_SIZE];
|
||||||
|
|
||||||
// get ticker content from game
|
// get ticker content from game
|
||||||
|
|||||||
@@ -12,11 +12,9 @@ namespace overlay::windows {
|
|||||||
this->title = "IIDX TDJ Subscreen";
|
this->title = "IIDX TDJ Subscreen";
|
||||||
|
|
||||||
if (GRAPHICS_IIDX_WSUB) {
|
if (GRAPHICS_IIDX_WSUB) {
|
||||||
this->disabled_message =
|
this->disabled_message = "Close this overlay and use the second window. (try ALT+TAB)";
|
||||||
"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.";
|
|
||||||
} else if (games::iidx::IIDX_TDJ_MONITOR_WARNING) {
|
} 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;
|
float size = 0.5f;
|
||||||
@@ -44,6 +42,18 @@ namespace overlay::windows {
|
|||||||
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2));
|
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) {
|
void IIDXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||||
if (!this->get_active()) {
|
if (!this->get_active()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
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 "overlay/window.h"
|
||||||
#include "cfg/button.h"
|
#include "cfg/button.h"
|
||||||
|
#include "cfg/light.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
|||||||
@@ -40,13 +40,17 @@ namespace overlay::windows {
|
|||||||
ImGui::Dummy(ImVec2(12, 0));
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
ImGui::PushID("P1");
|
||||||
this->draw_buttons(0);
|
this->draw_buttons(0);
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Dummy(ImVec2(12, 0));
|
ImGui::Dummy(ImVec2(12, 0));
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
ImGui::PushID("P2");
|
||||||
this->draw_buttons(1);
|
this->draw_buttons(1);
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DDRIOPanel::draw_buttons(const int p) {
|
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"
|
"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, "
|
"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"
|
"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"
|
"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();
|
const auto gle = GetLastError();
|
||||||
log_warning("patchmanager", "WinHttpSendRequest failed: {}", gle);
|
log_warning("patchmanager", "WinHttpSendRequest failed: {}", gle);
|
||||||
url_fetch_errors += fmt::format("WinHttpSendRequest failed: {}\n", gle);
|
url_fetch_errors += fmt::format("WinHttpSendRequest failed: {}\n", gle);
|
||||||
if (gle == 12175) {
|
if (gle == ERROR_WINHTTP_SECURE_FAILURE) {
|
||||||
url_fetch_errors += "\nThis is ERROR_WINHTTP_SECURE_FAILURE - most likely TLS 1.1 / TLS 1.2 error on old OS versions.\n\n";
|
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";
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -235,9 +243,9 @@ namespace overlay::windows {
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
static_cast<PLDR_DLL_NOTIFICATION_FUNCTION>([]
|
static_cast<PLDR_DLL_NOTIFICATION_FUNCTION>([]
|
||||||
#else
|
#else
|
||||||
[] CALLBACK
|
[]
|
||||||
#endif
|
#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) {
|
if (reason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
||||||
auto const dll = strtolower(std::filesystem::path({
|
auto const dll = strtolower(std::filesystem::path({
|
||||||
data->Loaded.FullDllName->Buffer,
|
data->Loaded.FullDllName->Buffer,
|
||||||
@@ -459,7 +467,7 @@ namespace overlay::windows {
|
|||||||
&patch_url,
|
&patch_url,
|
||||||
ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_AutoSelectAll);
|
ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_AutoSelectAll);
|
||||||
|
|
||||||
if (ImGui::Button("Paste")) {
|
if (ImGui::Button("Paste from clipboard")) {
|
||||||
auto clipboard_url = clipboard::paste_text();
|
auto clipboard_url = clipboard::paste_text();
|
||||||
if (!clipboard_url.empty()) {
|
if (!clipboard_url.empty()) {
|
||||||
strreplace(clipboard_url, "\r\n", "");
|
strreplace(clipboard_url, "\r\n", "");
|
||||||
@@ -534,7 +542,9 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
this->config_save();
|
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::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -834,6 +844,10 @@ namespace overlay::windows {
|
|||||||
ImGui::TextUnformatted(patch_checked ? "ON" : "off");
|
ImGui::TextUnformatted(patch_checked ? "ON" : "off");
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// row hover detection (invisible selectable that spans entire row)
|
||||||
|
ImGui::InvisibleTableRowSelectable();
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace overlay::windows {
|
|||||||
~ScreenResize() override;
|
~ScreenResize() override;
|
||||||
|
|
||||||
void build_content() override;
|
void build_content() override;
|
||||||
void update();
|
void update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t toggle_screen_resize = ~0u;
|
size_t toggle_screen_resize = ~0u;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace overlay::windows {
|
|||||||
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
|
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
|
||||||
} else {
|
} 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 <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
@@ -120,9 +121,15 @@ namespace rawinput {
|
|||||||
std::vector<std::string> value_caps_names;
|
std::vector<std::string> value_caps_names;
|
||||||
std::vector<HIDP_VALUE_CAPS> value_output_caps_list;
|
std::vector<HIDP_VALUE_CAPS> value_output_caps_list;
|
||||||
std::vector<std::string> value_output_caps_names;
|
std::vector<std::string> value_output_caps_names;
|
||||||
|
|
||||||
std::vector<std::vector<bool>> button_states;
|
std::vector<std::vector<bool>> button_states;
|
||||||
std::vector<std::vector<double>> button_up, button_down;
|
std::vector<std::vector<double>> button_up, button_down;
|
||||||
std::vector<std::vector<bool>> button_output_states;
|
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<float> value_states;
|
||||||
std::vector<LONG> value_states_raw;
|
std::vector<LONG> value_states_raw;
|
||||||
std::vector<float> value_output_states;
|
std::vector<float> value_output_states;
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ namespace rawinput {
|
|||||||
uint32_t MIDI_NOTE_SUSTAIN = 20;
|
uint32_t MIDI_NOTE_SUSTAIN = 20;
|
||||||
|
|
||||||
static MidiNoteAlgorithm MIDI_NOTE_ALGORITHM = MidiNoteAlgorithm::V2;
|
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() {
|
rawinput::MidiNoteAlgorithm rawinput::get_midi_algorithm() {
|
||||||
@@ -172,7 +176,7 @@ void rawinput::RawInputManager::input_hwnd_destroy() {
|
|||||||
|
|
||||||
void rawinput::RawInputManager::devices_reload() {
|
void rawinput::RawInputManager::devices_reload() {
|
||||||
this->devices_destruct();
|
this->devices_destruct();
|
||||||
log_info("rawinput", "reloading devices");
|
log_info("rawinput", "reloading devices...");
|
||||||
|
|
||||||
// scan for devices
|
// scan for devices
|
||||||
this->devices_scan_rawinput();
|
this->devices_scan_rawinput();
|
||||||
@@ -195,6 +199,7 @@ void rawinput::RawInputManager::devices_reload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rawinput::RawInputManager::devices_scan_rawinput(const std::string &device_name) {
|
void rawinput::RawInputManager::devices_scan_rawinput(const std::string &device_name) {
|
||||||
|
log_misc("rawinput", "scan rawinput devices...");
|
||||||
|
|
||||||
// get number of devices
|
// get number of devices
|
||||||
UINT device_no = 0;
|
UINT device_no = 0;
|
||||||
@@ -232,6 +237,8 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_misc("rawinput", "found rawinput device: {}", device_name);
|
||||||
|
|
||||||
// extract information out of name
|
// extract information out of name
|
||||||
auto device_info = rawinput::RawInputManager::get_device_info(device_name);
|
auto device_info = rawinput::RawInputManager::get_device_info(device_name);
|
||||||
auto device_description = rawinput::RawInputManager::rawinput_get_device_description(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::string> button_caps_names;
|
||||||
std::vector<std::vector<bool>> button_states;
|
std::vector<std::vector<bool>> button_states;
|
||||||
std::vector<std::vector<double>> button_up, button_down;
|
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++) {
|
for (int button_cap_num = 0; button_cap_num < button_cap_length; button_cap_num++) {
|
||||||
auto &button_caps = button_cap_data[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_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_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_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
|
// names
|
||||||
for (USAGE usg = button_caps.Range.UsageMin; usg <= button_caps.Range.UsageMax; usg++) {
|
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_up = std::move(button_up);
|
||||||
new_device.hidInfo->button_down = std::move(button_down);
|
new_device.hidInfo->button_down = std::move(button_down);
|
||||||
new_device.hidInfo->button_output_states = std::move(button_output_states);
|
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 = std::move(value_states);
|
||||||
new_device.hidInfo->value_states_raw = std::move(value_states_raw);
|
new_device.hidInfo->value_states_raw = std::move(value_states_raw);
|
||||||
new_device.hidInfo->value_output_states = std::move(value_output_states);
|
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() {
|
void rawinput::RawInputManager::devices_scan_midi() {
|
||||||
|
log_misc("rawinput", "scan MIDI devices...");
|
||||||
|
|
||||||
// add midi devices
|
// add midi devices
|
||||||
auto midi_device_count = midiInGetNumDevs();
|
auto midi_device_count = midiInGetNumDevs();
|
||||||
@@ -814,6 +825,9 @@ void rawinput::RawInputManager::devices_scan_midi() {
|
|||||||
continue;
|
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
|
// open device
|
||||||
HMIDIIN midi_device_handle;
|
HMIDIIN midi_device_handle;
|
||||||
if (midiInOpen(&midi_device_handle,
|
if (midiInOpen(&midi_device_handle,
|
||||||
@@ -913,6 +927,7 @@ void rawinput::RawInputManager::devices_scan_midi() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rawinput::RawInputManager::devices_scan_piuio() {
|
void rawinput::RawInputManager::devices_scan_piuio() {
|
||||||
|
log_misc("rawinput", "scan PIUIO devices...");
|
||||||
|
|
||||||
// add device to vector first so pointer is valid
|
// add device to vector first so pointer is valid
|
||||||
auto *new_piuio_device = new Device();
|
auto *new_piuio_device = new Device();
|
||||||
@@ -943,6 +958,8 @@ void rawinput::RawInputManager::devices_scan_piuio() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rawinput::RawInputManager::devices_scan_smxstage() {
|
void rawinput::RawInputManager::devices_scan_smxstage() {
|
||||||
|
log_misc("rawinput", "scan SMX Stage devices...");
|
||||||
|
|
||||||
auto *new_smxstage_device = new Device();
|
auto *new_smxstage_device = new Device();
|
||||||
new_smxstage_device->type = SMX_STAGE;
|
new_smxstage_device->type = SMX_STAGE;
|
||||||
new_smxstage_device->name = "smxstage";
|
new_smxstage_device->name = "smxstage";
|
||||||
@@ -967,6 +984,8 @@ void rawinput::RawInputManager::devices_scan_smxstage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rawinput::RawInputManager::devices_scan_smxdedicab() {
|
void rawinput::RawInputManager::devices_scan_smxdedicab() {
|
||||||
|
log_misc("rawinput", "scan SMX Dedicated Cabinet devices...");
|
||||||
|
|
||||||
auto *new_smxdedicab_device = new Device();
|
auto *new_smxdedicab_device = new Device();
|
||||||
new_smxdedicab_device->type = SMX_DEDICAB;
|
new_smxdedicab_device->type = SMX_DEDICAB;
|
||||||
new_smxdedicab_device->name = "smxdedicab";
|
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,
|
void rawinput::RawInputManager::sextet_register(const std::string &port_name, const std::string &alias,
|
||||||
bool warn) {
|
bool warn) {
|
||||||
|
|
||||||
|
log_misc("rawinput", "checking for sextet-stream device on {}...", port_name);
|
||||||
|
|
||||||
// check for any sextet-stream devices
|
// check for any sextet-stream devices
|
||||||
Device device {};
|
Device device {};
|
||||||
device.type = SEXTET_OUTPUT;
|
device.type = SEXTET_OUTPUT;
|
||||||
@@ -1258,6 +1279,8 @@ void rawinput::RawInputManager::devices_register() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_misc("rawinput", "registering raw input devices...");
|
||||||
|
|
||||||
// register keyboard
|
// register keyboard
|
||||||
RAWINPUTDEVICE keyboard_device{};
|
RAWINPUTDEVICE keyboard_device{};
|
||||||
if (rawinput::NOLEGACY) {
|
if (rawinput::NOLEGACY) {
|
||||||
@@ -1709,6 +1732,38 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
|||||||
// get HID data
|
// get HID data
|
||||||
auto &data_hid = data->data.hid;
|
auto &data_hid = data->data.hid;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
ULONG usages_length = button_count;
|
||||||
|
std::vector<USAGE> usages(static_cast<size_t>(usages_length));
|
||||||
|
if (HidP_GetUsages(
|
||||||
|
HidP_Input,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
for (size_t cap_num = 0; cap_num < device.hidInfo->button_caps_list.size(); cap_num++) {
|
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_caps = device.hidInfo->button_caps_list[cap_num];
|
||||||
@@ -1716,30 +1771,25 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
|||||||
auto &button_down = device.hidInfo->button_down[cap_num];
|
auto &button_down = device.hidInfo->button_down[cap_num];
|
||||||
auto &button_up = device.hidInfo->button_up[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;
|
||||||
|
}
|
||||||
|
|
||||||
// get button count
|
// get button count
|
||||||
int button_count = button_caps.Range.UsageMax - button_caps.Range.UsageMin + 1;
|
int button_count = button_caps.Range.UsageMax - button_caps.Range.UsageMin + 1;
|
||||||
if (button_count <= 0) {
|
if (button_count <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get usages
|
|
||||||
auto usages_length = static_cast<ULONG>(button_count);
|
|
||||||
std::vector<USAGE> usages(static_cast<size_t>(usages_length));
|
|
||||||
if (HidP_GetUsages(
|
|
||||||
HidP_Input,
|
|
||||||
button_caps.UsagePage,
|
|
||||||
button_caps.LinkCollection,
|
|
||||||
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) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update buttons
|
// update buttons
|
||||||
std::vector<bool> new_states(button_count);
|
std::vector<bool> new_states(button_count);
|
||||||
for (ULONG usage_num = 0; usage_num < usages_length; usage_num++) {
|
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;
|
USAGE usage = usages[usage_num] - button_caps.Range.UsageMin;
|
||||||
|
|
||||||
// guard against some buggy device sending an event for a usage below `UsageMin`
|
// guard against some buggy device sending an event for a usage below `UsageMin`
|
||||||
@@ -1759,6 +1809,7 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// analogs
|
// analogs
|
||||||
for (auto cap_num = 0; cap_num < device.hidInfo->caps.NumberInputValueCaps; cap_num++) {
|
for (auto cap_num = 0; cap_num < device.hidInfo->caps.NumberInputValueCaps; cap_num++) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "hotplug.h"
|
#include "hotplug.h"
|
||||||
|
#include "util/scope_guard.h"
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
|
|
||||||
@@ -31,6 +32,15 @@ namespace rawinput {
|
|||||||
extern bool NAIVE_REQUIRE_FOCUS;
|
extern bool NAIVE_REQUIRE_FOCUS;
|
||||||
extern bool RAWINPUT_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 {
|
struct DeviceCallback {
|
||||||
void *data;
|
void *data;
|
||||||
std::function<void(void*, Device*)> f;
|
std::function<void(void*, Device*)> f;
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ namespace rawinput::touch {
|
|||||||
bool INVERTED = false;
|
bool INVERTED = false;
|
||||||
|
|
||||||
// state
|
// state
|
||||||
static bool DISPLAY_INITIALIZED = false;
|
DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
|
||||||
static DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
|
long DISPLAY_SIZE_X = 1920L;
|
||||||
static long DISPLAY_SIZE_X = 1920L;
|
long DISPLAY_SIZE_Y = 1080L;
|
||||||
static long DISPLAY_SIZE_Y = 1080L;
|
bool DISPLAY_INITIALIZED = false;
|
||||||
|
|
||||||
bool is_touchscreen(Device *device) {
|
bool is_touchscreen(Device *device) {
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ namespace rawinput::touch {
|
|||||||
extern bool DISABLED;
|
extern bool DISABLED;
|
||||||
extern bool INVERTED;
|
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);
|
bool is_touchscreen(Device *device);
|
||||||
void enable(Device *device);
|
void enable(Device *device);
|
||||||
void disable(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 "cpuutils.h"
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|||||||
@@ -9,6 +9,16 @@
|
|||||||
#include "peb.h"
|
#include "peb.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
#include "util/dependencies.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::filesystem::path libutils::module_file_name(HMODULE module) {
|
||||||
std::wstring buf;
|
std::wstring buf;
|
||||||
@@ -361,11 +371,106 @@ void libutils::check_duplicate_dlls() {
|
|||||||
void libutils::warn_if_dll_exists(const std::string &file_name) {
|
void libutils::warn_if_dll_exists(const std::string &file_name) {
|
||||||
if (fileutils::file_exists(MODULE_PATH / file_name)) {
|
if (fileutils::file_exists(MODULE_PATH / file_name)) {
|
||||||
log_info("libutils", "found user-supplied {} in modules directory", file_name);
|
log_info("libutils", "found user-supplied {} in modules directory", file_name);
|
||||||
|
libutils::print_dll_info(MODULE_PATH / file_name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
|
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
|
||||||
if (fileutils::file_exists(spice_bin_path / file_name)) {
|
if (fileutils::file_exists(spice_bin_path / file_name)) {
|
||||||
log_info("libutils", "found user-supplied {} next to spice executable 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;
|
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 rva2offset(const std::filesystem::path &path, intptr_t rva);
|
||||||
intptr_t offset2rva(IMAGE_NT_HEADERS *nt_headers, intptr_t offset);
|
intptr_t offset2rva(IMAGE_NT_HEADERS *nt_headers, intptr_t offset);
|
||||||
intptr_t offset2rva(const std::filesystem::path &path, 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 += "Game has crashed.\n\n";
|
||||||
text += "Check log.txt and look for error messages near the end of file.\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 += "Unsure what to do next?\n";
|
||||||
text += " * update spice2x to the latest version\n";
|
text += " * Update spice2x to the latest version\n";
|
||||||
text += " * check the FAQ on spice2x github wiki\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 += " * 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.";
|
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
|
||||||
show_popup(text);
|
show_popup(text);
|
||||||
}
|
}
|
||||||
@@ -76,9 +76,9 @@ void show_popup_for_fatal_error(std::string message) {
|
|||||||
text += message;
|
text += message;
|
||||||
text += "----------------------------------------------------------\n\n";
|
text += "----------------------------------------------------------\n\n";
|
||||||
text += "Unsure what to do next?\n";
|
text += "Unsure what to do next?\n";
|
||||||
text += " * update spice2x to the latest version\n";
|
text += " * Update spice2x to the latest version\n";
|
||||||
text += " * check the FAQ on spice2x github wiki\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 += " * 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.";
|
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
|
||||||
show_popup(text);
|
show_popup(text);
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
#include "sysutils.h"
|
#include "sysutils.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#undef WIN32_NO_STATUS
|
#undef WIN32_NO_STATUS
|
||||||
|
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define log_dbug(module, format_str, ...) logger::push( \
|
#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);
|
log_dbug("gpuinfo", "{} {} flags : 0x{:x}", prefix.c_str(), index, adapter->StateFlags);
|
||||||
|
|
||||||
if (!is_monitor) {
|
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 = {};
|
DEVMODEA devmode = {};
|
||||||
devmode.dmSize = sizeof(devmode);
|
devmode.dmSize = sizeof(devmode);
|
||||||
if (EnumDisplaySettingsA(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) {
|
if (EnumDisplaySettingsA(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) {
|
||||||
@@ -236,11 +254,13 @@ namespace sysutils {
|
|||||||
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
|
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// primary?
|
||||||
log_misc(
|
log_misc(
|
||||||
"gpuinfo", "{} {} is primary : {}",
|
"gpuinfo", "{} {} is primary : {}",
|
||||||
prefix.c_str(),
|
prefix.c_str(),
|
||||||
index,
|
index,
|
||||||
(adapter->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no");
|
(adapter->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,4 +318,92 @@ namespace sysutils {
|
|||||||
return;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace sysutils {
|
namespace sysutils {
|
||||||
void print_smbios();
|
void print_smbios();
|
||||||
void print_gpus();
|
void print_gpus();
|
||||||
void print_os();
|
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>
|
template<class T, class... Args>
|
||||||
auto add(T&& func, Args&&... args)
|
auto add(T&& func, Args&&... args)
|
||||||
-> std::future<typename std::result_of<T(Args...)>::type> {
|
-> std::future<typename std::invoke_result<T, Args...>::type> {
|
||||||
using ret_t = typename std::result_of<T(Args...)>::type;
|
using ret_t = typename std::invoke_result<T, Args...>::type;
|
||||||
auto task = std::make_shared<std::packaged_task<ret_t()>>(
|
auto task = std::make_shared<std::packaged_task<ret_t()>>(
|
||||||
std::bind(std::forward<T>(func), std::forward<Args>(args)...));
|
std::bind(std::forward<T>(func), std::forward<Args>(args)...));
|
||||||
std::future<ret_t> fut = task->get_future();
|
std::future<ret_t> fut = task->get_future();
|
||||||
|
|||||||
Reference in New Issue
Block a user