mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
Compare commits
146 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 | |||
| badae01357 | |||
| 38d9939eb7 | |||
| f5a3caf536 | |||
| 027445afb1 | |||
| 63a3900b79 | |||
| f542a78347 | |||
| 4d32dde83e | |||
| 927170ce73 | |||
| 875a0f0765 | |||
| 3ad88ef15c | |||
| 7a6cadb176 | |||
| 4feca23240 | |||
| 2e52bebd3d | |||
| aa218d5be7 | |||
| 94f009991a | |||
| c3b9ce3fdc | |||
| 1517cf9a7c | |||
| f0f46296f2 | |||
| 1d1981fcde | |||
| 2f538fe2e4 | |||
| 8e01b2e5c8 | |||
| cd14ca800e | |||
| cb59fe14e9 | |||
| 9fd9290cd3 | |||
| 1dd99aac74 | |||
| 9e376f373e | |||
| d4e51b77d1 | |||
| 61181836b0 | |||
| 4e26c8afa5 | |||
| 7684028f99 | |||
| 1d6be8fe4b | |||
| c70cf40af3 | |||
| 5715d4e605 | |||
| 821a705a7d | |||
| 86f2a1fd39 | |||
| 86f218329a | |||
| 66094de38a | |||
| c00cff2afd | |||
| b2cc8d404c | |||
| a0a04ab62f | |||
| a3a59e689e | |||
| a6f29d6a5a | |||
| 9be618d791 | |||
| 6bf7ae9074 | |||
| e3d63c65c1 | |||
| 5d2a9fa1cf | |||
| 0078b72b03 | |||
| b1ee37a066 | |||
| ebf6382e7f | |||
| 8e23ef224a | |||
| 6706ffc321 | |||
| e755b06e35 | |||
| 9ef22712c0 | |||
| 7b903d35e2 | |||
| 7f281a50c5 | |||
| 1354d63b85 | |||
| e745cdcac8 | |||
| bf7666939a | |||
| 7e89037db3 | |||
| 6ffaa77f58 | |||
| 7a1f46d1be | |||
| cf59ae5f89 | |||
| 9c06dd36a0 | |||
| d5d082ec37 | |||
| 17723d290a | |||
| 8de7e5a9e4 | |||
| b8e99e75e5 | |||
| 46dc22dd63 | |||
| 69d78e0a87 | |||
| ab3d41432a | |||
| 73e10cac0c | |||
| c6242233a7 | |||
| 6b8335780f | |||
| 21f469b260 | |||
| 07a0df5fdc | |||
| d4f1768bbd | |||
| 96e7e4949f | |||
| 24d3062c66 | |||
| 6cd212aac2 | |||
| 064031bc12 | |||
| 4c8d4ebab6 | |||
| 462a07680b | |||
| 081def30db | |||
| 6776b08bce | |||
| 6cb507f4fa | |||
| de05af052f | |||
| c767918729 | |||
| fbc8bc2502 | |||
| c38f42cb19 | |||
| 42d8bbc92f | |||
| a09905792d | |||
| f113eb4f52 | |||
| f726748ebe |
+13
-3
@@ -1,8 +1,7 @@
|
||||
## Contributing
|
||||
Baseline rules for patch submissions are as follows. Any patches violating the rules below will not be accepted.
|
||||
|
||||
### New as of March 2025
|
||||
Pull requests in GitHub are open. You don't need to bother with patch file submissions anymore. To contribute, fork the repo (just the main branch), make changes in your fork, and contribute to upstream by opening a pull request to the main repo.
|
||||
To contribute, fork the repo (just the main branch), make changes in your fork, and contribute to upstream by opening a pull request to the main repo.
|
||||
|
||||
### Adding support for games
|
||||
|
||||
@@ -12,6 +11,14 @@ Pull requests in GitHub are open. You don't need to bother with patch file submi
|
||||
* For a new game that was never supported (not a new version of a game, but rather a new series): please wait 1 year after official AC release in Japan.
|
||||
* For new version of an already supported game: proceed with caution and use generally-accepted community guidelines.
|
||||
|
||||
### Forbidden "features" that will never be accepted
|
||||
|
||||
* Performing decryption of encrypted files
|
||||
* Including (bundling), distributing, or linking to game data, or making it easier to find/download game data
|
||||
* Any features that [phone home](https://en.wikipedia.org/wiki/Phoning_home)
|
||||
* This includes automatic updaters.
|
||||
* Exception: we have allowed the "patch import from URL" feature, but this has only been done under very careful considerations and plenty of warnings to the user.
|
||||
|
||||
### Avoiding regressions
|
||||
|
||||
The biggest risk in making code changes to spice is the risk of regressions. There is a **lot** of shared code used by many different games, reaching back game versions that are more than a decade old. It is practically impossible to test all supported games and versions.
|
||||
@@ -22,7 +29,7 @@ Therefore, when making code changes, please be extremely careful about containin
|
||||
|
||||
#### Config file compatibility
|
||||
|
||||
Do not change the names of options, buttons binds, analogs, etc - since they are used as unique identifiers in config files. If they are changed, you will introduce an incompatibility with previous versions of config file.
|
||||
Do not change the names of buttons binds, analogs, strings for command line parameters, etc - since they are used as unique identifiers in config files. If they are changed, you will introduce an incompatibility with previous versions of config file.
|
||||
|
||||
### Code quality requirements
|
||||
|
||||
@@ -49,6 +56,9 @@ Watch out for legacy OS compatibility. Currently, the minimum support floor is W
|
||||
|
||||
### Code style requirements
|
||||
* Indents are four spaces.
|
||||
* Try to keep under 100 characters per line. This includes comments and string literals.
|
||||
* Avoid making unnecessary formatting or whitespace changes in a PR; e.g., watch out for text editor automatically stripping trailing whitespace. It pollutes the diff and slows down code review.
|
||||
* For signed/unsigned integer types, prefer `cstdint` types; e.g., use `int32_t`, `uint64_t` and not `int` and `unsigned long long`.
|
||||
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
||||
|
||||
OK:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
>
|
||||
> Feel free to remove this section after you have read it.
|
||||
|
||||
## Link to GitHub Issue, if one exists
|
||||
## Link to GitHub Issue or related Pull Request, if one exists
|
||||
#0
|
||||
|
||||
## Description of change
|
||||
|
||||
@@ -109,6 +109,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# RapidJSON does this
|
||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||
|
||||
add_compile_options("-Wno-nontrivial-memcall") # imgui, rapidjson
|
||||
add_compile_options("-Wno-deprecated-builtins") # robin_hood
|
||||
add_compile_options("-Wno-deprecated-declarations") # rapidjson
|
||||
add_compile_options("-Wno-inconsistent-missing-override") # headsocket
|
||||
add_compile_options("-Wno-microsoft-exception-spec") # gcc/clang have different noexcept specifiers on d3d9 COM interfaces
|
||||
|
||||
# warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
||||
|
||||
@@ -118,6 +124,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static")
|
||||
else()
|
||||
|
||||
# warnings as errors
|
||||
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
|
||||
|
||||
# warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # enable stuff
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pointer-arith") # but we love pointer arithmetic :)
|
||||
@@ -167,8 +176,8 @@ else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
|
||||
# file prefix map for relative working directory
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||
endif()
|
||||
|
||||
# release with debug info flags
|
||||
@@ -257,6 +266,7 @@ set_source_files_properties(cfg/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/icon.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/Win32D.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(build/manifest64.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(stubs/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
|
||||
# sources
|
||||
#########
|
||||
@@ -343,6 +353,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
cfg/configurator.cpp
|
||||
cfg/configurator_wnd.cpp
|
||||
cfg/screen_resize.cpp
|
||||
cfg/controller_presets.cpp
|
||||
|
||||
# easrv
|
||||
easrv/easrv.cpp
|
||||
@@ -398,6 +409,10 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
games/nost/poke.cpp
|
||||
games/gitadora/gitadora.cpp
|
||||
games/gitadora/io.cpp
|
||||
games/gitadora/handle.cpp
|
||||
games/gitadora/j32d.cpp
|
||||
games/gitadora/j33i.cpp
|
||||
games/gitadora/bi2x_hook.cpp
|
||||
games/mga/mga.cpp
|
||||
games/mga/io.cpp
|
||||
games/mga/gunio.cpp
|
||||
@@ -434,6 +449,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
games/scotto/io.cpp
|
||||
games/drs/drs.cpp
|
||||
games/drs/io.cpp
|
||||
games/drs/rgb_cam.cpp
|
||||
games/we/we.cpp
|
||||
games/we/io.cpp
|
||||
games/we/touchpanel.cpp
|
||||
@@ -470,10 +486,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
hooks/audio/acm.cpp
|
||||
hooks/audio/audio.cpp
|
||||
hooks/audio/buffer.cpp
|
||||
hooks/audio/mme.cpp
|
||||
hooks/audio/util.cpp
|
||||
hooks/audio/backends/dsound/dsound_backend.cpp
|
||||
hooks/audio/backends/mmdevice/audio_endpoint_volume.cpp
|
||||
hooks/audio/backends/mmdevice/device.cpp
|
||||
hooks/audio/backends/mmdevice/device_collection.cpp
|
||||
hooks/audio/backends/mmdevice/device_enumerator.cpp
|
||||
hooks/audio/backends/wasapi/audio_client.cpp
|
||||
hooks/audio/backends/wasapi/audio_render_client.cpp
|
||||
@@ -526,9 +544,11 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
misc/device.cpp
|
||||
misc/eamuse.cpp
|
||||
misc/extdev.cpp
|
||||
misc/nativetouchhook.cpp
|
||||
misc/sciunit.cpp
|
||||
misc/sde.cpp
|
||||
misc/wintouchemu.cpp
|
||||
misc/ami2000.cpp
|
||||
|
||||
# nvapi
|
||||
nvapi/nvapi.cpp
|
||||
@@ -543,9 +563,11 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
overlay/windows/camera_control.cpp
|
||||
overlay/windows/card_manager.cpp
|
||||
overlay/windows/drs_dancefloor.cpp
|
||||
overlay/windows/gfdm_sub.cpp
|
||||
overlay/windows/screen_resize.cpp
|
||||
overlay/windows/sdvx_sub.cpp
|
||||
overlay/windows/config.cpp
|
||||
overlay/windows/controller_presets.cpp
|
||||
overlay/windows/control.cpp
|
||||
overlay/windows/eadev.cpp
|
||||
overlay/windows/fps.cpp
|
||||
@@ -613,6 +635,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
util/execexe.cpp
|
||||
util/dependencies.cpp
|
||||
util/deferlog.cpp
|
||||
util/socd_cleaner.cpp
|
||||
)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||
@@ -791,7 +814,8 @@ endif()
|
||||
|
||||
# nvcuda.dll
|
||||
set(SOURCE_FILES stubs/nvcuda.cpp)
|
||||
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} stubs/nvcuda.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuda.def)
|
||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
|
||||
|
||||
@@ -801,7 +825,8 @@ endif()
|
||||
|
||||
# nvcuvid.dll
|
||||
set(SOURCE_FILES stubs/nvcuvid.cpp)
|
||||
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} stubs/nvcuvid.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuvid.def)
|
||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
|
||||
|
||||
@@ -811,7 +836,8 @@ endif()
|
||||
|
||||
# nvEncodeAPI64.dll
|
||||
set(SOURCE_FILES stubs/nvEncodeAPI64.cpp)
|
||||
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} stubs/nvEncodeAPI64.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvEncodeAPI64.def)
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
|
||||
|
||||
@@ -821,7 +847,8 @@ endif()
|
||||
|
||||
# cpusbxpkm.dll (32 bit)
|
||||
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
|
||||
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} stubs/cpusbxpkm.def)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/cpusbxpkm.def)
|
||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
|
||||
|
||||
|
||||
@@ -9,11 +9,22 @@
|
||||
#include "games/drs/drs.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/tapeled.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[272] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
@@ -94,18 +105,25 @@ static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
||||
}
|
||||
|
||||
// volume left
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||
const auto now = get_performance_milliseconds();
|
||||
const auto vol_l_state = socd::socd_clean(0,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||
now);
|
||||
if (vol_l_state == socd::SocdCCW) {
|
||||
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||
} else if (vol_l_state == socd::SocdCW) {
|
||||
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||
const auto vol_r_state = socd::socd_clean(1,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||
now);
|
||||
if (vol_r_state == socd::SocdCCW) {
|
||||
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||
} else if (vol_r_state == socd::SocdCW) {
|
||||
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
@@ -127,6 +145,12 @@ static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
||||
// save volumes in buffer
|
||||
*((uint16_t*) &STATUS_BUFFER[17]) = (uint16_t) ((vol_left) << 2);
|
||||
*((uint16_t*) &STATUS_BUFFER[19]) = (uint16_t) ((vol_right) << 2);
|
||||
|
||||
log_debug(
|
||||
"bi2a",
|
||||
"knobs = {} {}",
|
||||
*((uint16_t*) &STATUS_BUFFER[17]),
|
||||
*((uint16_t*) &STATUS_BUFFER[19]));
|
||||
}
|
||||
|
||||
// DanceDanceRevolution
|
||||
|
||||
@@ -52,7 +52,7 @@ static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
||||
float pad3_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad3));
|
||||
float pad4_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad4));
|
||||
// FIXME(felix): this logic seems wrong for analog handling but correct for digital inputs
|
||||
if (pad1_vel > 0.f) {
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad1))) {
|
||||
STATUS_BUFFER[6] = (int) (51.f * pad1_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad2))) {
|
||||
@@ -67,10 +67,10 @@ static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
||||
|
||||
// process analogs
|
||||
auto &analogs = games::ftt::get_analogs();
|
||||
auto pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
||||
auto pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
||||
auto pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
||||
auto pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
||||
auto &pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
||||
auto &pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
||||
auto &pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
||||
auto &pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
||||
if (pad1_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad1_analog) + 0.5f);
|
||||
STATUS_BUFFER[6] = MAX(STATUS_BUFFER[6], val);
|
||||
|
||||
@@ -8,10 +8,21 @@
|
||||
#include "games/sdvx/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
// globals
|
||||
uint8_t KFCA_VOL_SOUND = 96;
|
||||
uint8_t KFCA_VOL_HEADPHONE = 96;
|
||||
@@ -342,18 +353,25 @@ static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
||||
}
|
||||
|
||||
// volume left
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||
const auto now = get_performance_milliseconds();
|
||||
const auto vol_l_state = socd::socd_clean(0,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||
now);
|
||||
if (vol_l_state == socd::SocdCCW) {
|
||||
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||
} else if (vol_l_state == socd::SocdCW) {
|
||||
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||
const auto vol_r_state = socd::socd_clean(1,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||
now);
|
||||
if (vol_r_state == socd::SocdCCW) {
|
||||
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||
} else if (vol_r_state == socd::SocdCW) {
|
||||
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
@@ -371,6 +389,8 @@ static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
||||
// proper loops
|
||||
vol_left %= 1024;
|
||||
vol_right %= 1024;
|
||||
|
||||
log_debug("kfca", "knobs = {} {}", vol_left, vol_right);
|
||||
|
||||
// save volumes in buffer
|
||||
STATUS_BUFFER[input_offset + 16 + 0] |= (unsigned char) ((vol_left << 6) & 0xFF);
|
||||
|
||||
+168
-16
@@ -9,6 +9,15 @@
|
||||
#include "util/utils.h"
|
||||
#include <mutex>
|
||||
|
||||
#define MDFX_DEBUG_VERBOSE 0
|
||||
|
||||
#if MDFX_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
|
||||
|
||||
// constants
|
||||
const size_t STATUS_BUFFER_SIZE = 32;
|
||||
const size_t STATUS_BUFFER_NUM_ENTRIES = 16;
|
||||
@@ -30,6 +39,21 @@ static bool IS_MDXF_ACTIVE = false;
|
||||
static const uint8_t BACKFILL_INTERVAL_MS = 4;
|
||||
static const uint8_t BACKFILL_PADDING_MS = 2;
|
||||
|
||||
// These are used to determine if a thread needs to be spun up to keep pad state ring buffers populated with enough recent polls
|
||||
static uint64_t START_TIME = 0;
|
||||
static int CALL_COUNT = 0;
|
||||
static const int THRESHOLD_REFRESH_RATE = 120;
|
||||
|
||||
static std::atomic<bool> IS_REFRESH_RATE_MEASUREMENT_STARTED{false};
|
||||
static std::atomic<bool> IS_REFRESH_RATE_DETERMINED{false};
|
||||
static std::atomic<bool> IS_THREAD_NEEDED{false};
|
||||
static std::atomic<bool> MDXF_THREAD_RUNNING{false};
|
||||
|
||||
static std::thread MDXF_THREAD;
|
||||
|
||||
static constexpr int THREAD_REFRESH_RATE_HZ = 125;
|
||||
static constexpr auto THREAD_PERIOD = std::chrono::milliseconds(1000 / THREAD_REFRESH_RATE_HZ);
|
||||
|
||||
// buffers
|
||||
#pragma pack(push, 1)
|
||||
static struct {
|
||||
@@ -40,9 +64,20 @@ static struct {
|
||||
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
// Decides which method to use for populating ring buffer entries for "padding".
|
||||
// Overwritten in spicecfg using P4IO Buffer Algorithm option.
|
||||
// THREAD_MODE: Spins a thread running at THREAD_REFRESH_RATE_HZ which periodically fills the ring
|
||||
// buffer with auxiliary entries. Falls back on BACKFILL_MODE
|
||||
// BACKFILL_MODE: On every update cycle, fill the ring buffer with entries for the last known state
|
||||
// BACKFILL_INTERVAL_MS apart from each other from the time of the last entry to the
|
||||
// current time before adding the entry for the current state.
|
||||
// AUTO_MODE: thread mode if <120Hz, backfill if >=120Hz
|
||||
acio::MDXFBufferFillMode acio::MDXF_BUFFER_FILL_MODE = acio::MDXFBufferFillMode::AUTO_MODE;
|
||||
|
||||
typedef enum {
|
||||
ARKMDXP4_POLL = 0,
|
||||
EXTERNAL_POLL = 1
|
||||
INTERNAL_POLL = 1,
|
||||
EXTERNAL_POLL = 2
|
||||
} MDXFPollSource;
|
||||
|
||||
typedef uint64_t (__cdecl *ARK_GET_TICK_TIME64_T)();
|
||||
@@ -61,6 +96,106 @@ static uint64_t arkGetTickTime64() {
|
||||
return getTickTime64 ? getTickTime64() : timeGetTime();
|
||||
}
|
||||
|
||||
// Used to keep the ring buffer populated with steady updates. 60Hz interval is too slow
|
||||
static void mdxf_thread_start() {
|
||||
bool expected = false;
|
||||
if (!MDXF_THREAD_RUNNING.compare_exchange_strong(expected, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("mdxf", "starting poll thread");
|
||||
|
||||
MDXF_THREAD = std::thread([] {
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||
|
||||
while (MDXF_THREAD_RUNNING.load(std::memory_order_acquire)) {
|
||||
mdxf_poll(false);
|
||||
std::this_thread::sleep_for(THREAD_PERIOD);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void mdxf_thread_stop() {
|
||||
if (!MDXF_THREAD_RUNNING.exchange(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (MDXF_THREAD.joinable()) {
|
||||
MDXF_THREAD.join();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Snaps measured refresh rate to best fit
|
||||
static int snap_refresh_rate(int measured_hz) {
|
||||
static constexpr std::array<int, 6> rates = {
|
||||
60, 120, 144, 165, 180, 240
|
||||
};
|
||||
|
||||
int best = rates[0];
|
||||
int best_err = std::fabs(measured_hz - best);
|
||||
|
||||
for (int r : rates) {
|
||||
int err = std::fabs(measured_hz - r);
|
||||
if (err < best_err) {
|
||||
best = r;
|
||||
best_err = err;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
// Increments the number of times the update function was called,
|
||||
// then calculates the current refresh rate of the game
|
||||
// (20 seconds after the game starts, until 25 seconds)
|
||||
static void count_calls_from_game() {
|
||||
if (IS_REFRESH_RATE_DETERMINED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t current_time = arkGetTickTime64();
|
||||
|
||||
if (!IS_REFRESH_RATE_MEASUREMENT_STARTED) {
|
||||
if (START_TIME == 0) {
|
||||
START_TIME = current_time;
|
||||
}
|
||||
|
||||
// boot screen takes about 10 seconds, so let's wait for double that
|
||||
if ((current_time - START_TIME) < 20000) {
|
||||
// too early, do nothing
|
||||
return;
|
||||
} else {
|
||||
// 20s has passed for the first time, start measuring on next call
|
||||
IS_REFRESH_RATE_MEASUREMENT_STARTED = true;
|
||||
START_TIME = current_time;
|
||||
log_debug("mdxf", "measurement begin");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t elapsed_time = current_time - START_TIME;
|
||||
CALL_COUNT++;
|
||||
if (elapsed_time >= 5000) {
|
||||
double measured_hz = static_cast<double>(CALL_COUNT) * 1000.0 / static_cast<double>(elapsed_time);
|
||||
|
||||
// Account for the main loop calling this twice per iteration
|
||||
measured_hz *= 0.5;
|
||||
const int snapped_hz = snap_refresh_rate(static_cast<int>(measured_hz));
|
||||
|
||||
IS_REFRESH_RATE_DETERMINED = true;
|
||||
IS_THREAD_NEEDED = (snapped_hz < THRESHOLD_REFRESH_RATE);
|
||||
log_info(
|
||||
"mdxf",
|
||||
"detected: {} Hz, best fit: {} Hz",
|
||||
static_cast<int>(measured_hz),
|
||||
snapped_hz);
|
||||
|
||||
if (IS_THREAD_NEEDED) {
|
||||
mdxf_thread_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
@@ -92,22 +227,20 @@ static uint64_t __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *out
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
|
||||
const uint8_t start_index = (head_in == 0xFF) ? *head : head_in;
|
||||
|
||||
if (head_in != 0xFF) {
|
||||
*head = head_in;
|
||||
}
|
||||
|
||||
// Compute ring index: walk backwards from head as index increases
|
||||
// Compute ring index: walk backwards from start_index as index increases
|
||||
// Assumes ring buffer size is a power of two
|
||||
const size_t mask = size - 1;
|
||||
const size_t offset = static_cast<size_t>(index) & mask;
|
||||
const size_t i = (static_cast<size_t>(*head) - offset + size) & mask;
|
||||
const size_t i = (static_cast<size_t>(start_index) - offset + size) & mask;
|
||||
|
||||
// Copy the chosen entry
|
||||
memcpy(out, buffer[i], STATUS_BUFFER_SIZE);
|
||||
|
||||
// Return the head value actually used
|
||||
return static_cast<uint64_t>(*head);
|
||||
// Return the start value actually used
|
||||
return static_cast<uint64_t>(start_index);
|
||||
}
|
||||
|
||||
return error_ret;
|
||||
@@ -162,8 +295,18 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
||||
// Dance Dance Revolution
|
||||
if (avs::game::is_model("MDX")) {
|
||||
// Marks this module as actively being used, allowing this function to be called from other sources
|
||||
if (!IS_MDXF_ACTIVE && source == ARKMDXP4_POLL) {
|
||||
IS_MDXF_ACTIVE = true;
|
||||
if (source == ARKMDXP4_POLL) {
|
||||
if (!IS_MDXF_ACTIVE) {
|
||||
log_debug("mdxf", "initializing mdxf I/O support");
|
||||
IS_MDXF_ACTIVE = true;
|
||||
if (acio::MDXF_BUFFER_FILL_MODE == acio::MDXFBufferFillMode::THREAD_MODE) {
|
||||
IS_THREAD_NEEDED = true;
|
||||
mdxf_thread_start();
|
||||
}
|
||||
}
|
||||
if (acio::MDXF_BUFFER_FILL_MODE == acio::MDXFBufferFillMode::AUTO_MODE) {
|
||||
count_calls_from_game();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t (*buffer)[STATUS_BUFFER_SIZE];
|
||||
@@ -226,6 +369,8 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
||||
}
|
||||
|
||||
uint16_t current_state;
|
||||
|
||||
// Only call getState() if called externally when actual input events happen, otherwise use previous known state
|
||||
if (source == EXTERNAL_POLL) {
|
||||
// get buttons
|
||||
auto &buttons = games::ddr::get_buttons();
|
||||
@@ -276,8 +421,8 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
||||
start_time = min_time;
|
||||
}
|
||||
|
||||
// Only write one entry if called externally
|
||||
if (source == EXTERNAL_POLL) {
|
||||
// Don't backfill entries if called externally or if a separate thread is being used to fill auxiliary entries
|
||||
if (source == EXTERNAL_POLL || IS_THREAD_NEEDED) {
|
||||
start_time = stop_time - 1;
|
||||
}
|
||||
|
||||
@@ -323,11 +468,12 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
|
||||
}
|
||||
|
||||
// Used for triggering updates of the controller states from outside arkmdxp4.dll main refresh loop (i.e. within rawinput.cpp on controller events)
|
||||
void mdxf_poll() {
|
||||
void mdxf_poll(bool isExternal) {
|
||||
if (IS_MDXF_ACTIVE) {
|
||||
const MDXFPollSource source = isExternal ? EXTERNAL_POLL : INTERNAL_POLL;
|
||||
const uint64_t call_time_ms = arkGetTickTime64();
|
||||
ac_io_mdxf_update_control_status_buffer_impl(17, EXTERNAL_POLL, call_time_ms);
|
||||
ac_io_mdxf_update_control_status_buffer_impl(18, EXTERNAL_POLL, call_time_ms);
|
||||
ac_io_mdxf_update_control_status_buffer_impl(17, source, call_time_ms);
|
||||
ac_io_mdxf_update_control_status_buffer_impl(18, source, call_time_ms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,4 +494,10 @@ void acio::MDXFModule::attach() {
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_set_output_level);
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_update_control_status_buffer);
|
||||
}
|
||||
|
||||
acio::MDXFModule::~MDXFModule() {
|
||||
if (IS_THREAD_NEEDED) {
|
||||
mdxf_thread_stop();
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,24 @@
|
||||
|
||||
namespace acio {
|
||||
|
||||
enum class MDXFBufferFillMode {
|
||||
// backfill mode, but if <120Hz, enable poll thread
|
||||
AUTO_MODE,
|
||||
|
||||
// forces poll thread
|
||||
THREAD_MODE,
|
||||
|
||||
// forces backfill mode (no poll thread)
|
||||
BACKFILL_MODE
|
||||
};
|
||||
|
||||
extern MDXFBufferFillMode MDXF_BUFFER_FILL_MODE;
|
||||
|
||||
class MDXFModule : public ACIOModule {
|
||||
public:
|
||||
MDXFModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
~MDXFModule() override;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
#pragma once
|
||||
|
||||
// Called from rawinput thread whenever inputs have just been updated
|
||||
void mdxf_poll();
|
||||
void mdxf_poll(bool isExternal);
|
||||
@@ -84,7 +84,7 @@ static bool __cdecl ac_io_panb_start_auto_input() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t panb_get_button_velocity(Button button, Button button_soft, Button button_medium, Button button_hard) {
|
||||
static uint8_t panb_get_button_velocity(Button& button, Button& button_soft, Button& button_medium, Button& button_hard) {
|
||||
const auto velocity = Buttons::getVelocity(RI_MGR, button);
|
||||
const auto velocity_soft = Buttons::getVelocity(RI_MGR, button_soft);
|
||||
const auto velocity_medium = Buttons::getVelocity(RI_MGR, button_medium);
|
||||
|
||||
@@ -95,12 +95,19 @@ namespace acio2emu {
|
||||
break;
|
||||
|
||||
default:
|
||||
log_fatal("acio2emu", "cannot set step: unknown value: {}", s);
|
||||
log_fatal(
|
||||
"acio2emu",
|
||||
"cannot set step: unknown value: {}",
|
||||
static_cast<uint32_t>(s));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
log_fatal("acio2emu", "illegal transition detected: {} -> {}", step_, s);
|
||||
log_fatal(
|
||||
"acio2emu",
|
||||
"illegal transition detected: {} -> {}",
|
||||
static_cast<uint32_t>(step_),
|
||||
static_cast<uint32_t>(s));
|
||||
}
|
||||
#endif
|
||||
step_ = s;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
@@ -101,13 +102,10 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
||||
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
if (
|
||||
avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
||||
// SDVX Valkyrie cabinet mode
|
||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
||||
) {
|
||||
if (avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
||||
games::sdvx::is_valkyrie_model()) {
|
||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
||||
} else if (avs::game::is_model({"VFG"})) {
|
||||
} else if (avs::game::is_model("VFG")) {
|
||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
||||
} else {
|
||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||
@@ -520,11 +518,7 @@ void ICCADevice::update_status(int unit) {
|
||||
buffer[1] = felica ? 0x01 : 0x00;
|
||||
buffer[10] = felica ? 0x01 : 0x00;
|
||||
|
||||
} else if (
|
||||
avs::game::is_model({"LDJ", "TBS", "XIF"}) ||
|
||||
// SDVX Valkyrie cabinet mode
|
||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))) {
|
||||
|
||||
} else if (avs::game::is_model({"LDJ", "TBS", "XIF"}) || games::sdvx::is_valkyrie_model()) {
|
||||
// set status to 0 otherwise reader power on fails
|
||||
buffer[0] = 0x00;
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace api::modules {
|
||||
void DRS::touch_set(Request &req, Response &res) {
|
||||
|
||||
// get all touch points
|
||||
games::drs::drs_touch_t touches[16];
|
||||
auto touches = std::make_unique<games::drs::drs_touch_t[]>(req.params.Size());
|
||||
size_t i = 0;
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
@@ -86,6 +86,6 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
// apply touch points
|
||||
games::drs::fire_touches(touches, i);
|
||||
games::drs::fire_touches(touches.get(), i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1108,6 +1108,8 @@ namespace avs {
|
||||
|
||||
// SOUND VOLTEX
|
||||
{"soundvoltex.dll", 0x10000000},
|
||||
// GITADORA
|
||||
{"gdxg.dll", 0x2000000},
|
||||
#endif
|
||||
// jubeat
|
||||
{"jubeat.dll", 0x2000000},
|
||||
@@ -1232,6 +1234,7 @@ namespace avs {
|
||||
break;
|
||||
case 'F':
|
||||
new_style = logger::Style::RED;
|
||||
deferredlogs::report_fatal_message();
|
||||
break;
|
||||
default:
|
||||
position = 0;
|
||||
@@ -1300,6 +1303,7 @@ namespace avs {
|
||||
break;
|
||||
case 'F':
|
||||
style = logger::Style::RED;
|
||||
deferredlogs::report_fatal_message();
|
||||
break;
|
||||
case 'W':
|
||||
style = logger::Style::YELLOW;
|
||||
|
||||
+54
-5
@@ -5,8 +5,9 @@
|
||||
#include "build/defs.h"
|
||||
#include "easrv/easrv.h"
|
||||
#include "easrv/smartea.h"
|
||||
#include "games/mfc/mfc.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "hooks/avshook.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/fileutils.h"
|
||||
@@ -216,7 +217,23 @@ namespace avs {
|
||||
ea3_config_name = "prop/eamuse-config.xml";
|
||||
}
|
||||
|
||||
log_info("avs-ea3", "booting (using {})", ea3_config_name);
|
||||
if (avs::core::file_exists(ea3_config_name)) {
|
||||
log_info("avs-ea3", "booting (using {})", ea3_config_name);
|
||||
} else {
|
||||
log_warning("avs-ea3", "looked for the following files in order:");
|
||||
log_warning("avs-ea3", " * prop/ea3-config.xml");
|
||||
log_warning("avs-ea3", " * prop/ea3-cfg.xml");
|
||||
if (avs::game::DLL_NAME == "beatstream1.dll") {
|
||||
log_warning("avs-ea3", " * prop/ea3-config-1.xml");
|
||||
}
|
||||
if (avs::game::DLL_NAME == "beatstream2.dll") {
|
||||
log_warning("avs-ea3", " * prop/ea3-config-2.xml");
|
||||
}
|
||||
log_warning("avs-ea3", " * prop/eamuse-config.xml");
|
||||
log_warning("avs-ea3", "none of the files above were found, game will fail to boot");
|
||||
log_warning("avs-ea3", "usually, this means your game data is incomplete");
|
||||
log_fatal("avs-ea3", "no ea3 config file found in prop directory; check log messages");
|
||||
}
|
||||
|
||||
// remember new config path
|
||||
CFG_PATH = ea3_config_name;
|
||||
@@ -494,7 +511,9 @@ namespace avs {
|
||||
std::ostringstream init_code;
|
||||
init_code << EA3_MODEL;
|
||||
init_code << EA3_DEST;
|
||||
if (strcmp(EA3_MODEL, "MDX") == 0 && strcmp(EA3_SPEC, "I") == 0) {
|
||||
if (avs::game::DLL_NAME == "arkmdxbio2.dll" &&
|
||||
strcmp(EA3_MODEL, "MDX") == 0 && strcmp(EA3_SPEC, "I") == 0) {
|
||||
// ddr gold cab
|
||||
init_code << "G";
|
||||
init_code << "B";
|
||||
} else {
|
||||
@@ -526,12 +545,42 @@ namespace avs {
|
||||
}
|
||||
}
|
||||
|
||||
// update -WINDOW in app-config
|
||||
if (games::gitadora::is_arena_model()) {
|
||||
avs::core::property_search_remove_safe(app_config, nullptr, "/param/cmdline");
|
||||
std::string cmdline{};
|
||||
if (games::gitadora::is_drum()) {
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
cmdline = "-d -DM -WINDOW";
|
||||
} else {
|
||||
cmdline = "-d -DM";
|
||||
}
|
||||
} else {
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
cmdline = "-g -GF -WINDOW";
|
||||
} else {
|
||||
cmdline = "-g -GF";
|
||||
}
|
||||
}
|
||||
log_info("avs-ea3", "using cmdline: `{}`", cmdline);
|
||||
avs::core::property_node_create(app_config, nullptr,
|
||||
avs::core::NODE_TYPE_str, "/param/cmdline", cmdline.c_str());
|
||||
}
|
||||
|
||||
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
||||
|
||||
// call the game init
|
||||
log_info("avs-ea3", "calling entry init");
|
||||
log_info("avs-ea3", "calling dll_entry_init (init code = {})", init_code_str);
|
||||
if (!avs::game::entry_init(init_code_str.data(), app_param)) {
|
||||
log_fatal("avs-ea3", "entry init failed :(");
|
||||
for (size_t i = 0; i < 255; i++) {
|
||||
log_warning("avs-ea3", "dll_entry_init failed :(");
|
||||
}
|
||||
deferredlogs::defer_error_messages({
|
||||
"dll_entry_init failed",
|
||||
" init code: " + init_code_str,
|
||||
" this is a fatal error that causes boot failure",
|
||||
" most likely, your prop files have errors"
|
||||
});
|
||||
}
|
||||
|
||||
// accommodate changes to soft id code
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
<!-- PHOENIXWAN controller preset for Beatmania IIDX
|
||||
|
||||
Button indices 0-6: keys 1-7
|
||||
Button indices 8-11: E1 (Start for Deluxe Model Style), E2 (Effect), E3 (VEFX), E4 (P1 Start for Lightning Model Style)
|
||||
|
||||
P1 Side P2 Side
|
||||
8 9 10 11 8 9 10 11
|
||||
2 4 6 2 4 6
|
||||
TT 1 3 5 7 1 3 5 7 TT -->
|
||||
<template name="DJ DAO PHOENIXWAN" game="Beatmania IIDX">
|
||||
<buttons>
|
||||
<button name="P1 1" vkey="0" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 2" vkey="1" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 3" vkey="2" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 4" vkey="3" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 5" vkey="4" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 6" vkey="5" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 7" vkey="6" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 1" vkey="0" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 2" vkey="1" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 3" vkey="2" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 4" vkey="3" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 5" vkey="4" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 6" vkey="5" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 7" vkey="6" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<!-- P1 Start: E1 primary (index 8), E4 alternative (index 11) -->
|
||||
<button name="P1 Start" vkey="8" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 Start" vkey="11" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<!-- P2 Start: E1 primary (index 8), E4 alternative (index 11) -->
|
||||
<button name="P2 Start" vkey="8" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 Start" vkey="11" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<!-- EFFECT: E2 (index 9), primary P2 side, alternative P1 side -->
|
||||
<button name="EFFECT" vkey="9" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="EFFECT" vkey="9" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<!-- VEFX: E3 (index 10), primary P2 side, alternative P1 side -->
|
||||
<button name="VEFX" vkey="10" analogtype="0" devid="Player 2" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="VEFX" vkey="10" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
</buttons>
|
||||
<analogs>
|
||||
<analog name="Turntable P1" devid="Player 1" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Turntable P2" devid="Player 2" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="P1 1" devid="Player 1" index="0"/>
|
||||
<light name="P1 2" devid="Player 1" index="1"/>
|
||||
<light name="P1 3" devid="Player 1" index="2"/>
|
||||
<light name="P1 4" devid="Player 1" index="3"/>
|
||||
<light name="P1 5" devid="Player 1" index="4"/>
|
||||
<light name="P1 6" devid="Player 1" index="5"/>
|
||||
<light name="P1 7" devid="Player 1" index="6"/>
|
||||
<light name="P2 1" devid="Player 2" index="0"/>
|
||||
<light name="P2 2" devid="Player 2" index="1"/>
|
||||
<light name="P2 3" devid="Player 2" index="2"/>
|
||||
<light name="P2 4" devid="Player 2" index="3"/>
|
||||
<light name="P2 5" devid="Player 2" index="4"/>
|
||||
<light name="P2 6" devid="Player 2" index="5"/>
|
||||
<light name="P2 7" devid="Player 2" index="6"/>
|
||||
<!-- Start buttons bind both E1 (index 8) and E4 (index 11), but lights use
|
||||
Lightning Model style only (one per side) to avoid both lighting up at once -->
|
||||
<light name="P1 Start" devid="Player 1" index="10"/>
|
||||
<light name="P2 Start" devid="Player 2" index="7"/>
|
||||
<light name="VEFX" devid="Player 1" index="8"/>
|
||||
<light name="VEFX" devid="Player 2" index="8"/>
|
||||
<light name="Effect" devid="Player 1" index="9"/>
|
||||
<light name="Effect" devid="Player 2" index="9"/>
|
||||
</lights>
|
||||
</template>
|
||||
<!-- Common "double scratch" BMS playstyle: LSHIFT A S D SPACE L ; ' RSHIFT -->
|
||||
<!-- Uses Up/Down arrows for turntable navigation -->
|
||||
<template name="Keyboard (linear, double scratch, QWERTY)" game="Beatmania IIDX">
|
||||
<buttons>
|
||||
<button name="P1 1" vkey="65" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 2" vkey="83" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 3" vkey="68" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 4" vkey="32" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 5" vkey="76" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 6" vkey="186" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 7" vkey="222" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 TT+" vkey="40" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 TT-" vkey="38" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 TT+/-" vkey="160" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 TT+/- Alternate" vkey="161" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 Start" vkey="13" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 Start" vkey="69" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="EFFECT" vkey="81" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="VEFX" vkey="87" analogtype="0" devid="" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
</buttons>
|
||||
<keypad_buttons/>
|
||||
<analogs/>
|
||||
<lights/>
|
||||
</template>
|
||||
<template name="arcin (arcin-infinitas)" game="Beatmania IIDX">
|
||||
<buttons>
|
||||
<button name="P1 1" vkey="0" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 2" vkey="1" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 3" vkey="2" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 4" vkey="3" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 5" vkey="4" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 6" vkey="5" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 7" vkey="6" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P1 Start" vkey="9" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 1" vkey="0" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 2" vkey="1" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 3" vkey="2" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 4" vkey="3" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 5" vkey="4" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 6" vkey="5" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 7" vkey="6" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="P2 Start" vkey="9" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="EFFECT" vkey="11" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="EFFECT" vkey="11" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="VEFX" vkey="10" analogtype="0" devid="arcin (1p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="VEFX" vkey="10" analogtype="0" devid="arcin (2p)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
</buttons>
|
||||
<keypad_buttons/>
|
||||
<!-- Assumes analog mode. Need to invert direction since this FW is designed for INF. -->
|
||||
<analogs>
|
||||
<analog name="Turntable P1" devid="arcin (1p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Turntable P2" devid="arcin (2p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="P1 1" devid="arcin (1p)" index="0"/>
|
||||
<light name="P1 2" devid="arcin (1p)" index="1"/>
|
||||
<light name="P1 3" devid="arcin (1p)" index="2"/>
|
||||
<light name="P1 4" devid="arcin (1p)" index="3"/>
|
||||
<light name="P1 5" devid="arcin (1p)" index="4"/>
|
||||
<light name="P1 6" devid="arcin (1p)" index="5"/>
|
||||
<light name="P1 7" devid="arcin (1p)" index="6"/>
|
||||
<light name="P2 1" devid="arcin (2p)" index="0"/>
|
||||
<light name="P2 2" devid="arcin (2p)" index="1"/>
|
||||
<light name="P2 3" devid="arcin (2p)" index="2"/>
|
||||
<light name="P2 4" devid="arcin (2p)" index="3"/>
|
||||
<light name="P2 5" devid="arcin (2p)" index="4"/>
|
||||
<light name="P2 6" devid="arcin (2p)" index="5"/>
|
||||
<light name="P2 7" devid="arcin (2p)" index="6"/>
|
||||
<light name="P1 Start" devid="arcin (1p)" index="9"/>
|
||||
<light name="P2 Start" devid="arcin (2p)" index="9"/>
|
||||
</lights>
|
||||
</template>
|
||||
<!-- This controller has many modes; here, we use HID Analog mode which is meant for data -->
|
||||
<template name="DJ DAO Nostroller (Analog mode)" game="Nostalgia">
|
||||
<buttons/>
|
||||
<keypad_buttons/>
|
||||
<analogs>
|
||||
<analog name="Key 1" devid="Analogs key 01-28" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 2" devid="Analogs key 01-28" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 3" devid="Analogs key 01-28" index="6" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 4" devid="Analogs key 01-28" index="7" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 5" devid="Analogs key 01-28" index="8" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 6" devid="Analogs key 01-28" index="9" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 7" devid="Analogs key 01-28" index="10" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 8" devid="Analogs key 01-28" index="11" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 9" devid="Analogs key 01-28" index="12" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 10" devid="Analogs key 01-28" index="13" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 11" devid="Analogs key 01-28" index="14" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 12" devid="Analogs key 01-28" index="15" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 13" devid="Analogs key 01-28" index="16" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 14" devid="Analogs key 01-28" index="17" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 15" devid="Analogs key 01-28" index="18" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 16" devid="Analogs key 01-28" index="19" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 17" devid="Analogs key 01-28" index="20" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 18" devid="Analogs key 01-28" index="21" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 19" devid="Analogs key 01-28" index="22" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 20" devid="Analogs key 01-28" index="23" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 21" devid="Analogs key 01-28" index="24" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 22" devid="Analogs key 01-28" index="25" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 23" devid="Analogs key 01-28" index="26" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 24" devid="Analogs key 01-28" index="27" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 25" devid="Analogs key 01-28" index="28" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 26" devid="Analogs key 01-28" index="29" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 27" devid="Analogs key 01-28" index="30" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Key 28" devid="Analogs key 01-28" index="31" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="Key 1 R" devid="Lights 01-14" index="0"/>
|
||||
<light name="Key 1 G" devid="Lights 01-14" index="1"/>
|
||||
<light name="Key 1 B" devid="Lights 01-14" index="2"/>
|
||||
<light name="Key 2 R" devid="Lights 01-14" index="3"/>
|
||||
<light name="Key 2 G" devid="Lights 01-14" index="4"/>
|
||||
<light name="Key 2 B" devid="Lights 01-14" index="5"/>
|
||||
<light name="Key 3 R" devid="Lights 01-14" index="6"/>
|
||||
<light name="Key 3 G" devid="Lights 01-14" index="7"/>
|
||||
<light name="Key 3 B" devid="Lights 01-14" index="8"/>
|
||||
<light name="Key 4 R" devid="Lights 01-14" index="9"/>
|
||||
<light name="Key 4 G" devid="Lights 01-14" index="10"/>
|
||||
<light name="Key 4 B" devid="Lights 01-14" index="11"/>
|
||||
<light name="Key 5 R" devid="Lights 01-14" index="12"/>
|
||||
<light name="Key 5 G" devid="Lights 01-14" index="13"/>
|
||||
<light name="Key 5 B" devid="Lights 01-14" index="14"/>
|
||||
<light name="Key 6 R" devid="Lights 01-14" index="15"/>
|
||||
<light name="Key 6 G" devid="Lights 01-14" index="16"/>
|
||||
<light name="Key 6 B" devid="Lights 01-14" index="17"/>
|
||||
<light name="Key 7 R" devid="Lights 01-14" index="18"/>
|
||||
<light name="Key 7 G" devid="Lights 01-14" index="19"/>
|
||||
<light name="Key 7 B" devid="Lights 01-14" index="20"/>
|
||||
<light name="Key 8 R" devid="Lights 01-14" index="21"/>
|
||||
<light name="Key 8 G" devid="Lights 01-14" index="22"/>
|
||||
<light name="Key 8 B" devid="Lights 01-14" index="23"/>
|
||||
<light name="Key 9 R" devid="Lights 01-14" index="24"/>
|
||||
<light name="Key 9 G" devid="Lights 01-14" index="25"/>
|
||||
<light name="Key 9 B" devid="Lights 01-14" index="26"/>
|
||||
<light name="Key 10 R" devid="Lights 01-14" index="27"/>
|
||||
<light name="Key 10 G" devid="Lights 01-14" index="28"/>
|
||||
<light name="Key 10 B" devid="Lights 01-14" index="29"/>
|
||||
<light name="Key 11 R" devid="Lights 01-14" index="30"/>
|
||||
<light name="Key 11 G" devid="Lights 01-14" index="31"/>
|
||||
<light name="Key 11 B" devid="Lights 01-14" index="32"/>
|
||||
<light name="Key 12 R" devid="Lights 01-14" index="33"/>
|
||||
<light name="Key 12 G" devid="Lights 01-14" index="34"/>
|
||||
<light name="Key 12 B" devid="Lights 01-14" index="35"/>
|
||||
<light name="Key 13 R" devid="Lights 01-14" index="36"/>
|
||||
<light name="Key 13 G" devid="Lights 01-14" index="37"/>
|
||||
<light name="Key 13 B" devid="Lights 01-14" index="38"/>
|
||||
<light name="Key 14 R" devid="Lights 01-14" index="39"/>
|
||||
<light name="Key 14 G" devid="Lights 01-14" index="40"/>
|
||||
<light name="Key 14 B" devid="Lights 01-14" index="41"/>
|
||||
<light name="Key 15 R" devid="Lights 15-28" index="0"/>
|
||||
<light name="Key 15 G" devid="Lights 15-28" index="1"/>
|
||||
<light name="Key 15 B" devid="Lights 15-28" index="2"/>
|
||||
<light name="Key 16 R" devid="Lights 15-28" index="3"/>
|
||||
<light name="Key 16 G" devid="Lights 15-28" index="4"/>
|
||||
<light name="Key 16 B" devid="Lights 15-28" index="5"/>
|
||||
<light name="Key 17 R" devid="Lights 15-28" index="6"/>
|
||||
<light name="Key 17 G" devid="Lights 15-28" index="7"/>
|
||||
<light name="Key 17 B" devid="Lights 15-28" index="8"/>
|
||||
<light name="Key 18 R" devid="Lights 15-28" index="9"/>
|
||||
<light name="Key 18 G" devid="Lights 15-28" index="10"/>
|
||||
<light name="Key 18 B" devid="Lights 15-28" index="11"/>
|
||||
<light name="Key 19 R" devid="Lights 15-28" index="12"/>
|
||||
<light name="Key 19 G" devid="Lights 15-28" index="13"/>
|
||||
<light name="Key 19 B" devid="Lights 15-28" index="14"/>
|
||||
<light name="Key 20 R" devid="Lights 15-28" index="15"/>
|
||||
<light name="Key 20 G" devid="Lights 15-28" index="16"/>
|
||||
<light name="Key 20 B" devid="Lights 15-28" index="17"/>
|
||||
<light name="Key 21 R" devid="Lights 15-28" index="18"/>
|
||||
<light name="Key 21 G" devid="Lights 15-28" index="19"/>
|
||||
<light name="Key 21 B" devid="Lights 15-28" index="20"/>
|
||||
<light name="Key 22 R" devid="Lights 15-28" index="21"/>
|
||||
<light name="Key 22 G" devid="Lights 15-28" index="22"/>
|
||||
<light name="Key 22 B" devid="Lights 15-28" index="23"/>
|
||||
<light name="Key 23 R" devid="Lights 15-28" index="24"/>
|
||||
<light name="Key 23 G" devid="Lights 15-28" index="25"/>
|
||||
<light name="Key 23 B" devid="Lights 15-28" index="26"/>
|
||||
<light name="Key 24 R" devid="Lights 15-28" index="27"/>
|
||||
<light name="Key 24 G" devid="Lights 15-28" index="28"/>
|
||||
<light name="Key 24 B" devid="Lights 15-28" index="29"/>
|
||||
<light name="Key 25 R" devid="Lights 15-28" index="30"/>
|
||||
<light name="Key 25 G" devid="Lights 15-28" index="31"/>
|
||||
<light name="Key 25 B" devid="Lights 15-28" index="32"/>
|
||||
<light name="Key 26 R" devid="Lights 15-28" index="33"/>
|
||||
<light name="Key 26 G" devid="Lights 15-28" index="34"/>
|
||||
<light name="Key 26 B" devid="Lights 15-28" index="35"/>
|
||||
<light name="Key 27 R" devid="Lights 15-28" index="36"/>
|
||||
<light name="Key 27 G" devid="Lights 15-28" index="37"/>
|
||||
<light name="Key 27 B" devid="Lights 15-28" index="38"/>
|
||||
<light name="Key 28 R" devid="Lights 15-28" index="39"/>
|
||||
<light name="Key 28 G" devid="Lights 15-28" index="40"/>
|
||||
<light name="Key 28 B" devid="Lights 15-28" index="41"/>
|
||||
</lights>
|
||||
</template>
|
||||
<!-- Gitaller has two modes - digital tilt and analog axis. This binds both of them at the same time. -->
|
||||
<template name="DJ DAO Gitaller" game="GitaDora">
|
||||
<buttons>
|
||||
<button name="Guitar P1 Start" vkey="9" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 Help" vkey="8" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 Pick Up" vkey="5" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 Pick Down" vkey="6" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 R" vkey="0" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 G" vkey="1" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 B" vkey="2" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 Y" vkey="3" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 P" vkey="4" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 Wail Up" vkey="10" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Guitar P1 Wail Down" vkey="11" analogtype="0" devid="Gitaller P1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
</buttons>
|
||||
<keypad_buttons/>
|
||||
<analogs>
|
||||
<analog name="Guitar P1 Wail X" devid="Gitaller P1" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Guitar P1 Wail Y" devid="Gitaller P1" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="Guitar P1 Wail Z" devid="Gitaller P1" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
</analogs>
|
||||
<lights/>
|
||||
<!-- Gen 1 FAUCETWO in HID mode; should also work for later gens -->
|
||||
</template>
|
||||
<template name="DJ DAO FAUCETWO" game="Sound Voltex">
|
||||
<buttons>
|
||||
<button name="BT-A" vkey="1" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="BT-B" vkey="2" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="BT-C" vkey="3" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="BT-D" vkey="4" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="FX-L" vkey="5" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="FX-R" vkey="6" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
<button name="Start" vkey="0" analogtype="0" devid="Buttons (MI_01)" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
</buttons>
|
||||
<keypad_buttons/>
|
||||
<analogs>
|
||||
<analog name="VOL-L" devid="Buttons (MI_01)" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
<analog name="VOL-R" devid="Buttons (MI_01)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="BT-A" devid="Lights (MI_02)" index="24"/>
|
||||
<light name="BT-B" devid="Lights (MI_02)" index="25"/>
|
||||
<light name="BT-C" devid="Lights (MI_02)" index="26"/>
|
||||
<light name="BT-D" devid="Lights (MI_02)" index="27"/>
|
||||
<light name="FX-L" devid="Lights (MI_02)" index="28"/>
|
||||
<light name="FX-R" devid="Lights (MI_02)" index="29"/>
|
||||
<light name="Start" devid="Lights (MI_02)" index="30"/>
|
||||
<light name="Upper Left Speaker Avg R" devid="Lights (MI_02)" index="0"/>
|
||||
<light name="Upper Left Speaker Avg G" devid="Lights (MI_02)" index="1"/>
|
||||
<light name="Upper Left Speaker Avg B" devid="Lights (MI_02)" index="2"/>
|
||||
<light name="Upper Right Speaker Avg R" devid="Lights (MI_02)" index="9"/>
|
||||
<light name="Upper Right Speaker Avg G" devid="Lights (MI_02)" index="10"/>
|
||||
<light name="Upper Right Speaker Avg B" devid="Lights (MI_02)" index="11"/>
|
||||
<light name="Lower Left Speaker Avg R" devid="Lights (MI_02)" index="3"/>
|
||||
<light name="Lower Left Speaker Avg G" devid="Lights (MI_02)" index="4"/>
|
||||
<light name="Lower Left Speaker Avg B" devid="Lights (MI_02)" index="5"/>
|
||||
<light name="Lower Right Speaker Avg R" devid="Lights (MI_02)" index="6"/>
|
||||
<light name="Lower Right Speaker Avg G" devid="Lights (MI_02)" index="7"/>
|
||||
<light name="Lower Right Speaker Avg B" devid="Lights (MI_02)" index="8"/>
|
||||
<light name="Control Panel Avg R" devid="Lights (MI_02)" index="15"/>
|
||||
<light name="Control Panel Avg G" devid="Lights (MI_02)" index="16"/>
|
||||
<light name="Control Panel Avg B" devid="Lights (MI_02)" index="17"/>
|
||||
</lights>
|
||||
</template>
|
||||
</templates>
|
||||
@@ -41,8 +41,8 @@ done
|
||||
# settings
|
||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
||||
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
||||
TOOLCHAIN_32="/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"
|
||||
TOOLCHAIN_64="/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"
|
||||
TOOLCHAIN_32="${TOOLCHAIN_32:-"/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"}"
|
||||
TOOLCHAIN_64="${TOOLCHAIN_64:-"/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"}"
|
||||
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
||||
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
||||
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||
@@ -74,7 +74,7 @@ then
|
||||
BUILD_TYPE="Debug"
|
||||
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
||||
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
||||
DIST_NAME=$(echo ${DIST_NAME} | sed 's/\.[^.]*$/-dbg&/')
|
||||
DIST_NAME=$(echo "${DIST_NAME}" | sed 's/\.[^.]*$/-dbg&/')
|
||||
fi
|
||||
|
||||
# determine number of cores
|
||||
@@ -114,7 +114,7 @@ time (
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_32}
|
||||
pushd ${BUILDDIR_32} > /dev/null
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_32}
|
||||
popd > /dev/null
|
||||
|
||||
# 64 bit
|
||||
@@ -127,7 +127,7 @@ time (
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_64}
|
||||
pushd ${BUILDDIR_64} > /dev/null
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_64}
|
||||
popd > /dev/null
|
||||
|
||||
echo ""
|
||||
@@ -234,7 +234,7 @@ mkdir -p ${OUTDIR}/src
|
||||
if ((INCLUDE_SRC > 0))
|
||||
then
|
||||
echo "Generating source file archive..."
|
||||
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \
|
||||
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > "${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz" 2>/dev/null || \
|
||||
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
||||
fi
|
||||
|
||||
@@ -249,11 +249,11 @@ if ((DIST_ENABLE > 0))
|
||||
then
|
||||
echo "Building dist..."
|
||||
mkdir -p ${DIST_FOLDER}
|
||||
rm -rf ${DIST_FOLDER}/${DIST_NAME}
|
||||
rm -rf "${DIST_FOLDER}/${DIST_NAME}"
|
||||
pushd ${OUTDIR}/.. > /dev/null
|
||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
||||
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME}" spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
||||
echo "Building extras..."
|
||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x -z <<< "$DIST_COMMENT"
|
||||
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS}" spice2x -z <<< "$DIST_COMMENT"
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
docker build --pull $PWD/external/docker -t spicetools/deps --platform linux/x86_64
|
||||
docker build --build-context gitroot=$PWD/../../.git . -t spicetools/spice:latest
|
||||
docker run --rm -v $PWD/dist:/src/src/spice2x/dist -v $PWD/bin:/src/src/spice2x/bin -v $PWD/.ccache:/src/src/spice2x/.ccache spicetools/spice "$@"
|
||||
docker build --pull "$PWD/external/docker" -t spicetools/deps --platform linux/x86_64
|
||||
docker build --build-context gitroot="$PWD/../../.git" . -t spicetools/spice:latest
|
||||
docker run --rm -v "$PWD/dist:/src/src/spice2x/dist" -v "$PWD/bin:/src/src/spice2x/bin" -v "$PWD/.ccache:/src/src/spice2x/.ccache" spicetools/spice "$@"
|
||||
|
||||
@@ -13,5 +13,6 @@ IDR_LICENSES RCDATA "../licenses.txt"
|
||||
IDR_PATCHES RCDATA "../build/patches.json"
|
||||
IDR_README RCDATA "../readme.txt"
|
||||
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
||||
IDR_CONTROLLER_PRESETS_BUILTIN RCDATA "../build/controller_presets.xml"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -91,6 +91,7 @@ public:
|
||||
setMultiplier(1);
|
||||
setRelativeMode(false);
|
||||
setDelayBufferDepth(0);
|
||||
setLastState(0.5f);
|
||||
}
|
||||
|
||||
inline void clearBindings() {
|
||||
|
||||
+50
-5
@@ -37,7 +37,11 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
||||
for (auto &bt : buttons) {
|
||||
if (name == bt.getName()) {
|
||||
button_found = true;
|
||||
sorted.push_back(bt);
|
||||
Button button_new = bt;
|
||||
if (vkey_defaults) {
|
||||
button_new.setVKeyDefault(vkey_defaults->at(index));
|
||||
}
|
||||
sorted.push_back(button_new);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +51,7 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
||||
|
||||
if (vkey_defaults) {
|
||||
button.setVKey(vkey_defaults->at(index));
|
||||
button.setVKeyDefault(vkey_defaults->at(index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,10 +649,12 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
||||
case rawinput::HID: {
|
||||
|
||||
// get value
|
||||
if (inverted) {
|
||||
value = 1.f - device->hidInfo->value_states[index];
|
||||
} else {
|
||||
value = device->hidInfo->value_states[index];
|
||||
if (index < device->hidInfo->value_states.size()) {
|
||||
if (inverted) {
|
||||
value = 1.f - device->hidInfo->value_states[index];
|
||||
} else {
|
||||
value = device->hidInfo->value_states[index];
|
||||
}
|
||||
}
|
||||
|
||||
// deadzone
|
||||
@@ -859,6 +866,44 @@ std::vector<Light> GameAPI::Lights::sortLights(
|
||||
return sorted;
|
||||
}
|
||||
|
||||
static std::vector<Light> sortLightsWithCategoryInternal(
|
||||
const std::vector<Light> &lights,
|
||||
const std::initializer_list<GameAPI::Lights::LightAndCategory> list)
|
||||
{
|
||||
std::vector<Light> sorted;
|
||||
|
||||
bool light_found;
|
||||
for (auto &name : list) {
|
||||
light_found = false;
|
||||
|
||||
for (auto &light : lights) {
|
||||
if (name.light_name == light.getName()) {
|
||||
light_found = true;
|
||||
Light light_new = light;
|
||||
light_new.setCategory(name.category_name);
|
||||
sorted.push_back(light_new);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!light_found) {
|
||||
sorted.emplace_back(name.light_name, name.category_name);
|
||||
}
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
void GameAPI::Lights::sortLightsWithCategory(
|
||||
std::vector<Light> *lights,
|
||||
const std::initializer_list<LightAndCategory> list) {
|
||||
|
||||
if (lights) {
|
||||
*lights = sortLightsWithCategoryInternal(*lights, list);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
|
||||
|
||||
// check device
|
||||
|
||||
+15
-21
@@ -6,24 +6,20 @@
|
||||
#include <windows.h>
|
||||
#include "option.h"
|
||||
|
||||
#include "button.h"
|
||||
#include "analog.h"
|
||||
#include "light.h"
|
||||
|
||||
namespace rawinput {
|
||||
class RawInputManager;
|
||||
struct Device;
|
||||
}
|
||||
|
||||
class Button;
|
||||
class Analog;
|
||||
class Light;
|
||||
class Game;
|
||||
class Option;
|
||||
|
||||
namespace GameAPI {
|
||||
namespace Buttons {
|
||||
enum State {
|
||||
BUTTON_PRESSED = true,
|
||||
BUTTON_NOT_PRESSED = false
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the config and returns the buttons set by the user.
|
||||
*
|
||||
@@ -113,21 +109,13 @@ namespace GameAPI {
|
||||
}
|
||||
|
||||
namespace Lights {
|
||||
|
||||
std::vector<Light> getLights(const std::string &game_name);
|
||||
|
||||
std::vector<Light> sortLights(
|
||||
const std::vector<Light> &lights,
|
||||
const std::vector<std::string> &light_names);
|
||||
|
||||
template<typename T>
|
||||
void sortLights(std::vector<Light> *lights, T t) {
|
||||
const std::vector<std::string> light_names { t };
|
||||
|
||||
if (lights) {
|
||||
*lights = GameAPI::Lights::sortLights(*lights, light_names);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename... Rest>
|
||||
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
|
||||
const std::vector<std::string> light_names { t, rest... };
|
||||
@@ -137,6 +125,16 @@ namespace GameAPI {
|
||||
}
|
||||
}
|
||||
|
||||
struct LightAndCategory {
|
||||
const std::string category_name;
|
||||
const std::string light_name;
|
||||
|
||||
LightAndCategory(std::string category_name, std::string light_name) :
|
||||
category_name(std::move(category_name)), light_name(std::move(light_name)) {}
|
||||
};
|
||||
|
||||
void sortLightsWithCategory(std::vector<Light> *lights, const std::initializer_list<LightAndCategory> list);
|
||||
|
||||
void writeLight(rawinput::Device *device, int index, float value);
|
||||
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
||||
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
||||
@@ -152,7 +150,3 @@ namespace GameAPI {
|
||||
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
|
||||
}
|
||||
}
|
||||
|
||||
#include "button.h"
|
||||
#include "analog.h"
|
||||
#include "light.h"
|
||||
|
||||
+52
-30
@@ -44,7 +44,7 @@ std::string Button::getVKeyString() {
|
||||
case 0x0C:
|
||||
return "Clear";
|
||||
case 0x0D:
|
||||
return "Enter";
|
||||
return "Enter/Return";
|
||||
case 0x10:
|
||||
return "Shift";
|
||||
case 0x11:
|
||||
@@ -58,8 +58,28 @@ std::string Button::getVKeyString() {
|
||||
return "Pause";
|
||||
case 0x14:
|
||||
return "Caps Lock";
|
||||
case 0x15:
|
||||
return "IME Kana/Hangul";
|
||||
case 0x16:
|
||||
return "IME On";
|
||||
case 0x17:
|
||||
return "IME Junja";
|
||||
case 0x18:
|
||||
return "IME Final";
|
||||
case 0x19:
|
||||
return "IME Hanja/Kanji";
|
||||
case 0x1A:
|
||||
return "IME Off";
|
||||
case 0x1B:
|
||||
return "Escape";
|
||||
case 0x1C:
|
||||
return "IME Convert";
|
||||
case 0x1D:
|
||||
return "IME Nonconvert";
|
||||
case 0x1E:
|
||||
return "IME Accept";
|
||||
case 0x1F:
|
||||
return "IME Mode Change";
|
||||
case 0x20:
|
||||
return "Space";
|
||||
case 0x21:
|
||||
@@ -163,37 +183,37 @@ std::string Button::getVKeyString() {
|
||||
case 0x5D:
|
||||
return "Apps";
|
||||
case 0x60:
|
||||
return "Num 0";
|
||||
return "Numpad 0";
|
||||
case 0x61:
|
||||
return "Num 1";
|
||||
return "Numpad 1";
|
||||
case 0x62:
|
||||
return "Num 2";
|
||||
return "Numpad 2";
|
||||
case 0x63:
|
||||
return "Num 3";
|
||||
return "Numpad 3";
|
||||
case 0x64:
|
||||
return "Num 4";
|
||||
return "Numpad 4";
|
||||
case 0x65:
|
||||
return "Num 5";
|
||||
return "Numpad 5";
|
||||
case 0x66:
|
||||
return "Num 6";
|
||||
return "Numpad 6";
|
||||
case 0x67:
|
||||
return "Num 7";
|
||||
return "Numpad 7";
|
||||
case 0x68:
|
||||
return "Num 8";
|
||||
return "Numpad 8";
|
||||
case 0x69:
|
||||
return "Num 9";
|
||||
return "Numpad 9";
|
||||
case 0x6A:
|
||||
return "*";
|
||||
return "Numpad *";
|
||||
case 0x6B:
|
||||
return "+";
|
||||
return "Numpad +";
|
||||
case 0x6C:
|
||||
return "Seperator";
|
||||
return "Numpad Seperator";
|
||||
case 0x6D:
|
||||
return "-";
|
||||
return "Numpad -";
|
||||
case 0x6E:
|
||||
return ".";
|
||||
return "Numpad .";
|
||||
case 0x6F:
|
||||
return "/";
|
||||
return "Numpad /";
|
||||
case 0x70:
|
||||
return "F1";
|
||||
case 0x71:
|
||||
@@ -255,9 +275,11 @@ std::string Button::getVKeyString() {
|
||||
case 0xA3:
|
||||
return "Right Control";
|
||||
case 0xA4:
|
||||
return "Left Menu";
|
||||
return "Left Alt";
|
||||
case 0xA5:
|
||||
return "Right Menu";
|
||||
return "Right Alt";
|
||||
case INVALID_VKEY:
|
||||
return "Invalid";
|
||||
default:
|
||||
|
||||
// check win API
|
||||
@@ -301,17 +323,17 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
||||
switch (device->type) {
|
||||
case rawinput::MOUSE: {
|
||||
const char *btn = "Unknown";
|
||||
static const char *MOUSE_NAMES[] = {
|
||||
"Left Mouse",
|
||||
"Right Mouse",
|
||||
"Middle Mouse",
|
||||
"Mouse 1",
|
||||
"Mouse 2",
|
||||
"Mouse 3",
|
||||
"Mouse 4",
|
||||
"Mouse 5",
|
||||
};
|
||||
if (vKey < sizeof(MOUSE_NAMES)) {
|
||||
static const std::array<const char *, 8>MOUSE_NAMES = {
|
||||
"Left Mouse",
|
||||
"Right Mouse",
|
||||
"Middle Mouse",
|
||||
"Mouse 1",
|
||||
"Mouse 2",
|
||||
"Mouse 3",
|
||||
"Mouse 4",
|
||||
"Mouse 5",
|
||||
};
|
||||
if (vKey < MOUSE_NAMES.size()) {
|
||||
btn = MOUSE_NAMES[vKey];
|
||||
}
|
||||
return fmt::format("{} ({})", btn, device->desc);
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api.h"
|
||||
|
||||
namespace rawinput {
|
||||
class RawInputManager;
|
||||
}
|
||||
|
||||
namespace GameAPI {
|
||||
namespace Buttons {
|
||||
enum State {
|
||||
BUTTON_PRESSED = true,
|
||||
BUTTON_NOT_PRESSED = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum ButtonAnalogType {
|
||||
BAT_NONE = 0,
|
||||
BAT_POSITIVE = 1,
|
||||
@@ -41,19 +49,24 @@ private:
|
||||
std::string name;
|
||||
std::string device_identifier = "";
|
||||
unsigned short vKey = INVALID_VKEY;
|
||||
|
||||
// default bindings are always naive
|
||||
unsigned short vKey_default = INVALID_VKEY;
|
||||
|
||||
ButtonAnalogType analog_type = BAT_NONE;
|
||||
double debounce_up = 0.0;
|
||||
double debounce_down = 0.0;
|
||||
bool invert = false;
|
||||
bool is_temporary = false;
|
||||
|
||||
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||
float last_velocity = 0.f;
|
||||
unsigned short velocity_threshold = 0;
|
||||
|
||||
std::string getVKeyString();
|
||||
std::string getMidiNoteString();
|
||||
|
||||
public:
|
||||
std::string getVKeyString();
|
||||
|
||||
// overrides
|
||||
bool override_enabled = false;
|
||||
@@ -70,12 +83,20 @@ public:
|
||||
if (this->override_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// has a device
|
||||
if (!this->device_identifier.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// naive, and has a valid vKey
|
||||
if (this->vKey != INVALID_VKEY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto &alternative : this->alternatives) {
|
||||
if (alternative.vKey != INVALID_VKEY) {
|
||||
if (!alternative.getDeviceIdentifier().empty() ||
|
||||
alternative.getVKey() != INVALID_VKEY) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -116,6 +137,14 @@ public:
|
||||
this->vKey = vKey;
|
||||
}
|
||||
|
||||
inline unsigned short getVKeyDefault() const {
|
||||
return this->vKey_default;
|
||||
}
|
||||
|
||||
inline void setVKeyDefault(unsigned short vKey_default) {
|
||||
this->vKey_default = vKey_default;
|
||||
}
|
||||
|
||||
inline ButtonAnalogType getAnalogType() const {
|
||||
return this->analog_type;
|
||||
}
|
||||
@@ -164,6 +193,14 @@ public:
|
||||
this->last_velocity = last_velocity;
|
||||
}
|
||||
|
||||
inline bool isTemporary() const {
|
||||
return this->is_temporary;
|
||||
}
|
||||
|
||||
inline void setTemporary(bool is_temporary) {
|
||||
this->is_temporary = is_temporary;
|
||||
}
|
||||
|
||||
inline unsigned short getVelocityThreshold() const {
|
||||
return this->velocity_threshold;
|
||||
}
|
||||
@@ -172,6 +209,20 @@ public:
|
||||
this->velocity_threshold = velocity_threshold;
|
||||
}
|
||||
|
||||
inline bool isValid() {
|
||||
// temporarily created by UI
|
||||
if (isTemporary()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// nothing is set
|
||||
if (getDeviceIdentifier().empty() && getVKey() == INVALID_VKEY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void getMidiVKey(int& channel, int& index);
|
||||
void setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "config.h"
|
||||
#include "util/logging.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
/*
|
||||
* This code absolutely sucks.
|
||||
@@ -10,6 +11,14 @@
|
||||
// settings
|
||||
std::string CONFIG_PATH_OVERRIDE = "";
|
||||
|
||||
#define CFG_DEBUG_VERBOSE 0
|
||||
|
||||
#if CFG_DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
///////////////////
|
||||
/// Constructor ///
|
||||
@@ -510,8 +519,10 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
||||
return false;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *gameButtonNode = nullptr;
|
||||
|
||||
// iterate button nodes
|
||||
tinyxml2::XMLElement *gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||
gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||
int button_count = 0;
|
||||
while (gameButtonNode != nullptr) {
|
||||
const char *buttonNodeName = gameButtonNode->Attribute("name");
|
||||
@@ -574,6 +585,39 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
||||
}
|
||||
}
|
||||
|
||||
// trim the trailing invalid (unbound) alternatives
|
||||
tinyxml2::XMLElement *to_delete = nullptr;
|
||||
gameButtonNode = gameButtonsNode->LastChildElement("button");
|
||||
while (gameButtonNode != nullptr) {
|
||||
const char *name = gameButtonNode->Attribute("name");
|
||||
const char *devid = gameButtonNode->Attribute("devid");
|
||||
int vkey = INVALID_VKEY;
|
||||
gameButtonNode->QueryIntAttribute("vkey", &vkey);
|
||||
|
||||
// wrong button, ignore and keep iterating backwards
|
||||
if (name == nullptr || std::string(name) != button.getName()) {
|
||||
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||
continue;
|
||||
}
|
||||
|
||||
// found a node for this button, which means to_delete is not the first one
|
||||
// delete the node for to_delete
|
||||
if (to_delete != nullptr) {
|
||||
log_debug("cfg", "deleting excessive button binding for {}", name);
|
||||
gameButtonsNode->DeleteChild(to_delete);
|
||||
to_delete = nullptr;
|
||||
}
|
||||
|
||||
if ((devid == nullptr || strlen(devid) == 0) && vkey == INVALID_VKEY) {
|
||||
// unbound button with no device ID, make a note of it so it can be deleted later
|
||||
to_delete = gameButtonNode;
|
||||
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||
} else {
|
||||
// valid binding found; stop iterating
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if button was not found
|
||||
if (button_count == 0) {
|
||||
return false;
|
||||
@@ -760,6 +804,37 @@ bool Config::updateBinding(const Game &game, const Light &light, int alternative
|
||||
}
|
||||
}
|
||||
|
||||
// trim the trailing invalid (unbound) alternatives
|
||||
tinyxml2::XMLElement *to_delete = nullptr;
|
||||
gameLightNode = gameLightsNode->LastChildElement("light");
|
||||
while (gameLightNode != nullptr) {
|
||||
const char *name = gameLightNode->Attribute("name");
|
||||
const char *devid = gameLightNode->Attribute("devid");
|
||||
|
||||
// wrong light, ignore and keep iterating backwards
|
||||
if (name == nullptr || std::string(name) != light.getName()) {
|
||||
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||
continue;
|
||||
}
|
||||
|
||||
// found a node for this button, which means to_delete is not the first one
|
||||
// delete the node for to_delete
|
||||
if (to_delete != nullptr) {
|
||||
log_debug("cfg", "deleting excessive light binding for {}", name);
|
||||
gameLightsNode->DeleteChild(to_delete);
|
||||
to_delete = nullptr;
|
||||
}
|
||||
|
||||
if (devid == nullptr || strlen(devid) == 0) {
|
||||
// unbound light, make a note of it so it can be deleted later
|
||||
to_delete = gameLightNode;
|
||||
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||
} else {
|
||||
// valid binding found; stop iterating
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if light was not found
|
||||
if (light_count == 0) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,460 @@
|
||||
#include "overlay/windows/controller_presets.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "cfg/resource.h"
|
||||
#include "external/tinyxml2/tinyxml2.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/resutils.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
static std::filesystem::path get_templates_path() {
|
||||
return fileutils::get_config_file_path("ControllerPresets", "spicetools_presets.xml");
|
||||
}
|
||||
|
||||
static void write_button_entry(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parent,
|
||||
const std::string &name, const TemplateButtonEntry &entry) {
|
||||
auto *el = doc.NewElement("button");
|
||||
el->SetAttribute("name", name.c_str());
|
||||
el->SetAttribute("vkey", entry.vKey);
|
||||
el->SetAttribute("analogtype", (int)entry.analog_type);
|
||||
el->SetAttribute("devid", entry.device_identifier.c_str());
|
||||
el->SetAttribute("invert", entry.invert);
|
||||
el->SetAttribute("debounce_up", entry.debounce_up);
|
||||
el->SetAttribute("debounce_down", entry.debounce_down);
|
||||
el->SetAttribute("velocity_threshold", entry.velocity_threshold);
|
||||
parent->InsertEndChild(el);
|
||||
}
|
||||
|
||||
static TemplateButtonEntry read_button_entry(tinyxml2::XMLElement *el) {
|
||||
TemplateButtonEntry entry;
|
||||
int vkey = INVALID_VKEY;
|
||||
el->QueryIntAttribute("vkey", &vkey);
|
||||
entry.vKey = (unsigned short)vkey;
|
||||
|
||||
int atype = 0;
|
||||
el->QueryIntAttribute("analogtype", &atype);
|
||||
entry.analog_type = (ButtonAnalogType)atype;
|
||||
|
||||
const char *devid = el->Attribute("devid");
|
||||
entry.device_identifier = devid ? devid : "";
|
||||
|
||||
el->QueryBoolAttribute("invert", &entry.invert);
|
||||
el->QueryDoubleAttribute("debounce_up", &entry.debounce_up);
|
||||
el->QueryDoubleAttribute("debounce_down", &entry.debounce_down);
|
||||
|
||||
int vel = 0;
|
||||
el->QueryIntAttribute("velocity_threshold", &vel);
|
||||
entry.velocity_threshold = (unsigned short)vel;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void write_analog(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parent,
|
||||
const TemplateAnalogBinding &analog) {
|
||||
auto *el = doc.NewElement("analog");
|
||||
el->SetAttribute("name", analog.name.c_str());
|
||||
el->SetAttribute("devid", analog.device_identifier.c_str());
|
||||
el->SetAttribute("index", analog.index);
|
||||
el->SetAttribute("sensivity", analog.sensitivity); // keep typo for compat
|
||||
el->SetAttribute("deadzone", analog.deadzone);
|
||||
el->SetAttribute("deadzone_mirror", analog.deadzone_mirror);
|
||||
el->SetAttribute("invert", analog.invert);
|
||||
el->SetAttribute("smoothing", analog.smoothing);
|
||||
el->SetAttribute("multiplier", analog.multiplier);
|
||||
el->SetAttribute("relative", analog.relative_mode);
|
||||
el->SetAttribute("delay", analog.delay_buffer_depth);
|
||||
parent->InsertEndChild(el);
|
||||
}
|
||||
|
||||
static TemplateAnalogBinding read_analog(tinyxml2::XMLElement *el) {
|
||||
TemplateAnalogBinding a;
|
||||
const char *name = el->Attribute("name");
|
||||
a.name = name ? name : "";
|
||||
|
||||
const char *devid = el->Attribute("devid");
|
||||
a.device_identifier = devid ? devid : "";
|
||||
|
||||
int idx = 0xFF;
|
||||
el->QueryIntAttribute("index", &idx);
|
||||
a.index = (unsigned short)idx;
|
||||
|
||||
el->QueryFloatAttribute("sensivity", &a.sensitivity); // keep typo
|
||||
el->QueryFloatAttribute("deadzone", &a.deadzone);
|
||||
el->QueryBoolAttribute("deadzone_mirror", &a.deadzone_mirror);
|
||||
el->QueryBoolAttribute("invert", &a.invert);
|
||||
el->QueryBoolAttribute("smoothing", &a.smoothing);
|
||||
el->QueryIntAttribute("multiplier", &a.multiplier);
|
||||
el->QueryBoolAttribute("relative", &a.relative_mode);
|
||||
el->QueryIntAttribute("delay", &a.delay_buffer_depth);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static void write_light_entry(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parent,
|
||||
const std::string &name, const TemplateLightEntry &entry) {
|
||||
auto *el = doc.NewElement("light");
|
||||
el->SetAttribute("name", name.c_str());
|
||||
el->SetAttribute("devid", entry.device_identifier.c_str());
|
||||
el->SetAttribute("index", entry.index);
|
||||
parent->InsertEndChild(el);
|
||||
}
|
||||
|
||||
static TemplateLightEntry read_light_entry(tinyxml2::XMLElement *el) {
|
||||
TemplateLightEntry entry;
|
||||
const char *devid = el->Attribute("devid");
|
||||
entry.device_identifier = devid ? devid : "";
|
||||
|
||||
int idx = 0;
|
||||
el->QueryIntAttribute("index", &idx);
|
||||
entry.index = (unsigned int)idx;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Parse all templates from an already-loaded XMLDocument.
|
||||
// is_builtin controls the is_builtin flag on each returned template.
|
||||
static std::vector<ControllerTemplate> load_templates_from_doc(
|
||||
tinyxml2::XMLDocument &doc, bool is_builtin) {
|
||||
std::vector<ControllerTemplate> templates;
|
||||
|
||||
auto *root = doc.RootElement();
|
||||
if (!root) {
|
||||
return templates;
|
||||
}
|
||||
|
||||
auto *tmpl_el = root->FirstChildElement("template");
|
||||
while (tmpl_el) {
|
||||
ControllerTemplate tmpl;
|
||||
const char *name = tmpl_el->Attribute("name");
|
||||
const char *game = tmpl_el->Attribute("game");
|
||||
tmpl.name = name ? name : "";
|
||||
tmpl.game_name = game ? game : "";
|
||||
tmpl.is_builtin = is_builtin;
|
||||
|
||||
// buttons
|
||||
auto *buttons_el = tmpl_el->FirstChildElement("buttons");
|
||||
if (buttons_el) {
|
||||
auto *btn_el = buttons_el->FirstChildElement("button");
|
||||
while (btn_el) {
|
||||
const char *btn_name = btn_el->Attribute("name");
|
||||
std::string btn_name_str = btn_name ? btn_name : "";
|
||||
|
||||
TemplateButtonBinding *binding = nullptr;
|
||||
for (auto &b : tmpl.buttons) {
|
||||
if (b.name == btn_name_str) { binding = &b; break; }
|
||||
}
|
||||
if (!binding) {
|
||||
tmpl.buttons.push_back({btn_name_str, read_button_entry(btn_el), {}});
|
||||
} else {
|
||||
binding->alternatives.push_back(read_button_entry(btn_el));
|
||||
}
|
||||
btn_el = btn_el->NextSiblingElement("button");
|
||||
}
|
||||
}
|
||||
|
||||
// keypad buttons
|
||||
auto *kp_el = tmpl_el->FirstChildElement("keypad_buttons");
|
||||
if (kp_el) {
|
||||
auto *btn_el = kp_el->FirstChildElement("button");
|
||||
while (btn_el) {
|
||||
const char *btn_name = btn_el->Attribute("name");
|
||||
std::string btn_name_str = btn_name ? btn_name : "";
|
||||
|
||||
TemplateButtonBinding *binding = nullptr;
|
||||
for (auto &b : tmpl.keypad_buttons) {
|
||||
if (b.name == btn_name_str) { binding = &b; break; }
|
||||
}
|
||||
if (!binding) {
|
||||
tmpl.keypad_buttons.push_back({btn_name_str, read_button_entry(btn_el), {}});
|
||||
} else {
|
||||
binding->alternatives.push_back(read_button_entry(btn_el));
|
||||
}
|
||||
btn_el = btn_el->NextSiblingElement("button");
|
||||
}
|
||||
}
|
||||
|
||||
// analogs
|
||||
auto *analogs_el = tmpl_el->FirstChildElement("analogs");
|
||||
if (analogs_el) {
|
||||
auto *analog_el = analogs_el->FirstChildElement("analog");
|
||||
while (analog_el) {
|
||||
tmpl.analogs.push_back(read_analog(analog_el));
|
||||
analog_el = analog_el->NextSiblingElement("analog");
|
||||
}
|
||||
}
|
||||
|
||||
// lights
|
||||
auto *lights_el = tmpl_el->FirstChildElement("lights");
|
||||
if (lights_el) {
|
||||
auto *light_el = lights_el->FirstChildElement("light");
|
||||
while (light_el) {
|
||||
const char *light_name = light_el->Attribute("name");
|
||||
std::string light_name_str = light_name ? light_name : "";
|
||||
|
||||
TemplateLightBinding *binding = nullptr;
|
||||
for (auto &l : tmpl.lights) {
|
||||
if (l.name == light_name_str) { binding = &l; break; }
|
||||
}
|
||||
if (!binding) {
|
||||
tmpl.lights.push_back({light_name_str, read_light_entry(light_el), {}});
|
||||
} else {
|
||||
binding->alternatives.push_back(read_light_entry(light_el));
|
||||
}
|
||||
light_el = light_el->NextSiblingElement("light");
|
||||
}
|
||||
}
|
||||
|
||||
templates.push_back(std::move(tmpl));
|
||||
tmpl_el = tmpl_el->NextSiblingElement("template");
|
||||
}
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
static std::vector<ControllerTemplate> load_builtin_templates() {
|
||||
auto xml = resutil::load_file_string(IDR_CONTROLLER_PRESETS_BUILTIN);
|
||||
tinyxml2::XMLDocument doc;
|
||||
auto err = doc.Parse(xml.c_str());
|
||||
if (err != tinyxml2::XML_SUCCESS) {
|
||||
log_warning("templates", "failed to parse builtin templates XML: {}", (int)err);
|
||||
return {};
|
||||
}
|
||||
return load_templates_from_doc(doc, true);
|
||||
}
|
||||
|
||||
std::vector<ControllerTemplate> load_user_templates() {
|
||||
std::vector<ControllerTemplate> templates;
|
||||
auto path = get_templates_path();
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||
return templates;
|
||||
}
|
||||
return load_templates_from_doc(doc, false);
|
||||
}
|
||||
|
||||
bool save_user_template(
|
||||
const ControllerTemplate &tmpl,
|
||||
const bool save_buttons,
|
||||
const bool save_keypads,
|
||||
const bool save_analogs,
|
||||
const bool save_lights) {
|
||||
|
||||
auto path = get_templates_path();
|
||||
auto path_tmp = path;
|
||||
path_tmp.replace_extension(L"tmp");
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||
auto *decl = doc.NewDeclaration();
|
||||
doc.InsertFirstChild(decl);
|
||||
auto *root = doc.NewElement("templates");
|
||||
doc.InsertEndChild(root);
|
||||
}
|
||||
|
||||
auto *root = doc.RootElement();
|
||||
if (!root) {
|
||||
root = doc.NewElement("templates");
|
||||
doc.InsertEndChild(root);
|
||||
}
|
||||
|
||||
// remove existing template with same name and game
|
||||
auto *existing = root->FirstChildElement("template");
|
||||
while (existing) {
|
||||
auto *next = existing->NextSiblingElement("template");
|
||||
const char *ename = existing->Attribute("name");
|
||||
const char *egame = existing->Attribute("game");
|
||||
if (ename && egame &&
|
||||
std::string(ename) == tmpl.name &&
|
||||
std::string(egame) == tmpl.game_name) {
|
||||
root->DeleteChild(existing);
|
||||
}
|
||||
existing = next;
|
||||
}
|
||||
|
||||
auto *tmpl_el = doc.NewElement("template");
|
||||
tmpl_el->SetAttribute("name", tmpl.name.c_str());
|
||||
tmpl_el->SetAttribute("game", tmpl.game_name.c_str());
|
||||
|
||||
// buttons — skip unbound entries
|
||||
auto *buttons_el = doc.NewElement("buttons");
|
||||
if (save_buttons) {
|
||||
for (auto &btn : tmpl.buttons) {
|
||||
if (!btn.primary.is_unbound()) {
|
||||
write_button_entry(doc, buttons_el, btn.name, btn.primary);
|
||||
}
|
||||
for (auto &alt : btn.alternatives) {
|
||||
if (!alt.is_unbound()) {
|
||||
write_button_entry(doc, buttons_el, btn.name, alt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tmpl_el->InsertEndChild(buttons_el);
|
||||
|
||||
// keypad buttons — skip unbound entries
|
||||
if (!tmpl.keypad_buttons.empty()) {
|
||||
auto *kp_el = doc.NewElement("keypad_buttons");
|
||||
if (save_keypads) {
|
||||
for (auto &btn : tmpl.keypad_buttons) {
|
||||
if (!btn.primary.is_unbound()) {
|
||||
write_button_entry(doc, kp_el, btn.name, btn.primary);
|
||||
}
|
||||
for (auto &alt : btn.alternatives) {
|
||||
if (!alt.is_unbound()) {
|
||||
write_button_entry(doc, kp_el, btn.name, alt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tmpl_el->InsertEndChild(kp_el);
|
||||
}
|
||||
|
||||
// analogs — skip unbound entries
|
||||
auto *analogs_el = doc.NewElement("analogs");
|
||||
if (save_analogs) {
|
||||
for (auto &a : tmpl.analogs) {
|
||||
if (!a.is_unbound()) {
|
||||
write_analog(doc, analogs_el, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
tmpl_el->InsertEndChild(analogs_el);
|
||||
|
||||
// lights — skip unbound entries
|
||||
auto *lights_el = doc.NewElement("lights");
|
||||
if (save_lights) {
|
||||
for (auto &l : tmpl.lights) {
|
||||
if (!l.primary.is_unbound()) {
|
||||
write_light_entry(doc, lights_el, l.name, l.primary);
|
||||
}
|
||||
for (auto &alt : l.alternatives) {
|
||||
if (!alt.is_unbound()) {
|
||||
write_light_entry(doc, lights_el, l.name, alt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tmpl_el->InsertEndChild(lights_el);
|
||||
|
||||
root->InsertEndChild(tmpl_el);
|
||||
|
||||
// ensure directory exists
|
||||
if (!path.parent_path().empty() && !std::filesystem::exists(path.parent_path())) {
|
||||
fileutils::dir_create_recursive(path.parent_path());
|
||||
}
|
||||
|
||||
// save atomically via temp file
|
||||
auto save_err = doc.SaveFile(path_tmp.c_str());
|
||||
if (save_err != tinyxml2::XML_SUCCESS) {
|
||||
log_warning("templates", "failed to save templates file: {}", (int)save_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::rename(path_tmp, path, ec);
|
||||
if (ec) {
|
||||
log_warning("templates", "failed to rename templates file: {}", ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool delete_user_template(const std::string &game_name, const std::string &template_name) {
|
||||
auto path = get_templates_path();
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) return false;
|
||||
|
||||
auto *root = doc.RootElement();
|
||||
if (!root) return false;
|
||||
|
||||
bool found = false;
|
||||
auto *tmpl_el = root->FirstChildElement("template");
|
||||
while (tmpl_el) {
|
||||
auto *next = tmpl_el->NextSiblingElement("template");
|
||||
const char *ename = tmpl_el->Attribute("name");
|
||||
const char *egame = tmpl_el->Attribute("game");
|
||||
if (ename && egame &&
|
||||
std::string(ename) == template_name &&
|
||||
std::string(egame) == game_name) {
|
||||
root->DeleteChild(tmpl_el);
|
||||
found = true;
|
||||
}
|
||||
tmpl_el = next;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
auto path_tmp = path;
|
||||
path_tmp.replace_extension(L"tmp");
|
||||
if (doc.SaveFile(path_tmp.c_str()) == tinyxml2::XML_SUCCESS) {
|
||||
std::error_code ec;
|
||||
std::filesystem::rename(path_tmp, path, ec);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool rename_user_template(const std::string &game_name,
|
||||
const std::string &old_name, const std::string &new_name) {
|
||||
auto path = get_templates_path();
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS) return false;
|
||||
|
||||
auto *root = doc.RootElement();
|
||||
if (!root) return false;
|
||||
|
||||
bool found = false;
|
||||
auto *tmpl_el = root->FirstChildElement("template");
|
||||
while (tmpl_el) {
|
||||
const char *ename = tmpl_el->Attribute("name");
|
||||
const char *egame = tmpl_el->Attribute("game");
|
||||
if (ename && egame &&
|
||||
std::string(ename) == old_name &&
|
||||
std::string(egame) == game_name) {
|
||||
tmpl_el->SetAttribute("name", new_name.c_str());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
tmpl_el = tmpl_el->NextSiblingElement("template");
|
||||
}
|
||||
|
||||
if (found) {
|
||||
auto path_tmp = path;
|
||||
path_tmp.replace_extension(L"tmp");
|
||||
if (doc.SaveFile(path_tmp.c_str()) == tinyxml2::XML_SUCCESS) {
|
||||
std::error_code ec;
|
||||
std::filesystem::rename(path_tmp, path, ec);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
std::vector<ControllerTemplate> get_templates_for_game(const std::string &game_name) {
|
||||
std::vector<ControllerTemplate> result;
|
||||
|
||||
log_misc("templates", "loading built-in templates for {}", game_name);
|
||||
for (auto &t : load_builtin_templates()) {
|
||||
if (t.game_name == game_name) {
|
||||
result.push_back(std::move(t));
|
||||
}
|
||||
}
|
||||
|
||||
log_misc("templates", "loading user templates for {}", game_name);
|
||||
for (auto &t : load_user_templates()) {
|
||||
if (t.game_name == game_name) {
|
||||
result.push_back(std::move(t));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,8 +11,10 @@ class Light {
|
||||
private:
|
||||
std::vector<Light> alternatives;
|
||||
std::string lightName;
|
||||
std::string lightCategory;
|
||||
std::string deviceIdentifier = "";
|
||||
unsigned int index = 0;
|
||||
bool is_temporary = false;
|
||||
|
||||
public:
|
||||
float last_state = 0.f;
|
||||
@@ -22,6 +24,8 @@ public:
|
||||
float override_state = 0.f;
|
||||
|
||||
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
|
||||
explicit Light(std::string lightName, std::string lightCategory) :
|
||||
lightName(std::move(lightName)), lightCategory(std::move(lightCategory)) {};
|
||||
|
||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||
|
||||
@@ -50,6 +54,14 @@ public:
|
||||
return this->lightName;
|
||||
}
|
||||
|
||||
inline const std::string &getCategory() const {
|
||||
return this->lightCategory;
|
||||
}
|
||||
|
||||
inline void setCategory(std::string category) {
|
||||
this->lightCategory = std::move(category);
|
||||
}
|
||||
|
||||
inline const std::string &getDeviceIdentifier() const {
|
||||
return this->deviceIdentifier;
|
||||
}
|
||||
@@ -65,4 +77,26 @@ public:
|
||||
inline void setIndex(unsigned int index) {
|
||||
this->index = index;
|
||||
}
|
||||
|
||||
inline bool isTemporary() const {
|
||||
return this->is_temporary;
|
||||
}
|
||||
|
||||
inline void setTemporary(bool is_temporary) {
|
||||
this->is_temporary = is_temporary;
|
||||
}
|
||||
|
||||
inline bool isValid() {
|
||||
// temporarily created by UI
|
||||
if (isTemporary()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// nothing is set
|
||||
if (getDeviceIdentifier().empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,6 +13,16 @@ enum class OptionType {
|
||||
Hex,
|
||||
};
|
||||
|
||||
enum class OptionPickerType {
|
||||
None,
|
||||
AsioDriver,
|
||||
EACard,
|
||||
CpuAffinity,
|
||||
FilePath,
|
||||
DirectoryPath,
|
||||
Monitor,
|
||||
};
|
||||
|
||||
struct OptionDefinition {
|
||||
std::string title;
|
||||
// unique identifier used for flag matching but also stored in config files
|
||||
@@ -32,6 +42,10 @@ struct OptionDefinition {
|
||||
bool sensitive = false;
|
||||
std::vector<std::pair<std::string, std::string>> elements = {};
|
||||
bool disabled = false;
|
||||
OptionPickerType picker = OptionPickerType::None;
|
||||
|
||||
// for OptionPickerType::FilePath
|
||||
std::string file_extension = "";
|
||||
};
|
||||
|
||||
class Option {
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
#define IDR_PATCHES 132
|
||||
#define IDR_README 133
|
||||
#define IDR_DSEGFONT 134
|
||||
#define IDR_CONTROLLER_PRESETS_BUILTIN 135
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "screen_resize.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "external/rapidjson/pointer.h"
|
||||
#include "external/rapidjson/prettywriter.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
@@ -78,6 +80,10 @@ namespace cfg {
|
||||
root);
|
||||
|
||||
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
||||
if (this->enable_screen_resize) {
|
||||
log_misc("ScreenResize", "enabled by config file");
|
||||
}
|
||||
|
||||
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||
auto& scene = this->scene_settings[i];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
@@ -51,6 +52,9 @@ namespace cfg {
|
||||
bool enable_linear_filter = true;
|
||||
fullscreen_setting scene_settings[4];
|
||||
|
||||
// state
|
||||
std::atomic<bool> need_surface_clean = false;
|
||||
|
||||
// windowed mode sizing
|
||||
// Windows terminology:
|
||||
// window = rectangle including the frame
|
||||
@@ -69,6 +73,7 @@ namespace cfg {
|
||||
uint32_t init_client_height = 0;
|
||||
float init_client_aspect_ratio = 1.f;
|
||||
uint32_t init_window_style = 0;
|
||||
uint32_t init_window_style_ex = 0;
|
||||
uint32_t window_deco_width = 0;
|
||||
uint32_t window_deco_height = 0;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2023 Omar Cornut
|
||||
Copyright (c) 2014-2026 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+645
-269
@@ -1,16 +1,37 @@
|
||||
// dear imgui: Renderer for DirectX9
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
// dear imgui: Renderer Backend for DirectX9
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
|
||||
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
|
||||
// 2026-03-19: Fixed issue in ImGui_ImplDX9_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310)
|
||||
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
|
||||
// 2025-06-11: DirectX9: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
|
||||
// 2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
||||
// 2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575)
|
||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-06-25: DirectX9: Explicitly disable texture state stages after >= 1.
|
||||
// 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states.
|
||||
// 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857).
|
||||
// 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file.
|
||||
// 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer.
|
||||
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
|
||||
@@ -22,160 +43,154 @@
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
|
||||
#include "imgui_impl_dx9.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// DirectX
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_dx9.h"
|
||||
|
||||
// allow std::min use
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
|
||||
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
|
||||
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
|
||||
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
|
||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
||||
// DirectX
|
||||
#include <d3d9.h>
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
struct ImGui_ImplDX9_Data
|
||||
{
|
||||
LPDIRECT3DDEVICE9 pd3dDevice;
|
||||
LPDIRECT3DVERTEXBUFFER9 pVB;
|
||||
LPDIRECT3DINDEXBUFFER9 pIB;
|
||||
int VertexBufferSize;
|
||||
int IndexBufferSize;
|
||||
bool HasRgbaSupport;
|
||||
|
||||
ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
|
||||
};
|
||||
|
||||
struct CUSTOMVERTEX
|
||||
{
|
||||
float pos[3];
|
||||
D3DCOLOR col;
|
||||
float uv[2];
|
||||
};
|
||||
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
|
||||
|
||||
// Render function.
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
||||
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
|
||||
#define IMGUI_COL_TO_DX9_ARGB(_COL) (_COL)
|
||||
#else
|
||||
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
|
||||
#endif
|
||||
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||
}
|
||||
|
||||
// Create and grow buffers if needed
|
||||
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) {
|
||||
if (g_pVB) {
|
||||
g_pVB->Release();
|
||||
g_pVB = NULL;
|
||||
}
|
||||
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(ImDrawVert),
|
||||
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX,
|
||||
D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
|
||||
return;
|
||||
}
|
||||
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) {
|
||||
if (g_pIB) {
|
||||
g_pIB->Release();
|
||||
g_pIB = NULL;
|
||||
}
|
||||
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx),
|
||||
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY,
|
||||
sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32,
|
||||
D3DPOOL_DEFAULT, &g_pIB, NULL) < 0)
|
||||
return;
|
||||
}
|
||||
// Forward Declarations
|
||||
static void ImGui_ImplDX9_InitMultiViewportSupport();
|
||||
static void ImGui_ImplDX9_ShutdownMultiViewportSupport();
|
||||
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
|
||||
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
|
||||
|
||||
// Backup the DX9 state
|
||||
IDirect3DStateBlock9 *d3d9_state_block = NULL;
|
||||
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
|
||||
return;
|
||||
struct render_state_t {
|
||||
IDirect3DSurface9 *back_buffer;
|
||||
IDirect3DSurface9 *depth_stencil;
|
||||
IDirect3DSurface9 *render_targets[8];
|
||||
D3DCAPS9 caps;
|
||||
};
|
||||
|
||||
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
|
||||
D3DMATRIX last_world, last_view, last_projection;
|
||||
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
|
||||
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
|
||||
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// Copy all vertices into a single contiguous buffer
|
||||
ImDrawVert *vtx_dst;
|
||||
ImDrawIdx *idx_dst;
|
||||
if (g_pVB->Lock(0, (UINT) (draw_data->TotalVtxCount * sizeof(ImDrawVert)), (void **) &vtx_dst,
|
||||
D3DLOCK_DISCARD) < 0)
|
||||
return;
|
||||
if (g_pIB->Lock(0, (UINT) (draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void **) &idx_dst,
|
||||
D3DLOCK_DISCARD) < 0)
|
||||
return;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++) {
|
||||
const ImDrawList *cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
g_pVB->Unlock();
|
||||
g_pIB->Unlock();
|
||||
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(ImDrawVert));
|
||||
g_pd3dDevice->SetIndices(g_pIB);
|
||||
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
|
||||
// Functions
|
||||
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data, struct render_state_t *render_state)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
|
||||
// Setup viewport
|
||||
D3DVIEWPORT9 vp;
|
||||
vp.X = vp.Y = 0;
|
||||
vp.Width = (DWORD) draw_data->DisplaySize.x;
|
||||
vp.Height = (DWORD) draw_data->DisplaySize.y;
|
||||
vp.Width = (DWORD)draw_data->DisplaySize.x;
|
||||
vp.Height = (DWORD)draw_data->DisplaySize.y;
|
||||
vp.MinZ = 0.0f;
|
||||
vp.MaxZ = 1.0f;
|
||||
g_pd3dDevice->SetViewport(&vp);
|
||||
|
||||
g_pd3dDevice->SetPixelShader(nullptr);
|
||||
g_pd3dDevice->SetVertexShader(nullptr);
|
||||
LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
|
||||
device->SetViewport(&vp);
|
||||
|
||||
D3DCAPS9 caps {};
|
||||
if (FAILED(g_pd3dDevice->GetDeviceCaps(&caps))) {
|
||||
caps.NumSimultaneousRTs = 0UL;
|
||||
}
|
||||
// spice ldj
|
||||
{
|
||||
if (FAILED(device->GetDeviceCaps(&render_state->caps))) {
|
||||
render_state->caps.NumSimultaneousRTs = 0UL;
|
||||
}
|
||||
|
||||
IDirect3DSurface9 *back_buffer = nullptr;
|
||||
IDirect3DSurface9 *depth_stencil = nullptr;
|
||||
IDirect3DSurface9 *render_targets[8];
|
||||
// save all previous render target state
|
||||
for (size_t target = 0; target < std::min(8UL, render_state->caps.NumSimultaneousRTs); target++) {
|
||||
if (FAILED(device->GetRenderTarget(target, &render_state->render_targets[target]))) {
|
||||
render_state->render_targets[target] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// save all previous render target state
|
||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
||||
if (FAILED(g_pd3dDevice->GetRenderTarget(target, &render_targets[target]))) {
|
||||
render_targets[target] = nullptr;
|
||||
// get the previous depth stencil
|
||||
if (FAILED(device->GetDepthStencilSurface(&render_state->depth_stencil))) {
|
||||
render_state->depth_stencil = nullptr;
|
||||
}
|
||||
|
||||
// set the back buffer as the current render target
|
||||
if (SUCCEEDED(device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &render_state->back_buffer))) {
|
||||
device->SetRenderTarget(0, render_state->back_buffer);
|
||||
device->SetDepthStencilSurface(nullptr);
|
||||
|
||||
for (size_t target = 1; target < std::min(8UL, render_state->caps.NumSimultaneousRTs); target++) {
|
||||
device->SetRenderTarget(target, nullptr);
|
||||
}
|
||||
} else {
|
||||
render_state->back_buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// get the previous depth stencil
|
||||
if (FAILED(g_pd3dDevice->GetDepthStencilSurface(&depth_stencil))) {
|
||||
depth_stencil = nullptr;
|
||||
}
|
||||
|
||||
// set the back buffer as the current render target
|
||||
if (SUCCEEDED(g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer))) {
|
||||
g_pd3dDevice->SetRenderTarget(0, back_buffer);
|
||||
g_pd3dDevice->SetDepthStencilSurface(nullptr);
|
||||
|
||||
for (size_t target = 1; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
||||
g_pd3dDevice->SetRenderTarget(target, nullptr);
|
||||
}
|
||||
} else {
|
||||
back_buffer = nullptr;
|
||||
}
|
||||
|
||||
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
|
||||
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||||
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling.
|
||||
device->SetPixelShader(nullptr);
|
||||
device->SetVertexShader(nullptr);
|
||||
device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
|
||||
device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
||||
device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
device->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
||||
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
|
||||
device->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
|
||||
device->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
|
||||
device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
|
||||
device->SetRenderState(D3DRS_FOGENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_CLIPPING, TRUE);
|
||||
device->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
|
||||
device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
|
||||
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
||||
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||||
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
|
||||
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
|
||||
|
||||
// Setup orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
@@ -185,176 +200,537 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
|
||||
float T = draw_data->DisplayPos.y + 0.5f;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
|
||||
D3DMATRIX mat_identity = {{{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } };
|
||||
D3DMATRIX mat_projection =
|
||||
{{{
|
||||
2.0f / (R - L), 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f / (T - B), 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.5f, 0.0f,
|
||||
(L + R) / (L - R), (T + B) / (B - T), 0.5f, 1.0f
|
||||
}}};
|
||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
|
||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
|
||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
||||
{ { {
|
||||
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f/(T-B), 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.5f, 0.0f,
|
||||
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
|
||||
} } };
|
||||
device->SetTransform(D3DTS_WORLD, &mat_identity);
|
||||
device->SetTransform(D3DTS_VIEW, &mat_identity);
|
||||
device->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
||||
}
|
||||
}
|
||||
|
||||
// Render function.
|
||||
void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplDX9_UpdateTexture(tex);
|
||||
|
||||
// Create and grow buffers if needed
|
||||
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
if (device->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0)
|
||||
return;
|
||||
}
|
||||
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
if (device->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Backup the DX9 state
|
||||
IDirect3DStateBlock9* state_block = nullptr;
|
||||
if (device->CreateStateBlock(D3DSBT_ALL, &state_block) < 0)
|
||||
return;
|
||||
if (state_block->Capture() < 0)
|
||||
{
|
||||
state_block->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
|
||||
D3DMATRIX last_world, last_view, last_projection;
|
||||
device->GetTransform(D3DTS_WORLD, &last_world);
|
||||
device->GetTransform(D3DTS_VIEW, &last_view);
|
||||
device->GetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// Allocate buffers
|
||||
CUSTOMVERTEX* vtx_dst;
|
||||
ImDrawIdx* idx_dst;
|
||||
if (bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
|
||||
{
|
||||
state_block->Release();
|
||||
return;
|
||||
}
|
||||
if (bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
|
||||
{
|
||||
bd->pVB->Unlock();
|
||||
state_block->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
|
||||
// FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and
|
||||
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
{
|
||||
const ImDrawVert* vtx_src = draw_list->VtxBuffer.Data;
|
||||
for (int i = 0; i < draw_list->VtxBuffer.Size; i++)
|
||||
{
|
||||
vtx_dst->pos[0] = vtx_src->pos.x;
|
||||
vtx_dst->pos[1] = vtx_src->pos.y;
|
||||
vtx_dst->pos[2] = 0.0f;
|
||||
vtx_dst->col = IMGUI_COL_TO_DX9_ARGB(vtx_src->col);
|
||||
vtx_dst->uv[0] = vtx_src->uv.x;
|
||||
vtx_dst->uv[1] = vtx_src->uv.y;
|
||||
vtx_dst++;
|
||||
vtx_src++;
|
||||
}
|
||||
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
idx_dst += draw_list->IdxBuffer.Size;
|
||||
}
|
||||
bd->pVB->Unlock();
|
||||
bd->pIB->Unlock();
|
||||
device->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX));
|
||||
device->SetIndices(bd->pIB);
|
||||
device->SetFVF(D3DFVF_CUSTOMVERTEX);
|
||||
|
||||
// Setup desired DX state
|
||||
struct render_state_t render_state = {};
|
||||
ImGui_ImplDX9_SetupRenderState(draw_data, &render_state);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++) {
|
||||
const ImDrawList *cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
|
||||
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL) {
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
} else {
|
||||
const RECT r = {(LONG) (pcmd->ClipRect.x - clip_off.x), (LONG) (pcmd->ClipRect.y - clip_off.y),
|
||||
(LONG) (pcmd->ClipRect.z - clip_off.x), (LONG) (pcmd->ClipRect.w - clip_off.y)};
|
||||
auto texture = reinterpret_cast<IDirect3DBaseTexture9 *>(pcmd->TextureId);
|
||||
g_pd3dDevice->SetTexture(0, texture);
|
||||
g_pd3dDevice->SetScissorRect(&r);
|
||||
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
|
||||
pcmd->VtxOffset + global_vtx_offset, 0,
|
||||
(UINT) cmd_list->VtxBuffer.Size,
|
||||
pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
{
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != nullptr)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX9_SetupRenderState(draw_data, &render_state);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
const RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
device->SetScissorRect(&r);
|
||||
|
||||
// Bind texture, Draw
|
||||
const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID();
|
||||
device->SetTexture(0, texture);
|
||||
device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)draw_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
global_idx_offset += draw_list->IdxBuffer.Size;
|
||||
global_vtx_offset += draw_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
if (back_buffer) {
|
||||
back_buffer->Release();
|
||||
back_buffer = nullptr;
|
||||
}
|
||||
// When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows
|
||||
// from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560)
|
||||
if (global_vtx_offset == 0)
|
||||
bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 0, 0, 0);
|
||||
|
||||
// restore previous depth stencil
|
||||
if (depth_stencil) {
|
||||
g_pd3dDevice->SetDepthStencilSurface(depth_stencil);
|
||||
depth_stencil->Release();
|
||||
depth_stencil = nullptr;
|
||||
}
|
||||
// spice ldj
|
||||
{
|
||||
if (render_state.back_buffer) {
|
||||
render_state.back_buffer->Release();
|
||||
render_state.back_buffer = nullptr;
|
||||
}
|
||||
// restore previous depth stencil
|
||||
if (render_state.depth_stencil) {
|
||||
device->SetDepthStencilSurface(render_state.depth_stencil);
|
||||
render_state.depth_stencil->Release();
|
||||
render_state.depth_stencil = nullptr;
|
||||
}
|
||||
// restore all render target state
|
||||
for (size_t target = 0; target < std::min(8UL, render_state.caps.NumSimultaneousRTs); target++) {
|
||||
auto render_target = render_state.render_targets[target];
|
||||
|
||||
// restore all render target state
|
||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
||||
auto render_target = render_targets[target];
|
||||
|
||||
if (render_target) {
|
||||
g_pd3dDevice->SetRenderTarget(target, render_target);
|
||||
render_target->Release();
|
||||
if (render_target) {
|
||||
device->SetRenderTarget(target, render_target);
|
||||
render_target->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore the DX9 transform
|
||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
|
||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
|
||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
// Restore the DX9 transform
|
||||
device->SetTransform(D3DTS_WORLD, &last_world);
|
||||
device->SetTransform(D3DTS_VIEW, &last_view);
|
||||
device->SetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// restore the DX9 state
|
||||
d3d9_state_block->Apply();
|
||||
d3d9_state_block->Release();
|
||||
// Restore the DX9 state
|
||||
state_block->Apply();
|
||||
state_block->Release();
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_Init(IDirect3DDevice9 *device) {
|
||||
static bool ImGui_ImplDX9_CheckFormatSupport(LPDIRECT3DDEVICE9 pDevice, D3DFORMAT format)
|
||||
{
|
||||
LPDIRECT3D9 pd3d = nullptr;
|
||||
if (pDevice->GetDirect3D(&pd3d) != D3D_OK)
|
||||
return false;
|
||||
D3DDEVICE_CREATION_PARAMETERS param = {};
|
||||
D3DDISPLAYMODE mode = {};
|
||||
if (pDevice->GetCreationParameters(¶m) != D3D_OK || pDevice->GetDisplayMode(0, &mode) != D3D_OK)
|
||||
{
|
||||
pd3d->Release();
|
||||
return false;
|
||||
}
|
||||
// Font texture should support linear filter, color blend and write to render-target
|
||||
bool support = (pd3d->CheckDeviceFormat(param.AdapterOrdinal, param.DeviceType, mode.Format, D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, format)) == D3D_OK;
|
||||
pd3d->Release();
|
||||
return support;
|
||||
}
|
||||
|
||||
// Setup back-end capabilities flags
|
||||
auto &io = ImGui::GetIO();
|
||||
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplDX9_Data* bd = IM_NEW(ImGui_ImplDX9_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx9";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
|
||||
|
||||
// We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = 4096;
|
||||
|
||||
bd->pd3dDevice = device;
|
||||
bd->pd3dDevice->AddRef();
|
||||
bd->HasRgbaSupport = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8);
|
||||
|
||||
ImGui_ImplDX9_InitMultiViewportSupport();
|
||||
|
||||
g_pd3dDevice = device;
|
||||
g_pd3dDevice->AddRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_Shutdown() {
|
||||
void ImGui_ImplDX9_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
|
||||
ImGui_ImplDX9_ShutdownMultiViewportSupport();
|
||||
ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||
|
||||
if (g_pd3dDevice) {
|
||||
g_pd3dDevice->Release();
|
||||
g_pd3dDevice = NULL;
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
|
||||
platform_io.ClearRendererHandlers();
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
|
||||
static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, const ImU32* src, int src_pitch, ImU32* dst, int dst_pitch, int w, int h)
|
||||
{
|
||||
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
const bool convert_rgba_to_bgra = (!bd->HasRgbaSupport && tex_use_colors);
|
||||
#else
|
||||
const bool convert_rgba_to_bgra = false;
|
||||
IM_UNUSED(tex_use_colors);
|
||||
#endif
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
const ImU32* src_p = (const ImU32*)(const void*)((const unsigned char*)src + src_pitch * y);
|
||||
ImU32* dst_p = (ImU32*)(void*)((unsigned char*)dst + dst_pitch * y);
|
||||
if (convert_rgba_to_bgra)
|
||||
for (int x = w; x > 0; x--, src_p++, dst_p++) // Convert copy
|
||||
*dst_p = IMGUI_COL_TO_DX9_ARGB(*src_p);
|
||||
else
|
||||
memcpy(dst_p, src_p, w * 4); // Raw copy
|
||||
}
|
||||
}
|
||||
|
||||
static bool ImGui_ImplDX9_CreateFontsTexture() {
|
||||
// Build texture atlas
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
unsigned char *pixels;
|
||||
int width, height, bytes_per_pixel;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
|
||||
void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
|
||||
// Upload texture to graphics system
|
||||
g_FontTexture = NULL;
|
||||
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
|
||||
D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
|
||||
return false;
|
||||
D3DLOCKED_RECT tex_locked_rect;
|
||||
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
|
||||
return false;
|
||||
for (int y = 0; y < height; y++)
|
||||
memcpy((unsigned char *) tex_locked_rect.pBits + tex_locked_rect.Pitch * y,
|
||||
pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
|
||||
g_FontTexture->UnlockRect(0);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID) g_FontTexture;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_CreateDeviceObjects() {
|
||||
if (!g_pd3dDevice) {
|
||||
return false;
|
||||
}
|
||||
return ImGui_ImplDX9_CreateFontsTexture();
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_InvalidateDeviceObjects() {
|
||||
if (!g_pd3dDevice)
|
||||
return;
|
||||
if (g_pVB) {
|
||||
g_pVB->Release();
|
||||
g_pVB = NULL;
|
||||
}
|
||||
if (g_pIB) {
|
||||
g_pIB->Release();
|
||||
g_pIB = NULL;
|
||||
}
|
||||
if (g_FontTexture) {
|
||||
g_FontTexture->Release();
|
||||
g_FontTexture = NULL;
|
||||
ImGui::GetIO().Fonts->TexID = 0;
|
||||
} // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_NewFrame() {
|
||||
if (!g_FontTexture) {
|
||||
ImGui_ImplDX9_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
IDirect3DSwapChain9 *swap_chain = nullptr;
|
||||
if (SUCCEEDED(g_pd3dDevice->GetSwapChain(0, &swap_chain))) {
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
D3DPRESENT_PARAMETERS present_params {};
|
||||
|
||||
if (SUCCEEDED(swap_chain->GetPresentParameters(&present_params))) {
|
||||
if (present_params.BackBufferWidth != 0 && present_params.BackBufferHeight != 0) {
|
||||
io.DisplaySize.x = static_cast<float>(present_params.BackBufferWidth);
|
||||
io.DisplaySize.y = static_cast<float>(present_params.BackBufferHeight);
|
||||
} else {
|
||||
RECT rect {};
|
||||
GetClientRect(present_params.hDeviceWindow, &rect);
|
||||
|
||||
io.DisplaySize.x = static_cast<float>(rect.right - rect.left);
|
||||
io.DisplaySize.y = static_cast<float>(rect.bottom - rect.top);
|
||||
}
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
LPDIRECT3DTEXTURE9 dx_tex = nullptr;
|
||||
HRESULT hr = bd->pd3dDevice->CreateTexture(tex->Width, tex->Height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &dx_tex, nullptr);
|
||||
if (hr < 0)
|
||||
{
|
||||
IM_ASSERT(hr >= 0 && "Backend failed to create texture!");
|
||||
return;
|
||||
}
|
||||
|
||||
swap_chain->Release();
|
||||
D3DLOCKED_RECT locked_rect;
|
||||
if (dx_tex->LockRect(0, &locked_rect, nullptr, 0) == D3D_OK)
|
||||
{
|
||||
ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixels(), tex->Width * 4, (ImU32*)locked_rect.pBits, (ImU32)locked_rect.Pitch, tex->Width, tex->Height);
|
||||
dx_tex->UnlockRect(0);
|
||||
}
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)dx_tex);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantUpdates)
|
||||
{
|
||||
// Update selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
||||
LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)(intptr_t)tex->TexID;
|
||||
RECT update_rect = { (LONG)tex->UpdateRect.x, (LONG)tex->UpdateRect.y, (LONG)(tex->UpdateRect.x + tex->UpdateRect.w), (LONG)(tex->UpdateRect.y + tex->UpdateRect.h) };
|
||||
D3DLOCKED_RECT locked_rect;
|
||||
if (backend_tex->LockRect(0, &locked_rect, &update_rect, 0) == D3D_OK)
|
||||
for (ImTextureRect& r : tex->Updates)
|
||||
ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixelsAt(r.x, r.y), tex->Width * 4,
|
||||
(ImU32*)locked_rect.pBits + (r.x - update_rect.left) + (r.y - update_rect.top) * (locked_rect.Pitch / 4), (int)locked_rect.Pitch, r.w, r.h);
|
||||
backend_tex->UnlockRect(0);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantDestroy)
|
||||
{
|
||||
if (tex->TexID != ImTextureID_Invalid)
|
||||
if (LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)tex->TexID)
|
||||
{
|
||||
IM_ASSERT(tex->TexID == (ImTextureID)(intptr_t)backend_tex);
|
||||
backend_tex->Release();
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_CreateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
if (!bd || !bd->pd3dDevice)
|
||||
return false;
|
||||
ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_InvalidateDeviceObjects()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
if (!bd || !bd->pd3dDevice)
|
||||
return;
|
||||
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
{
|
||||
tex->SetStatus(ImTextureStatus_WantDestroy);
|
||||
ImGui_ImplDX9_UpdateTexture(tex);
|
||||
}
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_NewFrame()
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX9_Init()?");
|
||||
// IM_UNUSED(bd);
|
||||
|
||||
// spice ldj
|
||||
{
|
||||
IDirect3DSwapChain9 *swap_chain = nullptr;
|
||||
if (SUCCEEDED(bd->pd3dDevice->GetSwapChain(0, &swap_chain))) {
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
D3DPRESENT_PARAMETERS present_params {};
|
||||
|
||||
if (SUCCEEDED(swap_chain->GetPresentParameters(&present_params))) {
|
||||
if (present_params.BackBufferWidth != 0 && present_params.BackBufferHeight != 0) {
|
||||
io.DisplaySize.x = static_cast<float>(present_params.BackBufferWidth);
|
||||
io.DisplaySize.y = static_cast<float>(present_params.BackBufferHeight);
|
||||
} else {
|
||||
RECT rect {};
|
||||
GetClientRect(present_params.hDeviceWindow, &rect);
|
||||
|
||||
io.DisplaySize.x = static_cast<float>(rect.right - rect.left);
|
||||
io.DisplaySize.y = static_cast<float>(rect.bottom - rect.top);
|
||||
}
|
||||
}
|
||||
|
||||
swap_chain->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
|
||||
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
|
||||
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
|
||||
struct ImGui_ImplDX9_ViewportData
|
||||
{
|
||||
IDirect3DSwapChain9* SwapChain;
|
||||
D3DPRESENT_PARAMETERS d3dpp;
|
||||
|
||||
ImGui_ImplDX9_ViewportData() { SwapChain = nullptr; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); }
|
||||
~ImGui_ImplDX9_ViewportData() { IM_ASSERT(SwapChain == nullptr); }
|
||||
};
|
||||
|
||||
static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
ImGui_ImplDX9_ViewportData* vd = IM_NEW(ImGui_ImplDX9_ViewportData)();
|
||||
viewport->RendererUserData = vd;
|
||||
|
||||
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL's WindowID).
|
||||
// Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
|
||||
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
|
||||
IM_ASSERT(hwnd != 0);
|
||||
|
||||
ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS));
|
||||
vd->d3dpp.Windowed = TRUE;
|
||||
vd->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||
vd->d3dpp.BackBufferWidth = (UINT)viewport->Size.x;
|
||||
vd->d3dpp.BackBufferHeight = (UINT)viewport->Size.y;
|
||||
vd->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
|
||||
vd->d3dpp.hDeviceWindow = hwnd;
|
||||
vd->d3dpp.EnableAutoDepthStencil = FALSE;
|
||||
vd->d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
|
||||
vd->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync
|
||||
|
||||
HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr);
|
||||
IM_ASSERT(hr == D3D_OK);
|
||||
IM_ASSERT(vd->SwapChain != nullptr);
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport)
|
||||
{
|
||||
// The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
|
||||
if (ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData)
|
||||
{
|
||||
if (vd->SwapChain)
|
||||
vd->SwapChain->Release();
|
||||
vd->SwapChain = nullptr;
|
||||
ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS));
|
||||
IM_DELETE(vd);
|
||||
}
|
||||
viewport->RendererUserData = nullptr;
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
|
||||
if (vd->SwapChain)
|
||||
{
|
||||
vd->SwapChain->Release();
|
||||
vd->SwapChain = nullptr;
|
||||
vd->d3dpp.BackBufferWidth = (UINT)size.x;
|
||||
vd->d3dpp.BackBufferHeight = (UINT)size.y;
|
||||
HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr);
|
||||
IM_ASSERT(hr == D3D_OK);
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*)
|
||||
{
|
||||
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
|
||||
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
LPDIRECT3DSURFACE9 render_target = nullptr;
|
||||
LPDIRECT3DSURFACE9 last_render_target = nullptr;
|
||||
LPDIRECT3DSURFACE9 last_depth_stencil = nullptr;
|
||||
vd->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target);
|
||||
bd->pd3dDevice->GetRenderTarget(0, &last_render_target);
|
||||
bd->pd3dDevice->GetDepthStencilSurface(&last_depth_stencil);
|
||||
bd->pd3dDevice->SetRenderTarget(0, render_target);
|
||||
bd->pd3dDevice->SetDepthStencilSurface(nullptr);
|
||||
|
||||
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
|
||||
{
|
||||
D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*255.0f), (int)(clear_color.y*255.0f), (int)(clear_color.z*255.0f), (int)(clear_color.w*255.0f));
|
||||
bd->pd3dDevice->Clear(0, nullptr, D3DCLEAR_TARGET, clear_col_dx, 1.0f, 0);
|
||||
}
|
||||
|
||||
ImGui_ImplDX9_RenderDrawData(viewport->DrawData);
|
||||
|
||||
// Restore render target
|
||||
bd->pd3dDevice->SetRenderTarget(0, last_render_target);
|
||||
bd->pd3dDevice->SetDepthStencilSurface(last_depth_stencil);
|
||||
render_target->Release();
|
||||
last_render_target->Release();
|
||||
if (last_depth_stencil) last_depth_stencil->Release();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*)
|
||||
{
|
||||
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
|
||||
HRESULT hr = vd->SwapChain->Present(nullptr, nullptr, vd->d3dpp.hDeviceWindow, nullptr, 0);
|
||||
// Let main application handle D3DERR_DEVICELOST by resetting the device.
|
||||
IM_ASSERT(SUCCEEDED(hr) || hr == D3DERR_DEVICELOST);
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_InitMultiViewportSupport()
|
||||
{
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow;
|
||||
platform_io.Renderer_DestroyWindow = ImGui_ImplDX9_DestroyWindow;
|
||||
platform_io.Renderer_SetWindowSize = ImGui_ImplDX9_SetWindowSize;
|
||||
platform_io.Renderer_RenderWindow = ImGui_ImplDX9_RenderWindow;
|
||||
platform_io.Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers;
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_ShutdownMultiViewportSupport()
|
||||
{
|
||||
ImGui::DestroyPlatformWindows();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows()
|
||||
{
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
for (int i = 1; i < platform_io.Viewports.Size; i++)
|
||||
if (!platform_io.Viewports[i]->RendererUserData)
|
||||
ImGui_ImplDX9_CreateWindow(platform_io.Viewports[i]);
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows()
|
||||
{
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
for (int i = 1; i < platform_io.Viewports.Size; i++)
|
||||
if (platform_io.Viewports[i]->RendererUserData)
|
||||
ImGui_ImplDX9_DestroyWindow(platform_io.Viewports[i]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
+25
-12
@@ -1,25 +1,38 @@
|
||||
// dear imgui: Renderer for DirectX9
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
// dear imgui: Renderer Backend for DirectX9
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
|
||||
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct IDirect3DDevice9;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9 *device);
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing ImGui state.
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||
|
||||
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
+13
-5
@@ -15,7 +15,8 @@
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
// - If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
// - Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
@@ -29,7 +30,6 @@
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
@@ -49,6 +49,7 @@
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean/ProggyForever), remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
@@ -59,12 +60,15 @@
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Performance optimization for DX9
|
||||
#define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
@@ -83,13 +87,13 @@
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience.
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
||||
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
|
||||
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
@@ -130,6 +134,10 @@
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
|
||||
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
|
||||
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
|
||||
Vendored
+3439
-1953
File diff suppressed because it is too large
Load Diff
Vendored
+1023
-517
File diff suppressed because it is too large
Load Diff
+2825
-2079
File diff suppressed because it is too large
Load Diff
+3216
-1153
File diff suppressed because it is too large
Load Diff
+780
-340
File diff suppressed because it is too large
Load Diff
+258
-164
@@ -1,6 +1,7 @@
|
||||
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
||||
// Get latest version at http://www.github.com/ocornut/imgui_club
|
||||
//
|
||||
// Licensed under The MIT License (MIT)
|
||||
|
||||
// Right-click anywhere to access the Options menu!
|
||||
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
||||
// The code assume a mono-space font for simplicity!
|
||||
@@ -39,10 +40,22 @@
|
||||
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]
|
||||
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
|
||||
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
|
||||
// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34)
|
||||
// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87.
|
||||
// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35)
|
||||
// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2])
|
||||
// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3]
|
||||
// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3]
|
||||
// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview.
|
||||
// *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed]
|
||||
// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54)
|
||||
// - v0.57 (2025/03/26): fixed warnings. using ImGui's ImSXX/ImUXX types instead of e.g. int32_t/uint32_t. (#56)
|
||||
// - v0.58 (2025/03/31): fixed extraneous footer spacing (added in 0.51) breaking vertical auto-resize. (#53)
|
||||
// - v0.59 (2025/04/08): fixed GotoAddrAndHighlight() not working if OptShowOptions is disabled.
|
||||
//
|
||||
// Todo/Bugs:
|
||||
// TODO:
|
||||
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.
|
||||
// - PageUp/PageDown are supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
|
||||
// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
|
||||
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
|
||||
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
||||
|
||||
@@ -51,15 +64,18 @@
|
||||
#include <stdio.h> // sprintf, scanf
|
||||
#include <stdint.h> // uint8_t, etc.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _PRISizeT "I"
|
||||
#if defined(_MSC_VER) && !defined(snprintf)
|
||||
#define ImSnprintf _snprintf
|
||||
#else
|
||||
#define _PRISizeT "z"
|
||||
#define ImSnprintf snprintf
|
||||
#endif
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define _PRISizeT "I"
|
||||
#else
|
||||
#define _PRISizeT "z"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) || defined(_UCRT)
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||
#endif
|
||||
@@ -88,9 +104,17 @@ struct MemoryEditor
|
||||
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
||||
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
|
||||
ImU32 HighlightColor; // // background color of highlighted bytes.
|
||||
ImU8 (*ReadFn)(const ImU8* data, size_t off); // = 0 // optional handler to read bytes.
|
||||
void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes.
|
||||
bool (*HighlightFn)(const ImU8* data, size_t off);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||
|
||||
// Function handlers
|
||||
ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes.
|
||||
void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes.
|
||||
bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||
ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes.
|
||||
void* UserData; // = NULL // user data forwarded to the function handlers
|
||||
|
||||
// Public read-only data
|
||||
bool MouseHovered; // set when mouse is hovering a value.
|
||||
size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set.
|
||||
|
||||
// [Internal State]
|
||||
bool ContentsWidthChanged;
|
||||
@@ -101,7 +125,7 @@ struct MemoryEditor
|
||||
char AddrInputBuf[32];
|
||||
size_t GotoAddr;
|
||||
size_t HighlightMin, HighlightMax;
|
||||
int PreviewEndianess;
|
||||
int PreviewEndianness;
|
||||
ImGuiDataType PreviewDataType;
|
||||
|
||||
MemoryEditor()
|
||||
@@ -120,9 +144,11 @@ struct MemoryEditor
|
||||
OptAddrDigitsCount = 0;
|
||||
OptFooterExtraHeight = 0.0f;
|
||||
HighlightColor = IM_COL32(255, 255, 255, 50);
|
||||
ReadFn = NULL;
|
||||
WriteFn = NULL;
|
||||
HighlightFn = NULL;
|
||||
ReadFn = nullptr;
|
||||
WriteFn = nullptr;
|
||||
HighlightFn = nullptr;
|
||||
BgColorFn = nullptr;
|
||||
UserData = nullptr;
|
||||
|
||||
// State/Internals
|
||||
ContentsWidthChanged = false;
|
||||
@@ -131,8 +157,10 @@ struct MemoryEditor
|
||||
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
||||
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
||||
GotoAddr = (size_t)-1;
|
||||
MouseHovered = false;
|
||||
MouseHoveredAddr = 0;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
PreviewEndianess = 0;
|
||||
PreviewEndianness = 0;
|
||||
PreviewDataType = ImGuiDataType_S32;
|
||||
}
|
||||
|
||||
@@ -145,16 +173,16 @@ struct MemoryEditor
|
||||
|
||||
struct Sizes
|
||||
{
|
||||
int AddrDigitsCount;
|
||||
float LineHeight;
|
||||
float GlyphWidth;
|
||||
float HexCellWidth;
|
||||
float SpacingBetweenMidCols;
|
||||
float PosHexStart;
|
||||
float PosHexEnd;
|
||||
float PosAsciiStart;
|
||||
float PosAsciiEnd;
|
||||
float WindowWidth;
|
||||
int AddrDigitsCount; // Number of digits required to represent maximum address.
|
||||
float LineHeight; // Height of each line (no spacing).
|
||||
float GlyphWidth; // Glyph width (assume mono-space).
|
||||
float HexCellWidth; // Width of a hex edit cell ~2.5f * GlypHWidth.
|
||||
float SpacingBetweenMidCols; // Spacing between each columns section (OptMidColsCount).
|
||||
float OffsetHexMinX;
|
||||
float OffsetHexMaxX;
|
||||
float OffsetAsciiMinX;
|
||||
float OffsetAsciiMaxX;
|
||||
float WindowWidth; // Ideal window width.
|
||||
|
||||
Sizes() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
@@ -170,17 +198,17 @@ struct MemoryEditor
|
||||
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
|
||||
s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere
|
||||
s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
|
||||
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
||||
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
||||
s.OffsetHexMinX = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||
s.OffsetHexMaxX = s.OffsetHexMinX + (s.HexCellWidth * Cols);
|
||||
s.OffsetAsciiMinX = s.OffsetAsciiMaxX = s.OffsetHexMaxX;
|
||||
if (OptShowAscii)
|
||||
{
|
||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||
s.OffsetAsciiMinX = s.OffsetHexMaxX + s.GlyphWidth * 1;
|
||||
if (OptMidColsCount > 0)
|
||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
||||
s.OffsetAsciiMinX += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
s.OffsetAsciiMaxX = s.OffsetAsciiMinX + Cols * s.GlyphWidth;
|
||||
}
|
||||
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||
s.WindowWidth = s.OffsetAsciiMaxX + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||
}
|
||||
|
||||
// Standalone Memory Editor window
|
||||
@@ -194,8 +222,6 @@ struct MemoryEditor
|
||||
Open = true;
|
||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("context");
|
||||
DrawContents(mem_data, mem_size, base_display_addr);
|
||||
if (ContentsWidthChanged)
|
||||
{
|
||||
@@ -217,6 +243,8 @@ struct MemoryEditor
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos();
|
||||
|
||||
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
||||
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
||||
const float height_separator = style.ItemSpacing.y;
|
||||
@@ -225,20 +253,21 @@ struct MemoryEditor
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||
if (OptShowDataPreview)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
|
||||
const ImVec2 avail_size = ImGui::GetContentRegionAvail();
|
||||
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(line_total_count, s.LineHeight);
|
||||
|
||||
bool data_next = false;
|
||||
|
||||
if (ReadOnly || DataEditingAddr >= mem_size)
|
||||
if (DataEditingAddr >= mem_size)
|
||||
DataEditingAddr = (size_t)-1;
|
||||
if (DataPreviewAddr >= mem_size)
|
||||
DataPreviewAddr = (size_t)-1;
|
||||
@@ -249,16 +278,16 @@ struct MemoryEditor
|
||||
if (DataEditingAddr != (size_t)-1)
|
||||
{
|
||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; }
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
||||
}
|
||||
|
||||
// Draw vertical separator
|
||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||
if (OptShowAscii)
|
||||
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||
draw_list->AddLine(ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||
|
||||
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
||||
@@ -268,36 +297,51 @@ struct MemoryEditor
|
||||
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||
|
||||
MouseHovered = false;
|
||||
MouseHoveredAddr = 0;
|
||||
|
||||
while (clipper.Step())
|
||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
||||
{
|
||||
size_t addr = (size_t)(line_i * Cols);
|
||||
size_t addr = (size_t)line_i * Cols;
|
||||
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||
|
||||
// Draw Hexadecimal
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
|
||||
float byte_pos_x = s.OffsetHexMinX + s.HexCellWidth * n;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
||||
// Draw highlight or custom background color
|
||||
const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData));
|
||||
const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
||||
|
||||
ImU32 bg_color = 0;
|
||||
bool is_next_byte_highlighted = false;
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float highlight_width = s.GlyphWidth * 2;
|
||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1)));
|
||||
is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size));
|
||||
bg_color = HighlightColor;
|
||||
}
|
||||
else if (BgColorFn != nullptr)
|
||||
{
|
||||
is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0);
|
||||
bg_color = BgColorFn(mem_data, addr, UserData);
|
||||
}
|
||||
if (bg_color != 0)
|
||||
{
|
||||
float bg_width = s.GlyphWidth * 2;
|
||||
if (is_next_byte_highlighted || (n + 1 == Cols))
|
||||
{
|
||||
highlight_width = s.HexCellWidth;
|
||||
bg_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||
highlight_width += s.SpacingBetweenMidCols;
|
||||
bg_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color);
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr)
|
||||
@@ -308,17 +352,21 @@ struct MemoryEditor
|
||||
if (DataEditingTakeFocus)
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||
ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||
}
|
||||
struct UserData
|
||||
struct InputTextUserData
|
||||
{
|
||||
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
||||
static int Callback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
UserData* user_data = (UserData*)data->UserData;
|
||||
InputTextUserData* user_data = (InputTextUserData*)data->UserData;
|
||||
if (!data->HasSelection())
|
||||
user_data->CursorPos = data->CursorPos;
|
||||
#if IMGUI_VERSION_NUM < 19102
|
||||
if (data->Flags & ImGuiInputTextFlags_ReadOnly)
|
||||
return 0;
|
||||
#endif
|
||||
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||
{
|
||||
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
|
||||
@@ -334,39 +382,42 @@ struct MemoryEditor
|
||||
char CurrentBufOverwrite[3]; // Input
|
||||
int CursorPos; // Output
|
||||
};
|
||||
UserData user_data;
|
||||
user_data.CursorPos = -1;
|
||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
InputTextUserData input_text_user_data;
|
||||
input_text_user_data.CursorPos = -1;
|
||||
ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
|
||||
#if IMGUI_VERSION_NUM >= 18104
|
||||
flags |= ImGuiInputTextFlags_AlwaysOverwrite;
|
||||
#else
|
||||
flags |= ImGuiInputTextFlags_AlwaysInsertMode;
|
||||
#endif
|
||||
if (ReadOnly)
|
||||
flags |= ImGuiInputTextFlags_ReadOnly;
|
||||
flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode
|
||||
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
|
||||
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, UserData::Callback, &user_data))
|
||||
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data))
|
||||
data_write = data_next = true;
|
||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
if (user_data.CursorPos >= 2)
|
||||
if (input_text_user_data.CursorPos >= 2)
|
||||
data_write = data_next = true;
|
||||
if (data_editing_addr_next != (size_t)-1)
|
||||
data_write = data_next = false;
|
||||
unsigned int data_input_value = 0;
|
||||
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||
if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||
{
|
||||
if (WriteFn)
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);
|
||||
else
|
||||
mem_data[addr] = (ImU8)data_input_value;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = addr;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
else
|
||||
{
|
||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||
|
||||
if (OptShowHexII)
|
||||
{
|
||||
@@ -386,10 +437,15 @@ struct MemoryEditor
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = addr;
|
||||
if (ImGui::IsMouseClicked(0))
|
||||
{
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -397,15 +453,24 @@ struct MemoryEditor
|
||||
if (OptShowAscii)
|
||||
{
|
||||
// Draw ASCII values
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImGui::SameLine(s.OffsetAsciiMinX);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
addr = (size_t)line_i * Cols;
|
||||
|
||||
const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x;
|
||||
const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.OffsetAsciiMaxX - s.OffsetAsciiMinX) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1;
|
||||
|
||||
ImGui::PushID(line_i);
|
||||
if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))
|
||||
if (ImGui::InvisibleButton("ascii", ImVec2(s.OffsetAsciiMaxX - s.OffsetAsciiMinX, s.LineHeight)))
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = addr + (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth);
|
||||
DataEditingAddr = DataPreviewAddr = mouse_addr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = mouse_addr;
|
||||
}
|
||||
ImGui::PopID();
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
@@ -414,7 +479,11 @@ struct MemoryEditor
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
else if (BgColorFn)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData));
|
||||
}
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||
pos.x += s.GlyphWidth;
|
||||
@@ -422,10 +491,14 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
const float child_width = ImGui::GetWindowSize().x;
|
||||
ImGui::EndChild();
|
||||
|
||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
||||
ImVec2 backup_pos = ImGui::GetCursorScreenPos();
|
||||
ImGui::SetCursorPosX(s.WindowWidth);
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
ImGui::SetCursorScreenPos(backup_pos);
|
||||
|
||||
if (data_next && DataEditingAddr + 1 < mem_size)
|
||||
{
|
||||
@@ -450,18 +523,28 @@ struct MemoryEditor
|
||||
ImGui::Separator();
|
||||
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
IM_UNUSED(mem_data);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
||||
if (GotoAddr != (size_t)-1)
|
||||
{
|
||||
if (GotoAddr < mem_size)
|
||||
{
|
||||
ImGui::BeginChild("##scrolling");
|
||||
ImGui::SetScrollY((GotoAddr / Cols) * ImGui::GetTextLineHeight() - avail_size.y * 0.5f);
|
||||
ImGui::EndChild();
|
||||
DataEditingAddr = DataPreviewAddr = GotoAddr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
GotoAddr = (size_t)-1;
|
||||
}
|
||||
|
||||
// Options menu
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("context");
|
||||
if (ImGui::BeginPopup("context"))
|
||||
const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y);
|
||||
//ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255));
|
||||
if (OptShowOptions)
|
||||
if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end))
|
||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("OptionsPopup");
|
||||
|
||||
if (ImGui::BeginPopup("OptionsPopup"))
|
||||
{
|
||||
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
|
||||
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
|
||||
@@ -473,6 +556,17 @@ struct MemoryEditor
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
IM_UNUSED(mem_data);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
||||
|
||||
// Options menu
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("OptionsPopup");
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
||||
@@ -488,18 +582,11 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
|
||||
if (GotoAddr != (size_t)-1)
|
||||
{
|
||||
if (GotoAddr < mem_size)
|
||||
{
|
||||
ImGui::BeginChild("##scrolling");
|
||||
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());
|
||||
ImGui::EndChild();
|
||||
DataEditingAddr = DataPreviewAddr = GotoAddr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
GotoAddr = (size_t)-1;
|
||||
}
|
||||
//if (MouseHovered)
|
||||
//{
|
||||
// ImGui::SameLine();
|
||||
// ImGui::Text("Hovered: %p", MouseHoveredAddr);
|
||||
//}
|
||||
}
|
||||
|
||||
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
||||
@@ -511,16 +598,21 @@ struct MemoryEditor
|
||||
ImGui::Text("Preview as:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
|
||||
static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double };
|
||||
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
||||
{
|
||||
for (int n = 0; n < ImGuiDataType_COUNT; n++)
|
||||
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
|
||||
PreviewDataType = (ImGuiDataType)n;
|
||||
for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++)
|
||||
{
|
||||
ImGuiDataType data_type = supported_data_types[n];
|
||||
if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))
|
||||
PreviewDataType = data_type;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
|
||||
ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0");
|
||||
|
||||
char buf[128] = "";
|
||||
float x = s.GlyphWidth * 6.0f;
|
||||
@@ -537,18 +629,19 @@ struct MemoryEditor
|
||||
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
}
|
||||
|
||||
// Utilities for Data Preview
|
||||
// Utilities for Data Preview (since we don't access imgui_internal.h)
|
||||
// FIXME: This technically depends on ImGuiDataType order.
|
||||
const char* DataTypeGetDesc(ImGuiDataType data_type) const
|
||||
{
|
||||
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs));
|
||||
return descs[data_type];
|
||||
}
|
||||
|
||||
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
||||
{
|
||||
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes));
|
||||
return sizes[data_type];
|
||||
}
|
||||
|
||||
@@ -561,18 +654,18 @@ struct MemoryEditor
|
||||
|
||||
bool IsBigEndian() const
|
||||
{
|
||||
uint16_t x = 1;
|
||||
ImU16 x = 1;
|
||||
char c[2];
|
||||
memcpy(c, &x, 2);
|
||||
return c[0] != 0;
|
||||
}
|
||||
|
||||
static void* EndianessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
{
|
||||
if (is_little_endian)
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
ImU8* dst = (ImU8*)_dst;
|
||||
ImU8* src = (ImU8*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
@@ -583,7 +676,7 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
|
||||
static void* EndianessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||
{
|
||||
if (is_little_endian)
|
||||
{
|
||||
@@ -591,23 +684,23 @@ struct MemoryEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
ImU8* dst = (ImU8*)_dst;
|
||||
ImU8* src = (ImU8*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
}
|
||||
}
|
||||
|
||||
void* EndianessCopy(void* dst, void* src, size_t size) const
|
||||
void* EndiannessCopy(void* dst, void* src, size_t size) const
|
||||
{
|
||||
static void* (*fp)(void*, void*, size_t, int) = NULL;
|
||||
if (fp == NULL)
|
||||
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianess);
|
||||
static void* (*fp)(void*, void*, size_t, int) = nullptr;
|
||||
if (fp == nullptr)
|
||||
fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianness);
|
||||
}
|
||||
|
||||
const char* FormatBinary(const uint8_t* buf, int width) const
|
||||
const char* FormatBinary(const ImU8* buf, int width) const
|
||||
{
|
||||
IM_ASSERT(width <= 64);
|
||||
size_t out_n = 0;
|
||||
@@ -627,19 +720,19 @@ struct MemoryEditor
|
||||
// [Internal]
|
||||
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
||||
{
|
||||
uint8_t buf[8];
|
||||
ImU8 buf[8];
|
||||
size_t elem_size = DataTypeGetSize(data_type);
|
||||
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
||||
if (ReadFn)
|
||||
for (int i = 0, n = (int)size; i < n; ++i)
|
||||
buf[i] = ReadFn(mem_data, addr + i);
|
||||
buf[i] = ReadFn(mem_data, addr + i, UserData);
|
||||
else
|
||||
memcpy(buf, mem_data + addr, size);
|
||||
|
||||
if (data_format == DataFormat_Bin)
|
||||
{
|
||||
uint8_t binbuf[8];
|
||||
EndianessCopy(binbuf, buf, size);
|
||||
ImU8 binbuf[8];
|
||||
EndiannessCopy(binbuf, buf, size);
|
||||
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
||||
return;
|
||||
}
|
||||
@@ -649,84 +742,85 @@ struct MemoryEditor
|
||||
{
|
||||
case ImGuiDataType_S8:
|
||||
{
|
||||
int8_t int8 = 0;
|
||||
EndianessCopy(&int8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", int8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
|
||||
ImS8 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U8:
|
||||
{
|
||||
uint8_t uint8 = 0;
|
||||
EndianessCopy(&uint8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", uint8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
|
||||
ImU8 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S16:
|
||||
{
|
||||
int16_t int16 = 0;
|
||||
EndianessCopy(&int16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", int16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
|
||||
ImS16 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U16:
|
||||
{
|
||||
uint16_t uint16 = 0;
|
||||
EndianessCopy(&uint16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", uint16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
|
||||
ImU16 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S32:
|
||||
{
|
||||
int32_t int32 = 0;
|
||||
EndianessCopy(&int32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", int32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
|
||||
ImS32 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U32:
|
||||
{
|
||||
uint32_t uint32 = 0;
|
||||
EndianessCopy(&uint32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", uint32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
|
||||
ImU32 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S64:
|
||||
{
|
||||
int64_t int64 = 0;
|
||||
EndianessCopy(&int64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
|
||||
ImS64 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U64:
|
||||
{
|
||||
uint64_t uint64 = 0;
|
||||
EndianessCopy(&uint64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)uint64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
|
||||
ImU64 data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Float:
|
||||
{
|
||||
float float32 = 0.0f;
|
||||
EndianessCopy(&float32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
|
||||
float data = 0.0f;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Double:
|
||||
{
|
||||
double float64 = 0.0;
|
||||
EndianessCopy(&float64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
|
||||
double data = 0.0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case ImGuiDataType_COUNT:
|
||||
break;
|
||||
} // Switch
|
||||
|
||||
+352
-176
File diff suppressed because it is too large
Load Diff
+1497
-961
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -315,7 +315,7 @@ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
// but we'll now add more waste for everything we've visited
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
@@ -470,7 +470,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
// stitched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
|
||||
+113
-55
@@ -5,7 +5,8 @@
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||
// - Added name to struct or it may be forward declared in our code.
|
||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
// - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||
// Grep for [DEAR IMGUI] to find some changes.
|
||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||
|
||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||
@@ -39,6 +40,7 @@
|
||||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||
// 1.14 (2021-07-11) page up/down, various fixes
|
||||
// 1.13 (2019-02-07) fix bug in undo size management
|
||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||
@@ -141,12 +143,14 @@
|
||||
// with previous char)
|
||||
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
||||
// (return type is int, -1 means not valid to insert)
|
||||
// (not supported if you want to use UTF-8, see below)
|
||||
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
|
||||
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
||||
// as manually wordwrapping for end-of-line positioning
|
||||
//
|
||||
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
|
||||
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
||||
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
||||
// returns number of characters actually inserted. [DEAR IMGUI]
|
||||
//
|
||||
// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
|
||||
//
|
||||
@@ -178,6 +182,13 @@
|
||||
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
|
||||
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
|
||||
//
|
||||
// To support UTF-8:
|
||||
//
|
||||
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
|
||||
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
|
||||
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
|
||||
// Instead, call stb_textedit_text() directly for text contents.
|
||||
//
|
||||
// Keyboard input must be encoded as a single integer value; e.g. a character code
|
||||
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
||||
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
||||
@@ -250,8 +261,10 @@
|
||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||
// transformed into text and stb_textedit_text() is automatically called.
|
||||
//
|
||||
// text: [DEAR IMGUI] added 2024-09
|
||||
// call this to text inputs sent to the textfield.
|
||||
// text: (added 2025)
|
||||
// call this to directly send text input the textfield, which is required
|
||||
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
|
||||
// cannot infer text length.
|
||||
//
|
||||
//
|
||||
// When rendering, you can read the cursor position and selection state from
|
||||
@@ -400,6 +413,16 @@ typedef struct
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -407,7 +430,7 @@ typedef struct
|
||||
//
|
||||
|
||||
// traverse the layout to locate the nearest character to a display position
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
@@ -417,6 +440,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
r.x0 = r.x1 = 0;
|
||||
r.ymin = r.ymax = 0;
|
||||
r.num_chars = 0;
|
||||
*out_side_on_line = 0;
|
||||
|
||||
// search rows to find one that straddles 'y'
|
||||
while (i < n) {
|
||||
@@ -436,7 +460,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
|
||||
// below all text, return 'after' last character
|
||||
if (i >= n)
|
||||
{
|
||||
*out_side_on_line = 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
// check if it's before the beginning of the line
|
||||
if (x < r.x0)
|
||||
@@ -449,6 +476,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||
if (x < prev_x+w) {
|
||||
*out_side_on_line = (k == 0) ? 0 : 1;
|
||||
if (x < prev_x+w/2)
|
||||
return k+i;
|
||||
else
|
||||
@@ -460,6 +488,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
}
|
||||
|
||||
// if the last character is a newline, return that. otherwise return 'after' the last character
|
||||
*out_side_on_line = 1;
|
||||
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
|
||||
return i+r.num_chars-1;
|
||||
else
|
||||
@@ -471,6 +500,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
||||
{
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
int side_on_line;
|
||||
if( state->single_line )
|
||||
{
|
||||
StbTexteditRow r;
|
||||
@@ -478,16 +508,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
||||
y = r.ymin;
|
||||
}
|
||||
|
||||
state->cursor = stb_text_locate_coord(str, x, y);
|
||||
state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||
state->select_start = state->cursor;
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||
}
|
||||
|
||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
int p = 0;
|
||||
int side_on_line;
|
||||
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
@@ -501,8 +533,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
||||
if (state->select_start == state->select_end)
|
||||
state->select_start = state->cursor;
|
||||
|
||||
p = stb_text_locate_coord(str, x, y);
|
||||
p = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||
state->cursor = state->select_end = p;
|
||||
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -552,6 +585,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||
if (n < i + r.num_chars)
|
||||
break;
|
||||
if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
|
||||
break;
|
||||
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
|
||||
break; // [DEAR IMGUI]
|
||||
prev_start = i;
|
||||
@@ -648,15 +683,33 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
|
||||
}
|
||||
}
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
|
||||
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
|
||||
#ifndef STB_TEXTEDIT_MOVELINESTART
|
||||
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||
{
|
||||
if (state->single_line)
|
||||
return 0;
|
||||
while (cursor > 0) {
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
|
||||
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
cursor = prev;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
|
||||
#ifndef STB_TEXTEDIT_MOVELINEEND
|
||||
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||
{
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->single_line)
|
||||
return n;
|
||||
while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
|
||||
return cursor;
|
||||
}
|
||||
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
|
||||
#endif
|
||||
|
||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||
@@ -668,9 +721,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
--c; // always move at least one character
|
||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||
--c;
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
|
||||
while (c >= 0 && !is_word_boundary(str, c))
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
|
||||
|
||||
if( c < 0 )
|
||||
c = 0;
|
||||
@@ -684,9 +737,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
|
||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||
++c; // always move at least one character
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
|
||||
while( c < len && !is_word_boundary( str, c ) )
|
||||
++c;
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
|
||||
|
||||
if( c > len )
|
||||
c = len;
|
||||
@@ -725,7 +778,8 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_delete_selection(str,state);
|
||||
// try to insert the characters
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {
|
||||
len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len);
|
||||
if (len) {
|
||||
stb_text_makeundo_insert(state, state->cursor, len);
|
||||
state->cursor += len;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -739,6 +793,7 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
||||
#define STB_TEXTEDIT_KEYTYPE int
|
||||
#endif
|
||||
|
||||
// API key: process text input
|
||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
@@ -749,14 +804,15 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||
if (text_len) {
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||
if (text_len) {
|
||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -771,6 +827,7 @@ retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||
// This is not suitable for UTF-8 support.
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||
@@ -911,15 +968,16 @@ retry:
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// going down while being on the last line shouldn't bring us to that line end
|
||||
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
//if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
// break;
|
||||
|
||||
// now find character position down a row
|
||||
state->cursor = start;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -927,10 +985,13 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
if (state->cursor == find.first_char + find.length)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||
state->has_preferred_x = 1;
|
||||
state->preferred_x = goal_x;
|
||||
|
||||
@@ -980,8 +1041,9 @@ retry:
|
||||
state->cursor = find.prev_first;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -989,10 +1051,15 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
if (state->cursor == find.first_char)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Right;
|
||||
else if (state->cursor == find.prev_first)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||
state->has_preferred_x = 1;
|
||||
state->preferred_x = goal_x;
|
||||
|
||||
@@ -1002,10 +1069,15 @@ retry:
|
||||
// go to previous line
|
||||
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
||||
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
|
||||
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
--prev_scan;
|
||||
while (prev_scan > 0)
|
||||
{
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
|
||||
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
prev_scan = prev;
|
||||
}
|
||||
find.first_char = find.prev_first;
|
||||
find.prev_first = prev_scan;
|
||||
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1079,10 +1151,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINESTART:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_move_to_first(state);
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -1090,13 +1159,9 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINEEND2:
|
||||
#endif
|
||||
case STB_TEXTEDIT_K_LINEEND: {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_move_to_first(state);
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
stb_textedit_move_to_last(str, state);
|
||||
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
}
|
||||
@@ -1107,10 +1172,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1119,13 +1181,9 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
|
||||
#endif
|
||||
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1300,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// check type of recorded action:
|
||||
if (u.insert_length) {
|
||||
// easy case: was a deletion, so we need to insert n characters
|
||||
STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
||||
u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
||||
s->undo_char_point -= u.insert_length;
|
||||
}
|
||||
|
||||
@@ -1351,7 +1409,7 @@ static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
|
||||
if (r.insert_length) {
|
||||
// easy case: need to insert n characters
|
||||
STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
||||
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
||||
s->redo_char_point += r.insert_length;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -886,7 +886,7 @@ STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, fl
|
||||
// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
|
||||
|
||||
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
|
||||
// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
|
||||
// the same as stbtt_GetCodepointBitmap, but you can specify a subpixel
|
||||
// shift for the character
|
||||
|
||||
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
|
||||
@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
|
||||
q2[0] = (float)x2;
|
||||
q2[1] = (float)y2;
|
||||
if (equal(q0,q1) || equal(q1,q2)) {
|
||||
x0 = (int)verts[i-1].x;
|
||||
y0 = (int)verts[i-1].y;
|
||||
x0 = (int)verts[i-1].x; //-V1048
|
||||
y0 = (int)verts[i-1].y; //-V1048
|
||||
x1 = (int)verts[i ].x;
|
||||
y1 = (int)verts[i ].y;
|
||||
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <cstdint>
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
@@ -16,15 +18,19 @@ namespace games::ccj {
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0xf8];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t data[0xe30];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_TBS {
|
||||
uint8_t data[0x4680];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB__NODEINFO {
|
||||
@@ -72,6 +78,12 @@ namespace games::ccj {
|
||||
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
||||
};
|
||||
|
||||
// checked with UJK-001-2024060400
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_TBS) == 0x4680);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value);
|
||||
|
||||
/*
|
||||
@@ -621,4 +633,6 @@ namespace games::ccj {
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::ccj {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -65,12 +65,20 @@ namespace games::ccj {
|
||||
libutils::load_library("libaio-iob2_video.dll");
|
||||
libutils::load_library("win10actlog.dll");
|
||||
|
||||
// we do this to prevent this unexpectedly being unloaded
|
||||
// which leads to D3D10Warp.dll_unloaded crash
|
||||
libutils::load_library("d3d10warp.dll");
|
||||
|
||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
||||
|
||||
#if SPICE64
|
||||
|
||||
// insert BI2X hooks
|
||||
bi2x_hook_init();
|
||||
|
||||
#endif
|
||||
|
||||
// insert trackball hooks
|
||||
trackball_hook_init();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "hooks/setupapihook.h"
|
||||
#include "hooks/sleephook.h"
|
||||
#include "hooks/input/dinput8/hook.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/fileutils.h"
|
||||
@@ -108,18 +109,32 @@ namespace games::ddr {
|
||||
log_info("ddr", "`{}` returned {}", cmd, result);
|
||||
} else {
|
||||
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
||||
deferredlogs::defer_error_messages({
|
||||
"DDR codec registration failure",
|
||||
fmt::format(" {} failed to register with error {}", filename.string(), result)});
|
||||
}
|
||||
|
||||
if (!contains_only_ascii(file.path().string())) {
|
||||
log_warning(
|
||||
"ddr",
|
||||
"BAD PATH ERROR\n\n\n"
|
||||
"!!! !!!\n"
|
||||
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
|
||||
"!!! this may cause the game to crash! !!!\n"
|
||||
"!!! !!!\n"
|
||||
);
|
||||
}
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, [file]() {
|
||||
if (!contains_only_ascii(file.path().string())) {
|
||||
log_warning(
|
||||
"ddr",
|
||||
"BAD PATH ERROR\n\n"
|
||||
"!!! !!!\n"
|
||||
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
|
||||
"!!! this may cause failures when loading codecs, and !!!\n"
|
||||
"!!! potentially leading the game to crash! !!!\n"
|
||||
"!!! !!!\n"
|
||||
);
|
||||
|
||||
deferredlogs::defer_error_messages({
|
||||
"path to DDR codecs contain non-Latin characters",
|
||||
" this may lead to movies not playing, white screen issue,",
|
||||
" game hanging, or potentially a crash",
|
||||
" move the game to a directory that does not contain non-Latin characters",
|
||||
" if you encountered any issues"});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+114
-110
@@ -41,136 +41,140 @@ std::vector<Light> &games::ddr::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
&lights,
|
||||
"P1 Foot Left",
|
||||
"P1 Foot Up",
|
||||
"P1 Foot Right",
|
||||
"P1 Foot Down",
|
||||
"P2 Foot Left",
|
||||
"P2 Foot Up",
|
||||
"P2 Foot Right",
|
||||
"P2 Foot Down",
|
||||
"Spot Red",
|
||||
"Spot Blue",
|
||||
"Top Spot Red",
|
||||
"Top Spot Blue",
|
||||
"P1 Halogen Upper",
|
||||
"P1 Halogen Lower",
|
||||
"P2 Halogen Upper",
|
||||
"P2 Halogen Lower",
|
||||
"P1 Button",
|
||||
"P2 Button",
|
||||
"Neon",
|
||||
"HD P1 Start",
|
||||
"HD P1 Menu Left-Right",
|
||||
"HD P1 Menu Up-Down",
|
||||
"HD P2 Start",
|
||||
"HD P2 Menu Left-Right",
|
||||
"HD P2 Menu Up-Down",
|
||||
"HD P1 Speaker F R",
|
||||
"HD P1 Speaker F G",
|
||||
"HD P1 Speaker F B",
|
||||
"HD P1 Speaker W R",
|
||||
"HD P1 Speaker W G",
|
||||
"HD P1 Speaker W B",
|
||||
"HD P2 Speaker F R",
|
||||
"HD P2 Speaker F G",
|
||||
"HD P2 Speaker F B",
|
||||
"HD P2 Speaker W R",
|
||||
"HD P2 Speaker W G",
|
||||
"HD P2 Speaker W B",
|
||||
GameAPI::Lights::sortLightsWithCategory(
|
||||
&lights,
|
||||
{
|
||||
{"General", "P1 Foot Left"},
|
||||
{"General", "P1 Foot Up"},
|
||||
{"General", "P1 Foot Right"},
|
||||
{"General", "P1 Foot Down"},
|
||||
{"General", "P2 Foot Left"},
|
||||
{"General", "P2 Foot Up"},
|
||||
{"General", "P2 Foot Right"},
|
||||
{"General", "P2 Foot Down"},
|
||||
|
||||
"WHITE Speaker Top R",
|
||||
"WHITE Speaker Top G",
|
||||
"WHITE Speaker Top B",
|
||||
"WHITE Speaker Bottom R",
|
||||
"WHITE Speaker Bottom G",
|
||||
"WHITE Speaker Bottom B",
|
||||
"WHITE Woofer R",
|
||||
"WHITE Woofer G",
|
||||
"WHITE Woofer B",
|
||||
{"SD", "Spot Red"},
|
||||
{"SD", "Spot Blue"},
|
||||
{"SD", "Top Spot Red"},
|
||||
{"SD", "Top Spot Blue"},
|
||||
{"SD", "P1 Halogen Upper"},
|
||||
{"SD", "P1 Halogen Lower"},
|
||||
{"SD", "P2 Halogen Upper"},
|
||||
{"SD", "P2 Halogen Lower"},
|
||||
{"SD", "P1 Button"},
|
||||
{"SD", "P2 Button"},
|
||||
{"SD", "Neon"},
|
||||
|
||||
"GOLD P1 Menu Start",
|
||||
"GOLD P1 Menu Up",
|
||||
"GOLD P1 Menu Down",
|
||||
"GOLD P1 Menu Left",
|
||||
"GOLD P1 Menu Right",
|
||||
{"HD", "HD P1 Start"},
|
||||
{"HD", "HD P1 Menu Left-Right"},
|
||||
{"HD", "HD P1 Menu Up-Down"},
|
||||
{"HD", "HD P2 Start"},
|
||||
{"HD", "HD P2 Menu Left-Right"},
|
||||
{"HD", "HD P2 Menu Up-Down"},
|
||||
{"HD", "HD P1 Speaker F R"},
|
||||
{"HD", "HD P1 Speaker F G"},
|
||||
{"HD", "HD P1 Speaker F B"},
|
||||
{"HD", "HD P1 Speaker W R"},
|
||||
{"HD", "HD P1 Speaker W G"},
|
||||
{"HD", "HD P1 Speaker W B"},
|
||||
{"HD", "HD P2 Speaker F R"},
|
||||
{"HD", "HD P2 Speaker F G"},
|
||||
{"HD", "HD P2 Speaker F B"},
|
||||
{"HD", "HD P2 Speaker W R"},
|
||||
{"HD", "HD P2 Speaker W G"},
|
||||
{"HD", "HD P2 Speaker W B"},
|
||||
|
||||
"GOLD P2 Menu Start",
|
||||
"GOLD P2 Menu Up",
|
||||
"GOLD P2 Menu Down",
|
||||
"GOLD P2 Menu Left",
|
||||
"GOLD P2 Menu Right",
|
||||
{"WHITE", "WHITE Speaker Top R"},
|
||||
{"WHITE", "WHITE Speaker Top G"},
|
||||
{"WHITE", "WHITE Speaker Top B"},
|
||||
{"WHITE", "WHITE Speaker Bottom R"},
|
||||
{"WHITE", "WHITE Speaker Bottom G"},
|
||||
{"WHITE", "WHITE Speaker Bottom B"},
|
||||
{"WHITE", "WHITE Woofer R"},
|
||||
{"WHITE", "WHITE Woofer G"},
|
||||
{"WHITE", "WHITE Woofer B"},
|
||||
|
||||
"GOLD Title Panel Left",
|
||||
"GOLD Title Panel Center",
|
||||
"GOLD Title Panel Right",
|
||||
{"GOLD", "GOLD P1 Menu Start"},
|
||||
{"GOLD", "GOLD P1 Menu Up"},
|
||||
{"GOLD", "GOLD P1 Menu Down"},
|
||||
{"GOLD", "GOLD P1 Menu Left"},
|
||||
{"GOLD", "GOLD P1 Menu Right"},
|
||||
|
||||
"GOLD P1 Woofer Corner",
|
||||
"GOLD P2 Woofer Corner",
|
||||
{"GOLD", "GOLD P2 Menu Start"},
|
||||
{"GOLD", "GOLD P2 Menu Up"},
|
||||
{"GOLD", "GOLD P2 Menu Down"},
|
||||
{"GOLD", "GOLD P2 Menu Left"},
|
||||
{"GOLD", "GOLD P2 Menu Right"},
|
||||
|
||||
"GOLD P1 Card Unit R",
|
||||
"GOLD P1 Card Unit G",
|
||||
"GOLD P1 Card Unit B",
|
||||
{"GOLD", "GOLD Title Panel Left"},
|
||||
{"GOLD", "GOLD Title Panel Center"},
|
||||
{"GOLD", "GOLD Title Panel Right"},
|
||||
|
||||
"GOLD P2 Card Unit R",
|
||||
"GOLD P2 Card Unit G",
|
||||
"GOLD P2 Card Unit B",
|
||||
{"GOLD", "GOLD P1 Woofer Corner"},
|
||||
{"GOLD", "GOLD P2 Woofer Corner"},
|
||||
|
||||
"GOLD Top Panel Avg R",
|
||||
"GOLD Top Panel Avg G",
|
||||
"GOLD Top Panel Avg B",
|
||||
{"GOLD", "GOLD P1 Card Unit R"},
|
||||
{"GOLD", "GOLD P1 Card Unit G"},
|
||||
{"GOLD", "GOLD P1 Card Unit B"},
|
||||
|
||||
"GOLD Monitor Side Left Avg R",
|
||||
"GOLD Monitor Side Left Avg G",
|
||||
"GOLD Monitor Side Left Avg B",
|
||||
{"GOLD", "GOLD P2 Card Unit R"},
|
||||
{"GOLD", "GOLD P2 Card Unit G"},
|
||||
{"GOLD", "GOLD P2 Card Unit B"},
|
||||
|
||||
"GOLD Monitor Side Right Avg R",
|
||||
"GOLD Monitor Side Right Avg G",
|
||||
"GOLD Monitor Side Right Avg B",
|
||||
{"GOLD", "GOLD Top Panel Avg R"},
|
||||
{"GOLD", "GOLD Top Panel Avg G"},
|
||||
{"GOLD", "GOLD Top Panel Avg B"},
|
||||
|
||||
"GOLD P1 Foot Up Avg R",
|
||||
"GOLD P1 Foot Up Avg G",
|
||||
"GOLD P1 Foot Up Avg B",
|
||||
{"GOLD", "GOLD Monitor Side Left Avg R"},
|
||||
{"GOLD", "GOLD Monitor Side Left Avg G"},
|
||||
{"GOLD", "GOLD Monitor Side Left Avg B"},
|
||||
|
||||
"GOLD P1 Foot Down Avg R",
|
||||
"GOLD P1 Foot Down Avg G",
|
||||
"GOLD P1 Foot Down Avg B",
|
||||
{"GOLD", "GOLD Monitor Side Right Avg R"},
|
||||
{"GOLD", "GOLD Monitor Side Right Avg G"},
|
||||
{"GOLD", "GOLD Monitor Side Right Avg B"},
|
||||
|
||||
"GOLD P1 Foot Left Avg R",
|
||||
"GOLD P1 Foot Left Avg G",
|
||||
"GOLD P1 Foot Left Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Up Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Up Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Up Avg B"},
|
||||
|
||||
"GOLD P1 Foot Right Avg R",
|
||||
"GOLD P1 Foot Right Avg G",
|
||||
"GOLD P1 Foot Right Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Down Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Down Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Down Avg B"},
|
||||
|
||||
"GOLD P2 Foot Up Avg R",
|
||||
"GOLD P2 Foot Up Avg G",
|
||||
"GOLD P2 Foot Up Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Left Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Left Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Left Avg B"},
|
||||
|
||||
"GOLD P2 Foot Down Avg R",
|
||||
"GOLD P2 Foot Down Avg G",
|
||||
"GOLD P2 Foot Down Avg B",
|
||||
{"GOLD", "GOLD P1 Foot Right Avg R"},
|
||||
{"GOLD", "GOLD P1 Foot Right Avg G"},
|
||||
{"GOLD", "GOLD P1 Foot Right Avg B"},
|
||||
|
||||
"GOLD P2 Foot Left Avg R",
|
||||
"GOLD P2 Foot Left Avg G",
|
||||
"GOLD P2 Foot Left Avg B",
|
||||
{"GOLD", "GOLD P2 Foot Up Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Up Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Up Avg B"},
|
||||
|
||||
"GOLD P2 Foot Right Avg R",
|
||||
"GOLD P2 Foot Right Avg G",
|
||||
"GOLD P2 Foot Right Avg B",
|
||||
{"GOLD", "GOLD P2 Foot Down Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Down Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Down Avg B"},
|
||||
|
||||
"GOLD P1 Stage Corner Up-Left",
|
||||
"GOLD P1 Stage Corner Up-Right",
|
||||
"GOLD P1 Stage Corner Down-Left",
|
||||
"GOLD P1 Stage Corner Down-Right",
|
||||
{"GOLD", "GOLD P2 Foot Left Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Left Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Left Avg B"},
|
||||
|
||||
"GOLD P2 Stage Corner Up-Left",
|
||||
"GOLD P2 Stage Corner Up-Right",
|
||||
"GOLD P2 Stage Corner Down-Left",
|
||||
"GOLD P2 Stage Corner Down-Right"
|
||||
{"GOLD", "GOLD P2 Foot Right Avg R"},
|
||||
{"GOLD", "GOLD P2 Foot Right Avg G"},
|
||||
{"GOLD", "GOLD P2 Foot Right Avg B"},
|
||||
|
||||
{"GOLD", "GOLD P1 Stage Corner Up-Left"},
|
||||
{"GOLD", "GOLD P1 Stage Corner Up-Right"},
|
||||
{"GOLD", "GOLD P1 Stage Corner Down-Left"},
|
||||
{"GOLD", "GOLD P1 Stage Corner Down-Right"},
|
||||
|
||||
{"GOLD", "GOLD P2 Stage Corner Up-Left"},
|
||||
{"GOLD", "GOLD P2 Stage Corner Up-Right"},
|
||||
{"GOLD", "GOLD P2 Stage Corner Down-Left"},
|
||||
{"GOLD", "GOLD P2 Stage Corner Down-Right"}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ int games::ddr::DDRP3IOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWri
|
||||
}
|
||||
|
||||
// use both sat spots for a neon pulse
|
||||
float value_neon = (light_bits & hd_mapping_bits[0] && hd_mapping_bits[1]) ? 1.f : 0.f;
|
||||
float value_neon = (light_bits & hd_mapping_bits[0] && light_bits & hd_mapping_bits[1]) ? 1.f : 0.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
||||
}
|
||||
|
||||
|
||||
@@ -253,28 +253,25 @@ namespace games::ddr {
|
||||
if(((dwIoControlCode>>16)&0xFFFF) == FILE_DEVICE_UNKNOWN) {
|
||||
switch((dwIoControlCode>>2)&0x3FFF) {
|
||||
case P4IO_IOCTL_GET_INPUTS: {
|
||||
|
||||
// Prevents this function from being called at its normal 2000-2500 kHz cadence and overloading the CPU, instead reduces it to 250 Hz
|
||||
Sleep(4);
|
||||
|
||||
memset(lpOutBuffer, 0, 16);
|
||||
auto controls = (uint32_t*) lpOutBuffer;
|
||||
|
||||
const std::pair<size_t, uint8_t> buttonMapping[] =
|
||||
{
|
||||
// Panel input comes from MDXF Control Status Buffer functions
|
||||
{Buttons::SERVICE, 25}, //Good
|
||||
{Buttons::TEST, 28}, //Good
|
||||
{Buttons::COIN_MECH, 24}, //Good
|
||||
{Buttons::P1_START, 0}, //Good
|
||||
{Buttons::P1_PANEL_UP, 5}, //Good
|
||||
{Buttons::P1_PANEL_DOWN, 6}, //Good
|
||||
{Buttons::P1_PANEL_LEFT, 7}, //Goood
|
||||
{Buttons::P1_PANEL_RIGHT, 16}, //Good
|
||||
{Buttons::P1_MENU_UP, 1}, //Good
|
||||
{Buttons::P1_MENU_DOWN, 2}, //Good
|
||||
{Buttons::P1_MENU_LEFT, 3}, //Good
|
||||
{Buttons::P1_MENU_RIGHT, 4}, //Good
|
||||
{Buttons::P2_START, 8}, //Good
|
||||
{Buttons::P2_PANEL_UP, 13}, //Good
|
||||
{Buttons::P2_PANEL_DOWN, 14}, //Good
|
||||
{Buttons::P2_PANEL_LEFT, 15}, //Good
|
||||
{Buttons::P2_PANEL_RIGHT, 20}, //Good
|
||||
{Buttons::P2_MENU_UP, 9}, //Good
|
||||
{Buttons::P2_MENU_DOWN, 10}, //Good
|
||||
{Buttons::P2_MENU_LEFT, 11}, //Good
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/memutils.h"
|
||||
#include "rgb_cam.h"
|
||||
|
||||
#pragma pack(push)
|
||||
|
||||
@@ -150,6 +151,7 @@ namespace games::drs {
|
||||
char DRS_TAPELED[DRS_TAPELED_COLS_TOTAL][3] {};
|
||||
bool DISABLE_TOUCH = false;
|
||||
bool TRANSPOSE_TOUCH = false;
|
||||
bool RGB_CAMERA_HOOK = false;
|
||||
|
||||
std::vector<TouchEvent> TOUCH_EVENTS;
|
||||
|
||||
@@ -313,6 +315,10 @@ namespace games::drs {
|
||||
} else {
|
||||
log_info("drs", "no native input method detected");
|
||||
}
|
||||
|
||||
if (RGB_CAMERA_HOOK) {
|
||||
init_rgb_camera_hook();
|
||||
}
|
||||
}
|
||||
|
||||
void DRSGame::detach() {
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace games::drs {
|
||||
extern std::vector<TouchEvent> TOUCH_EVENTS;
|
||||
extern bool DISABLE_TOUCH;
|
||||
extern bool TRANSPOSE_TOUCH;
|
||||
extern bool RGB_CAMERA_HOOK;
|
||||
|
||||
void fire_touches(drs_touch_t *events, size_t event_count);
|
||||
void start_touch();
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "games/drs/rgb_cam.h"
|
||||
|
||||
// set version to Windows 7 to enable Media Foundation functions
|
||||
#ifdef _WIN32_WINNT
|
||||
#undef _WIN32_WINNT
|
||||
#endif
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
// turn on C-style COM objects
|
||||
#define CINTERFACE
|
||||
|
||||
#include <mfobjects.h>
|
||||
#include <mfidl.h>
|
||||
#include <string>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "hooks/cfgmgr32hook.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
|
||||
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
|
||||
|
||||
namespace games::drs {
|
||||
|
||||
/*
|
||||
* Camera related stuff
|
||||
*/
|
||||
static std::wstring CAMERA0_ID;
|
||||
|
||||
static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue,
|
||||
UINT32 *pcchLength)
|
||||
{
|
||||
// call the original
|
||||
HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength);
|
||||
|
||||
// log the cam name
|
||||
log_misc("drs::rgbcam", "obtained {}", ws2s(*ppwszValue));
|
||||
|
||||
// try first camera
|
||||
wchar_t *pwc = nullptr;
|
||||
if (CAMERA0_ID.length() == 23) {
|
||||
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
|
||||
}
|
||||
|
||||
// check if camera could be identified
|
||||
if (pwc) {
|
||||
|
||||
// fake the USB IDs
|
||||
pwc[4] = L'2';
|
||||
pwc[5] = L'8';
|
||||
pwc[6] = L'8';
|
||||
pwc[7] = L'c';
|
||||
|
||||
pwc[13] = L'0';
|
||||
pwc[14] = L'0';
|
||||
pwc[15] = L'0';
|
||||
pwc[16] = L'2';
|
||||
|
||||
pwc[21] = L'0';
|
||||
pwc[22] = L'0';
|
||||
|
||||
log_misc("drs::rgbcam", "replaced {}", ws2s(*ppwszValue));
|
||||
}
|
||||
|
||||
// return original result
|
||||
return result;
|
||||
}
|
||||
|
||||
static void hook_camera(std::wstring camera_id, std::string camera_instance) {
|
||||
// don't hook if camera 0 is already hooked
|
||||
if (CAMERA0_ID.length() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save the camera ID
|
||||
CAMERA0_ID = camera_id;
|
||||
|
||||
// cfgmgr hook
|
||||
CFGMGR32_HOOK_SETTING camera_setting;
|
||||
camera_setting.device_instance = 0xDEADBEEF;
|
||||
camera_setting.parent_instance = ~camera_setting.device_instance;
|
||||
camera_setting.device_id = "USB\\VEN_8086&DEV_8C2D";
|
||||
camera_setting.device_node_id = "USB\\VID_288C&PID_0002&MI_00\\?&????????&?&????";
|
||||
if (camera_instance.length() == 17) {
|
||||
for (int i = 0; i < 17; i++) {
|
||||
camera_setting.device_node_id[28 + i] = camera_instance[i];
|
||||
}
|
||||
}
|
||||
cfgmgr32hook_add(camera_setting);
|
||||
|
||||
log_info("drs::rgbcam", "hooked camera @ {}", ws2s(camera_id));
|
||||
}
|
||||
|
||||
static void hook_camera_vtable(IMFActivate *camera) {
|
||||
|
||||
// hook allocated string method for camera identification
|
||||
memutils::VProtectGuard camera_guard(camera->lpVtbl);
|
||||
camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI MFEnumDeviceSources_hook(IMFAttributes *pAttributes, IMFActivate ***pppSourceActivate,
|
||||
UINT32 *pcSourceActivate) {
|
||||
|
||||
// call original function
|
||||
HRESULT result_orig = MFEnumDeviceSources_orig(pAttributes, pppSourceActivate, pcSourceActivate);
|
||||
|
||||
// check for capture devices
|
||||
if (FAILED(result_orig) || !*pcSourceActivate) {
|
||||
return result_orig;
|
||||
}
|
||||
|
||||
// get camera
|
||||
IMFActivate *camera = **pppSourceActivate;
|
||||
|
||||
// save original method for later use
|
||||
if (GetAllocatedString_orig == nullptr) {
|
||||
GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString;
|
||||
}
|
||||
|
||||
// hook allocated string method for camera identification
|
||||
hook_camera_vtable(camera);
|
||||
|
||||
// get camera link
|
||||
LPWSTR camera_link_lpwstr;
|
||||
UINT32 camera_link_length;
|
||||
if (SUCCEEDED(GetAllocatedString_orig(
|
||||
camera,
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
||||
&camera_link_lpwstr,
|
||||
&camera_link_length))) {
|
||||
|
||||
// cut name to make ID
|
||||
std::wstring camera_link_ws = std::wstring(camera_link_lpwstr);
|
||||
std::wstring camera_id = camera_link_ws.substr(8, 23);
|
||||
|
||||
log_info("drs::rgbcam", "found video capture device: {}", ws2s(camera_link_ws));
|
||||
|
||||
// only support cameras that start with \\?\usb
|
||||
if (!string_begins_with(camera_link_ws, L"\\\\?\\usb")) {
|
||||
return result_orig;
|
||||
}
|
||||
|
||||
// get camera instance
|
||||
std::string camera_link = ws2s(camera_link_ws);
|
||||
std::string camera_instance = camera_link.substr(32, 17);
|
||||
|
||||
// hook the camera
|
||||
hook_camera(camera_id, camera_instance);
|
||||
} else {
|
||||
log_warning("drs::rgbcam", "failed to open camera");
|
||||
}
|
||||
|
||||
// return result
|
||||
return result_orig;
|
||||
}
|
||||
|
||||
void init_rgb_camera_hook() {
|
||||
log_info("drs::rgbcam", "initializing camera hook");
|
||||
|
||||
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
||||
|
||||
// camera media framework hook
|
||||
MFEnumDeviceSources_orig = detour::iat_try(
|
||||
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace games::drs {
|
||||
void init_rgb_camera_hook();
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/io.h"
|
||||
#include "io.h"
|
||||
#include "util/tapeled.h"
|
||||
#include <typeinfo>
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
// /*
|
||||
// * class definitions
|
||||
// */
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t data[0xe18];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB5 {
|
||||
uint8_t data[0xb10];
|
||||
};
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0x100];
|
||||
};
|
||||
|
||||
struct AIO_IOB5_Y32D {
|
||||
uint8_t data[0x2e8];
|
||||
};
|
||||
|
||||
// struct AIO_IOB5_Y33I {
|
||||
// };
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
uint8_t data[0x4570];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB__NODEINFO {
|
||||
uint8_t data[0xA8];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_SCI_COMM_T {
|
||||
uint64_t unk_0;
|
||||
struct AIO_SCI_COMM *aioSciComm;
|
||||
};
|
||||
|
||||
struct AIO_COMM_STATUS {
|
||||
uint64_t unk[5];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__INPUT {
|
||||
uint8_t DevIoCounter;
|
||||
uint8_t bExIoAErr;
|
||||
uint8_t bExIoBErr;
|
||||
uint8_t bPcPowerOn;
|
||||
uint8_t bPcPowerCheck;
|
||||
uint8_t bCoin1Jam;
|
||||
uint8_t bCoin2Jam;
|
||||
uint8_t bCoin3Jam;
|
||||
uint8_t bCoin4Jam;
|
||||
uint8_t Coin1Count;
|
||||
uint8_t Coin2Count;
|
||||
uint8_t Coin3Count;
|
||||
uint8_t Coin4Count;
|
||||
uint16_t AnalogCh1;
|
||||
uint16_t AnalogCh2;
|
||||
uint16_t AnalogCh3;
|
||||
uint16_t AnalogCh4;
|
||||
uint8_t CN8_8;
|
||||
uint8_t CN8_9;
|
||||
uint8_t CN8_10;
|
||||
uint8_t CN9_8;
|
||||
uint8_t CN9_9;
|
||||
uint8_t CN9_10;
|
||||
uint8_t CN11_11;
|
||||
uint8_t CN11_12;
|
||||
uint8_t CN11_13;
|
||||
uint8_t CN11_14;
|
||||
uint8_t CN11_15;
|
||||
uint8_t CN11_16;
|
||||
uint8_t CN11_17;
|
||||
uint8_t CN11_18;
|
||||
uint8_t CN11_19;
|
||||
uint8_t CN11_20;
|
||||
uint8_t CN12_11;
|
||||
uint8_t CN12_12;
|
||||
uint8_t CN12_13;
|
||||
uint8_t CN12_14;
|
||||
uint8_t CN12_15;
|
||||
uint8_t CN12_16;
|
||||
uint8_t CN12_17;
|
||||
uint8_t CN12_18;
|
||||
uint8_t CN12_19;
|
||||
uint8_t CN12_20;
|
||||
uint8_t CN12_21;
|
||||
uint8_t CN12_22;
|
||||
uint8_t CN12_23;
|
||||
uint8_t CN12_24;
|
||||
uint8_t CN15_3;
|
||||
uint8_t CN15_4;
|
||||
uint8_t CN15_5;
|
||||
uint8_t CN15_6;
|
||||
uint8_t CN15_7;
|
||||
uint8_t CN15_8;
|
||||
uint8_t CN15_9;
|
||||
uint8_t CN15_10;
|
||||
uint8_t CN15_11;
|
||||
uint8_t CN15_12;
|
||||
uint8_t CN15_13;
|
||||
uint8_t CN15_14;
|
||||
uint8_t CN15_15;
|
||||
uint8_t CN15_16;
|
||||
uint8_t CN15_17;
|
||||
uint8_t CN15_18;
|
||||
uint8_t CN15_19;
|
||||
uint8_t CN15_20;
|
||||
uint8_t CN19_8;
|
||||
uint8_t CN19_9;
|
||||
uint8_t CN19_10;
|
||||
uint8_t CN19_11;
|
||||
uint8_t CN19_12;
|
||||
uint8_t CN19_13;
|
||||
uint8_t CN19_14;
|
||||
uint8_t CN19_15;
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__INPUTDATA {
|
||||
uint8_t data[247];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__OUTPUTDATA {
|
||||
uint8_t data[48];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__ICNPIN {
|
||||
uint16_t Ain[4];
|
||||
uint64_t CnPin;
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__DEVSTATUS {
|
||||
uint8_t InputCounter;
|
||||
uint8_t OutputCounter;
|
||||
uint8_t IoResetCounter;
|
||||
uint8_t TapeLedCounter;
|
||||
uint8_t TapeLedRate[8];
|
||||
AIO_IOB2_BI2X_AC1__INPUT Input;
|
||||
AIO_IOB2_BI2X_AC1__INPUTDATA InputData;
|
||||
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
||||
AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20];
|
||||
};
|
||||
|
||||
struct AIO_IOB5_Y32D__DEVSTATUS {
|
||||
uint8_t unk[0xB1];
|
||||
};
|
||||
|
||||
// verified with M32-007-2025092400
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe18);
|
||||
static_assert(sizeof(AIO_NMGR_IOB5) == 0xb10);
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0x100);
|
||||
static_assert(sizeof(AIO_IOB5_Y32D) == 0x2e8);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4570);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
typedef AIO_IOB2_BI2X_AC1 *(__fastcall *aioIob2Bi2xAC1_Create_t)(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId,
|
||||
AIO_IOB2_BI2X_AC1__SETTING *i_Setting);
|
||||
typedef void(__fastcall *aioIob2Bi2xAC1_GetDeviceStatus_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl,
|
||||
AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus);
|
||||
typedef void(__fastcall *aioIob2Bi2xAC1_SetWatchDogTimer_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count);
|
||||
typedef void(__fastcall *aioIob2Bi2xAC1_AddCounter_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter,
|
||||
uint32_t i_Count);
|
||||
typedef void(__fastcall *aioIob2Bi2xAC1_SetOutputData_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin,
|
||||
uint8_t i_Data);
|
||||
typedef void(__fastcall *aioIob2Bi2xAC1_SetTapeLedDataPart_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
|
||||
uint32_t i_Offset, uint8_t *i_pData,
|
||||
uint32_t i_cntTapeLed, bool i_bReverse);
|
||||
typedef void(__fastcall *aioIob2Bi2x_SetTapeLedDataLimit_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Channel,
|
||||
uint8_t i_Scale, uint8_t i_Limit);
|
||||
typedef AIO_IOB2_BI2X_WRFIRM *(__fastcall *aioIob2Bi2x_CreateWriteFirmContext_t)(uint32_t i_SerialNumber,
|
||||
uint32_t i_bfIob);
|
||||
typedef void(__fastcall *aioIob2Bi2x_DestroyWriteFirmContext_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm);
|
||||
typedef int32_t(__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm);
|
||||
typedef bool(__fastcall *aioIob2Bi2x_WriteFirmIsCompleted_t)(int32_t i_State);
|
||||
|
||||
// libaio-iob.dll
|
||||
typedef AIO_NMGR_IOB2 *(__fastcall *aioNMgrIob2_Create_t)(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode);
|
||||
typedef void(__fastcall *aioNMgrIob_BeginManage_t)(AIO_NMGR_IOB2 *i_pNodeMgr);
|
||||
typedef void(__fastcall *aioNCtlIob_GetNodeInfo_t)(AIO_IOB5_Y32D *i_pNodeCtl,
|
||||
AIO_NMGR_IOB__NODEINFO *o_NodeInfo);
|
||||
|
||||
// libaio.dll
|
||||
typedef AIO_SCI_COMM_T *(__fastcall *aioSciComm_Open_t)(AIO_SCI_COMM_T *unk);
|
||||
typedef void(__fastcall *aioSci_Destroy_t)(AIO_SCI_COMM *i_pNodeMgr);
|
||||
typedef void(__fastcall *aioSci_GetCommStatus_t)(AIO_SCI_COMM *i_pNodeMgr, AIO_COMM_STATUS *i_Status);
|
||||
typedef void(__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB5 *i_pNodeMgr);
|
||||
typedef int32_t(__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB5 *i_pNodeMgr);
|
||||
typedef bool(__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State);
|
||||
typedef bool(__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State);
|
||||
typedef void(__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB5_Y32D *i_pNodeCtl);
|
||||
typedef int32_t(__fastcall *aioNodeCtl_GetState_t)(AIO_IOB5_Y32D *i_pNodeCtl);
|
||||
typedef bool(__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State);
|
||||
typedef bool(__fastcall *aioNodeCtl_IsError_t)(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State);
|
||||
typedef void(__fastcall *aioNodeCtl_UpdateDevicesStatus_t)();
|
||||
|
||||
// libaio-iob5.dll
|
||||
typedef AIO_NMGR_IOB5 *(__fastcall *aioNMgrIob5_Create_t)(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode);
|
||||
|
||||
// libaio-iob5_y32.dll
|
||||
typedef AIO_IOB5_Y32D *(__fastcall *aioIob5Y32d_Create_t)(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode);
|
||||
// typedef AIO_IOB5_Y33I *(__fastcall *aioIob5Y33i_Create_t)(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode);
|
||||
|
||||
typedef void(__fastcall *aioIob5Y32d_GetDeviceStatus_t)(AIO_IOB5_Y32D *i_pNodeCtl,
|
||||
AIO_IOB5_Y32D__DEVSTATUS *o_DevStatus);
|
||||
// typedef void(__fastcall *aioIob5Y33i_GetDeviceStatus_t)(AIO_IOB5_Y33I *i_pNodeCtl,
|
||||
// AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus);
|
||||
|
||||
/*
|
||||
* function pointers
|
||||
*/
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
static aioIob2Bi2xAC1_Create_t aioIob2Bi2xAC1_Create_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_GetDeviceStatus_t aioIob2Bi2xAC1_GetDeviceStatus_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_SetWatchDogTimer_t aioIob2Bi2xAC1_SetWatchDogTimer_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_AddCounter_t aioIob2Bi2xAC1_AddCounter_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_SetOutputData_t aioIob2Bi2xAC1_SetOutputData_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_SetTapeLedDataPart_t aioIob2Bi2xAC1_SetTapeLedDataPart_orig = nullptr;
|
||||
static aioIob2Bi2x_SetTapeLedDataLimit_t aioIob2Bi2x_SetTapeLedDataLimit_orig = nullptr;
|
||||
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
|
||||
static aioIob2Bi2x_DestroyWriteFirmContext_t aioIob2Bi2x_DestroyWriteFirmContext_orig = nullptr;
|
||||
static aioIob2Bi2x_WriteFirmGetState_t aioIob2Bi2x_WriteFirmGetState_orig = nullptr;
|
||||
static aioIob2Bi2x_WriteFirmIsCompleted_t aioIob2Bi2x_WriteFirmIsCompleted_orig = nullptr;
|
||||
|
||||
// libaio.dll
|
||||
static aioSciComm_Open_t aioSciComm_Open_orig = nullptr;
|
||||
static aioSci_Destroy_t aioSci_Destroy_orig = nullptr;
|
||||
static aioSci_GetCommStatus_t aioSci_GetCommStatus_orig = nullptr;
|
||||
static aioNodeMgr_Destroy_t aioNodeMgr_Destroy_orig = nullptr;
|
||||
static aioNodeMgr_GetState_t aioNodeMgr_GetState_orig = nullptr;
|
||||
static aioNodeMgr_IsReady_t aioNodeMgr_IsReady_orig = nullptr;
|
||||
static aioNodeMgr_IsError_t aioNodeMgr_IsError_orig = nullptr;
|
||||
static aioNodeCtl_Destroy_t aioNodeCtl_Destroy_orig = nullptr;
|
||||
static aioNodeCtl_GetState_t aioNodeCtl_GetState_orig = nullptr;
|
||||
static aioNodeCtl_IsReady_t aioNodeCtl_IsReady_orig = nullptr;
|
||||
static aioNodeCtl_IsError_t aioNodeCtl_IsError_orig = nullptr;
|
||||
static aioNodeCtl_UpdateDevicesStatus_t aioNodeCtl_UpdateDevicesStatus_orig = nullptr;
|
||||
|
||||
// libaio-iob5.dll
|
||||
static aioNMgrIob5_Create_t aioNMgrIob5_Create_orig = nullptr;
|
||||
|
||||
// libaio-iob.dll
|
||||
static aioNMgrIob2_Create_t aioNMgrIob2_Create_orig = nullptr;
|
||||
static aioNMgrIob_BeginManage_t aioNMgrIob_BeginManage_orig = nullptr;
|
||||
static aioNCtlIob_GetNodeInfo_t aioNCtlIob_GetNodeInfo_orig = nullptr;
|
||||
|
||||
// libaio-iob5_y32.dll
|
||||
static aioIob5Y32d_Create_t aioIob5Y32d_Create_orig = nullptr;
|
||||
static aioIob5Y32d_GetDeviceStatus_t aioIob5Y32d_GetDeviceStatus_orig = nullptr;
|
||||
|
||||
/*
|
||||
* variables
|
||||
*/
|
||||
static AIO_IOB2_BI2X_AC1 *aioIob2Bi2xAc1;
|
||||
static AIO_NMGR_IOB2 *aioNmgrIob2;
|
||||
static AIO_NMGR_IOB5 *aioNmgrIob5;
|
||||
static AIO_IOB5_Y32D *aioIob5Y32d;
|
||||
static AIO_SCI_COMM *aioSciComm;
|
||||
static AIO_IOB2_BI2X_WRFIRM *aioIob2Bi2xWrfirm;
|
||||
|
||||
/*
|
||||
* implementations
|
||||
*/
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
|
||||
static AIO_IOB2_BI2X_AC1 *__fastcall aioIob2Bi2xAC1_Create(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId,
|
||||
AIO_IOB2_BI2X_AC1__SETTING *i_Setting) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob2) {
|
||||
aioIob2Bi2xAc1 = new AIO_IOB2_BI2X_AC1;
|
||||
return aioIob2Bi2xAc1;
|
||||
} else {
|
||||
return aioIob2Bi2xAC1_Create_orig(i_pNodeMgr, i_DevId, i_Setting);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_GetDeviceStatus(AIO_IOB2_BI2X_AC1 *i_pNodeCtl,
|
||||
AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus) {
|
||||
|
||||
RI_MGR->devices_flush_output();
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus);
|
||||
}
|
||||
|
||||
memset(o_DevStatus, 0x00, sizeof(AIO_IOB2_BI2X_AC1__DEVSTATUS));
|
||||
|
||||
auto &buttons = get_buttons();
|
||||
// struct may be misaligned
|
||||
o_DevStatus->Input.CN8_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN9_8 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN9_9 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Coin]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_14 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]) ? 0xFF : 0;
|
||||
|
||||
// coin
|
||||
o_DevStatus->Input.Coin1Count = eamuse_coin_get_stock();
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_SetWatchDogTimer(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count) {
|
||||
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_AddCounter(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) {
|
||||
|
||||
if (i_pNodeCtl == aioIob2Bi2xAc1 && i_Count == 0) {
|
||||
eamuse_coin_set_stock((uint16_t)i_Count);
|
||||
} else {
|
||||
return aioIob2Bi2xAC1_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_SetOutputData(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data) {
|
||||
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_SetOutputData_orig(i_pNodeCtl, i_CnPin, i_Data);
|
||||
}
|
||||
|
||||
std::optional<Lights::gitadora_lights_t> light;
|
||||
switch (i_CnPin) {
|
||||
case 14:
|
||||
light = Lights::ArenaWooferR;
|
||||
break;
|
||||
case 15:
|
||||
light = Lights::ArenaWooferG;
|
||||
break;
|
||||
case 16:
|
||||
light = Lights::ArenaWooferB;
|
||||
break;
|
||||
case 17:
|
||||
light = Lights::ArenaCardR;
|
||||
break;
|
||||
case 18:
|
||||
light = Lights::ArenaCardG;
|
||||
break;
|
||||
case 19:
|
||||
light = Lights::ArenaCardB;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (light.has_value()) {
|
||||
auto &lights = games::gitadora::get_lights();
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(light.value()), i_Data / 255.f);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_SetTapeLedDataPart(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
|
||||
uint32_t i_Offset, uint8_t *i_pData,
|
||||
uint32_t i_cntTapeLed, bool i_bReverse) {
|
||||
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse);
|
||||
}
|
||||
|
||||
if (tapeledutils::is_enabled() &&
|
||||
i_TapeLedCh == 0 && i_Offset == 0 && i_cntTapeLed == 62) {
|
||||
|
||||
auto &lights = get_lights();
|
||||
const auto rgb = tapeledutils::pick_color_from_led_tape(i_pData, i_cntTapeLed);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::ArenaTitleAvgR], rgb.r);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::ArenaTitleAvgG], rgb.g);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::ArenaTitleAvgB], rgb.b);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2x_SetTapeLedDataLimit(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Channel,
|
||||
uint8_t i_Scale, uint8_t i_Limit) {
|
||||
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2x_SetTapeLedDataLimit_orig(i_pNodeCtl, i_Channel, i_Scale, i_Limit);
|
||||
}
|
||||
}
|
||||
|
||||
static AIO_IOB2_BI2X_WRFIRM *__fastcall aioIob2Bi2x_CreateWriteFirmContext(
|
||||
uint32_t i_SerialNumber, uint32_t i_bfIob) {
|
||||
|
||||
aioIob2Bi2xWrfirm = new AIO_IOB2_BI2X_WRFIRM;
|
||||
return aioIob2Bi2xWrfirm;
|
||||
}
|
||||
|
||||
static void __fastcall aioIob2Bi2x_DestroyWriteFirmContext(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) {
|
||||
|
||||
if (i_pWrFirm == aioIob2Bi2xWrfirm) {
|
||||
delete aioIob2Bi2xWrfirm;
|
||||
aioIob2Bi2xWrfirm = nullptr;
|
||||
} else {
|
||||
return aioIob2Bi2x_DestroyWriteFirmContext_orig(i_pWrFirm);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioIob2Bi2x_WriteFirmGetState(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) {
|
||||
|
||||
if (i_pWrFirm == aioIob2Bi2xWrfirm) {
|
||||
return 8;
|
||||
} else {
|
||||
return aioIob2Bi2x_WriteFirmGetState_orig(i_pWrFirm);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioIob2Bi2x_WriteFirmIsCompleted(int32_t i_State) {
|
||||
|
||||
if (aioIob2Bi2xWrfirm != nullptr) {
|
||||
return true;
|
||||
} else {
|
||||
return aioIob2Bi2x_WriteFirmIsCompleted_orig(i_State);
|
||||
}
|
||||
}
|
||||
|
||||
// libaio-iob.dll
|
||||
static AIO_NMGR_IOB2 *__fastcall aioNMgrIob2_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||
|
||||
if (i_pSci == aioSciComm) {
|
||||
aioNmgrIob2 = new AIO_NMGR_IOB2;
|
||||
return aioNmgrIob2;
|
||||
} else {
|
||||
return aioNMgrIob2_Create_orig(i_pSci, i_bfMode);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNMgrIob_BeginManage(AIO_NMGR_IOB2 *i_pNodeMgr) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob2) {
|
||||
} else {
|
||||
return aioNMgrIob_BeginManage_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNCtlIob_GetNodeInfo(
|
||||
AIO_IOB5_Y32D *i_pNodeCtl, AIO_NMGR_IOB__NODEINFO *o_NodeInfo) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d) {
|
||||
memset(o_NodeInfo, 0, sizeof(AIO_NMGR_IOB__NODEINFO));
|
||||
} else {
|
||||
return aioNCtlIob_GetNodeInfo_orig(i_pNodeCtl, o_NodeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// libaio.dll
|
||||
|
||||
static AIO_SCI_COMM_T *__fastcall aioSciComm_Open(AIO_SCI_COMM_T *unk) {
|
||||
|
||||
aioSciComm = new AIO_SCI_COMM;
|
||||
memset(unk, 0, sizeof(AIO_SCI_COMM_T));
|
||||
unk->aioSciComm = aioSciComm;
|
||||
return unk;
|
||||
}
|
||||
|
||||
static void __fastcall aioSci_GetCommStatus(AIO_SCI_COMM *i_pNodeMgr, AIO_COMM_STATUS *i_Status) {
|
||||
|
||||
if (i_pNodeMgr == aioSciComm) {
|
||||
memset(i_Status, 0, sizeof(AIO_COMM_STATUS));
|
||||
} else {
|
||||
return aioSci_GetCommStatus_orig(i_pNodeMgr, i_Status);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioSci_Destroy(AIO_SCI_COMM *i_pNodeMgr) {
|
||||
|
||||
if (i_pNodeMgr == aioSciComm) {
|
||||
delete aioSciComm;
|
||||
} else {
|
||||
return aioSci_Destroy_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeMgr_Destroy(AIO_NMGR_IOB5 *i_pNodeMgr) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
|
||||
delete aioNmgrIob5;
|
||||
aioNmgrIob5 = nullptr;
|
||||
} else {
|
||||
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB5 *i_pNodeMgr) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeMgr_GetState_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State) {
|
||||
|
||||
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeCtl_Destroy(AIO_IOB5_Y32D *i_pNodeCtl) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
delete aioIob5Y32d;
|
||||
aioIob5Y32d = nullptr;
|
||||
} else {
|
||||
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB5_Y32D *i_pNodeCtl) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeCtl_GetState_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsError(AIO_IOB5_Y32D *i_pNodeCtl, int32_t i_State) {
|
||||
|
||||
if (i_pNodeCtl == aioIob5Y32d ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioIob2Bi2xAc1 ||
|
||||
i_pNodeCtl == (AIO_IOB5_Y32D *)aioNmgrIob2) {
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeCtl_UpdateDevicesStatus() {
|
||||
}
|
||||
|
||||
// libaio-iob5.dll
|
||||
|
||||
static AIO_NMGR_IOB5 *__fastcall aioNMgrIob5_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||
|
||||
if (i_pSci == aioSciComm) {
|
||||
aioNmgrIob5 = new AIO_NMGR_IOB5;
|
||||
return aioNmgrIob5;
|
||||
} else {
|
||||
return aioNMgrIob5_Create_orig(i_pSci, i_bfMode);
|
||||
}
|
||||
}
|
||||
|
||||
// libaio-iob5_y32.dll
|
||||
|
||||
static AIO_IOB5_Y32D *__fastcall aioIob5Y32d_Create(AIO_NMGR_IOB5 *i_pSci, uint32_t i_bfMode) {
|
||||
|
||||
if (i_pSci == aioNmgrIob5) {
|
||||
aioIob5Y32d = new AIO_IOB5_Y32D;
|
||||
return aioIob5Y32d;
|
||||
} else {
|
||||
return aioIob5Y32d_Create_orig(i_pSci, i_bfMode);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob5Y32d_GetDeviceStatus(AIO_IOB5_Y32D *i_pNodeCtl,
|
||||
AIO_IOB5_Y32D__DEVSTATUS *o_DevStatus) {
|
||||
|
||||
if (i_pNodeCtl != aioIob5Y32d) {
|
||||
return aioIob5Y32d_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus);
|
||||
}
|
||||
|
||||
memset(o_DevStatus, 0, sizeof(AIO_IOB5_Y32D__DEVSTATUS));
|
||||
}
|
||||
|
||||
void bi2x_hook_init() {
|
||||
|
||||
// avoid double init
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// announce
|
||||
log_info("bi2x_hook", "init");
|
||||
|
||||
// libaio-iob5_y32.dll
|
||||
const auto libaioIob5Y32Dll = "libaio-iob5_y32.dll";
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32d_Create",
|
||||
aioIob5Y32d_Create, &aioIob5Y32d_Create_orig);
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32i_Create",
|
||||
aioIob5Y32d_Create, &aioIob5Y32d_Create_orig);
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32d_GetDeviceStatus",
|
||||
aioIob5Y32d_GetDeviceStatus, &aioIob5Y32d_GetDeviceStatus_orig);
|
||||
detour::trampoline_try(libaioIob5Y32Dll, "aioIob5Y32i_GetDeviceStatus",
|
||||
aioIob5Y32d_GetDeviceStatus, &aioIob5Y32d_GetDeviceStatus_orig);
|
||||
|
||||
// libaio-iob5.dll
|
||||
const auto libaioIob5Dll = "libaio-iob5.dll";
|
||||
detour::trampoline_try(libaioIob5Dll, "aioNMgrIob5_Create",
|
||||
aioNMgrIob5_Create, &aioNMgrIob5_Create_orig);
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
const auto libaioIob2VideoDll = "libaio-iob2_video.dll";
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_Create",
|
||||
aioIob2Bi2xAC1_Create, &aioIob2Bi2xAC1_Create_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_GetDeviceStatus",
|
||||
aioIob2Bi2xAC1_GetDeviceStatus, &aioIob2Bi2xAC1_GetDeviceStatus_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetWatchDogTimer",
|
||||
aioIob2Bi2xAC1_SetWatchDogTimer, &aioIob2Bi2xAC1_SetWatchDogTimer_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_AddCounter",
|
||||
aioIob2Bi2xAC1_AddCounter, &aioIob2Bi2xAC1_AddCounter_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetOutputData",
|
||||
aioIob2Bi2xAC1_SetOutputData, &aioIob2Bi2xAC1_SetOutputData_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetTapeLedDataPart",
|
||||
aioIob2Bi2xAC1_SetTapeLedDataPart, &aioIob2Bi2xAC1_SetTapeLedDataPart_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataLimit",
|
||||
aioIob2Bi2x_SetTapeLedDataLimit, &aioIob2Bi2x_SetTapeLedDataLimit_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?CreateWriteFirmContext@AIO_IOB2_BI2X@@SAPEAUWRFIRM@1@II@Z",
|
||||
aioIob2Bi2x_CreateWriteFirmContext, &aioIob2Bi2x_CreateWriteFirmContext_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?DestroyWriteFirmContext@AIO_IOB2_BI2X@@SAXPEAUWRFIRM@1@@Z",
|
||||
aioIob2Bi2x_DestroyWriteFirmContext, &aioIob2Bi2x_DestroyWriteFirmContext_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?WriteFirmGetState@AIO_IOB2_BI2X@@SAHPEAUWRFIRM@1@@Z",
|
||||
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
||||
detour::trampoline_try(libaioIob2VideoDll, "?WriteFirmIsCompleted@AIO_IOB2_BI2X@@SA_NH@Z",
|
||||
aioIob2Bi2x_WriteFirmIsCompleted, &aioIob2Bi2x_WriteFirmIsCompleted_orig);
|
||||
|
||||
// libaio-iob.dll
|
||||
const auto libaioIobDll = "libaio-iob.dll";
|
||||
detour::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
|
||||
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
||||
detour::trampoline_try(libaioIobDll, "aioNMgrIob_BeginManage",
|
||||
aioNMgrIob_BeginManage, &aioNMgrIob_BeginManage_orig);
|
||||
detour::trampoline_try(libaioIobDll, "aioNCtlIob_GetNodeInfo",
|
||||
aioNCtlIob_GetNodeInfo, &aioNCtlIob_GetNodeInfo_orig);
|
||||
|
||||
// libaio.dll
|
||||
const auto libaioDll = "libaio.dll";
|
||||
detour::trampoline_try(libaioDll, "?Open@AIO_SCI_COMM@@SA?AU?$AC_ERESULT_T@PEAVAIO_SCI_COMM@@@@PEBD0AEBUSETTING@1@@Z",
|
||||
aioSciComm_Open, &aioSciComm_Open_orig);
|
||||
detour::trampoline_try(libaioDll, "aioSci_GetCommStatus",
|
||||
aioSci_GetCommStatus, &aioSci_GetCommStatus_orig);
|
||||
detour::trampoline_try(libaioDll, "aioSci_Destroy",
|
||||
aioSci_Destroy, &aioSci_Destroy_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
|
||||
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeMgr_GetState",
|
||||
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
|
||||
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeMgr_IsError",
|
||||
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
|
||||
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_GetState",
|
||||
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
|
||||
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_UpdateDevicesStatus",
|
||||
aioNodeCtl_UpdateDevicesStatus, &aioNodeCtl_UpdateDevicesStatus_orig);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::gitadora {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
class GitadoraDevice : public acioemu::ACIODeviceEmu {
|
||||
public:
|
||||
bool is_ready = false;
|
||||
};
|
||||
}
|
||||
@@ -1,20 +1,56 @@
|
||||
#include "gitadora.h"
|
||||
|
||||
#include "handle.h"
|
||||
#include "bi2x_hook.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cfg/configurator.h"
|
||||
#include "hooks/audio/mme.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/cpuutils.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "hooks/setupapihook.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
// settings
|
||||
bool TWOCHANNEL = false;
|
||||
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
||||
bool ARENA_SINGLE_WINDOW = false;
|
||||
bool P1_LEFTY = false;
|
||||
bool P2_LEFTY = false;
|
||||
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||
std::optional<socd::SocdAlgorithm> PICK_ALGO = socd::SocdAlgorithm::PreferRecent;
|
||||
|
||||
/*
|
||||
* Prevent GitaDora from creating folders on F drive
|
||||
*/
|
||||
|
||||
#ifdef SPICE64
|
||||
static DWORD WINAPI GetLogicalDrives_hook() {
|
||||
return GetLogicalDrives() | 0x20;
|
||||
}
|
||||
|
||||
static UINT WINAPI GetDriveTypeA_hook(LPCSTR lpRootPathName) {
|
||||
if (!strcmp(lpRootPathName, "F:\\")) {
|
||||
return DRIVE_FIXED;
|
||||
}
|
||||
|
||||
return GetDriveTypeA(lpRootPathName);
|
||||
}
|
||||
|
||||
static BOOL WINAPI CreateDirectoryA_hook(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) {
|
||||
if (!strncmp(lpPathName, "F:/", 3)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return CreateDirectoryA(lpPathName, lpSecurityAttributes);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GitaDora checks if the IP address has changed, and if it has it throws 5-1506-0000 like jubeat.
|
||||
@@ -184,6 +220,23 @@ namespace games::gitadora {
|
||||
log_fatal("gitadora", "Cabinet type 3 (SD2) not supported on XG series");
|
||||
}
|
||||
#endif
|
||||
|
||||
// arena model launches a tiny window yet backbuffer at 4k, resulting in unusable overlay
|
||||
// force scaling to make things usable
|
||||
if (!overlay::UI_SCALE_PERCENT.has_value() && is_arena_model() &&
|
||||
GRAPHICS_WINDOWED && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
overlay::UI_SCALE_PERCENT = 250;
|
||||
}
|
||||
|
||||
// for guitar wail SOCD cleaning
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||
|
||||
// for guitar picking
|
||||
if (PICK_ALGO.has_value()) {
|
||||
log_info("gitadora", "guitar pick SOCD algorithm: {}", static_cast<int>(PICK_ALGO.value()));
|
||||
} else {
|
||||
log_info("gitadora", "guitar pick SOCD algorithm: legacy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +267,44 @@ namespace games::gitadora {
|
||||
system_module, "sys_debug_dip_set_param"));
|
||||
|
||||
#ifdef SPICE64
|
||||
// gitadora arena model
|
||||
auto aio = libutils::try_library("libaio.dll");
|
||||
if (aio != nullptr) {
|
||||
SETUPAPI_SETTINGS settings{};
|
||||
settings.class_guid[0] = 0x86E0D1E0;
|
||||
settings.class_guid[1] = 0x11D08089;
|
||||
settings.class_guid[2] = 0x0008E49C;
|
||||
settings.class_guid[3] = 0x731F303E;
|
||||
const char property[] = "1CCF(8050)_000";
|
||||
const char property_hardwareid[] = "USB\\VID_1CCF&PID_8050&MI_00\\000";
|
||||
memcpy(settings.property_devicedesc, property, sizeof(property));
|
||||
memcpy(settings.property_hardwareid, property_hardwareid, sizeof(property_hardwareid));
|
||||
setupapihook_init(avs::game::DLL_INSTANCE);
|
||||
setupapihook_add(settings);
|
||||
|
||||
// Gitadora IO(J32D/J33I) board emulation
|
||||
devicehook_init(avs::game::DLL_INSTANCE);
|
||||
devicehook_add(new GitaDoraSerialHandle());
|
||||
|
||||
// test/service/coin buttons
|
||||
bi2x_hook_init();
|
||||
|
||||
// f: drive hook
|
||||
detour::iat_try("GetLogicalDrives", GetLogicalDrives_hook, avs::game::DLL_INSTANCE);
|
||||
detour::iat_try("GetDriveTypeA", GetDriveTypeA_hook, avs::game::DLL_INSTANCE);
|
||||
detour::iat_try("CreateDirectoryA", CreateDirectoryA_hook, avs::game::DLL_INSTANCE);
|
||||
|
||||
// volume change prevention
|
||||
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
||||
|
||||
// touch hook
|
||||
if (ARENA_SINGLE_WINDOW) {
|
||||
wintouchemu::FORCE = true;
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook("GITADORA", avs::game::DLL_INSTANCE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
||||
|
||||
|
||||
@@ -4,12 +4,18 @@
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/game.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
// settings
|
||||
extern bool TWOCHANNEL;
|
||||
extern std::optional<unsigned int> CAB_TYPE;
|
||||
extern bool ARENA_SINGLE_WINDOW;
|
||||
extern bool P1_LEFTY;
|
||||
extern bool P2_LEFTY;
|
||||
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||
extern std::optional<socd::SocdAlgorithm> PICK_ALGO;
|
||||
|
||||
class GitaDoraGame : public games::Game {
|
||||
public:
|
||||
@@ -21,14 +27,29 @@ namespace games::gitadora {
|
||||
static inline bool is_drum() {
|
||||
return (
|
||||
avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B')
|
||||
(avs::game::is_model("M32") && (avs::game::SPEC[0] == 'B' || avs::game::SPEC[0] == 'D'))
|
||||
);
|
||||
}
|
||||
static inline bool is_guitar() {
|
||||
return (
|
||||
avs::game::is_model({ "J33", "K33", "L33" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'A')
|
||||
(avs::game::is_model("M32") && (avs::game::SPEC[0] == 'A' || avs::game::SPEC[0] == 'C'))
|
||||
);
|
||||
}
|
||||
|
||||
static inline bool is_arena_model() {
|
||||
return (
|
||||
avs::game::is_model("M32") &&
|
||||
(avs::game::SPEC[0] == 'C' || avs::game::SPEC[0] == 'D')
|
||||
);
|
||||
}
|
||||
|
||||
static inline bool is_player_lefty(size_t player) {
|
||||
if (player == 0) {
|
||||
return P1_LEFTY;
|
||||
} else if (player == 1) {
|
||||
return P2_LEFTY;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "handle.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/time.h"
|
||||
#include "io.h"
|
||||
#include "gitadora.h"
|
||||
#include "j32d.h"
|
||||
#include "j33i.h"
|
||||
#include "device.h"
|
||||
|
||||
|
||||
using namespace acioemu;
|
||||
static games::gitadora::GitadoraDevice *device;
|
||||
|
||||
bool games::gitadora::GitaDoraSerialHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"COM1") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_drum = games::gitadora::is_drum();
|
||||
|
||||
if (is_drum) {
|
||||
device = new J32DSerialDevice();
|
||||
} else {
|
||||
device = new J33ISerialDevice();
|
||||
}
|
||||
|
||||
acio_emu.add_device(device);
|
||||
log_info("gitadora", "Opened COM1 ({} DEVICE)", is_drum ? "J32D" : "J33I");
|
||||
return true;
|
||||
}
|
||||
|
||||
int games::gitadora::GitaDoraSerialHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
|
||||
// the game does not send cmd 0x12F to the IO
|
||||
// but it is expected to receive 0x12F for user input
|
||||
// so i manually created a payload so that parse_msg can be used
|
||||
// to write to the response_buffer
|
||||
if (device->is_ready) {
|
||||
uint8_t chk = 0;
|
||||
MessageData* msg = ACIODeviceEmu::create_msg(0x1, 0x12F, 0, 0);
|
||||
msg->addr = 0x1;
|
||||
auto write_buffer = reinterpret_cast<uint8_t *>(msg);
|
||||
while (acio_emu.read() != std::nullopt); // there may be old stuffs in the buffer
|
||||
acio_emu.write(0xAA);
|
||||
acio_emu.write(0xAA);
|
||||
|
||||
for(DWORD i = 0; i < 5; i++) {
|
||||
chk += write_buffer[i];
|
||||
acio_emu.write(write_buffer[i]);
|
||||
}
|
||||
// direct copy and paste from write_msg
|
||||
if (chk == ACIO_SOF || chk == ACIO_ESCAPE) {
|
||||
acio_emu.write(ACIO_ESCAPE);
|
||||
acio_emu.write(~chk);
|
||||
} else {
|
||||
acio_emu.write(chk);
|
||||
}
|
||||
}
|
||||
|
||||
// read from emu
|
||||
DWORD bytes_read = 0;
|
||||
while (bytes_read < nNumberOfBytesToRead) {
|
||||
auto cur_byte = acio_emu.read();
|
||||
|
||||
if (cur_byte.has_value()) {
|
||||
buffer[bytes_read++] = cur_byte.value();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Read IO Data: {}", bin2hex(buffer, bytes_read));
|
||||
#endif
|
||||
// return amount of bytes read
|
||||
return (int)bytes_read;
|
||||
}
|
||||
|
||||
|
||||
int games::gitadora::GitaDoraSerialHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite)
|
||||
{
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
// write to emu
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
acio_emu.write(buffer[i]);
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Write IO Data: {}", bin2hex(buffer, nNumberOfBytesToWrite));
|
||||
#endif
|
||||
// return all data written
|
||||
return (int)nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int games::gitadora::GitaDoraSerialHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer, DWORD nOutBufferSize) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool games::gitadora::GitaDoraSerialHandle::close() {
|
||||
|
||||
log_info("gitadora", "Closed COM1");
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
class GitaDoraSerialHandle : public CustomHandle {
|
||||
private:
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
bool close() override;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -10,6 +10,7 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
||||
"Service",
|
||||
"Test",
|
||||
"Coin",
|
||||
"Headphone",
|
||||
"Guitar P1 Start",
|
||||
"Guitar P1 Up",
|
||||
"Guitar P1 Down",
|
||||
@@ -64,17 +65,18 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
||||
"Drum Help",
|
||||
"Drum Button Extra 1",
|
||||
"Drum Button Extra 2",
|
||||
|
||||
"Drum Left Cymbal",
|
||||
"Drum Hi-Hat",
|
||||
"Drum Hi-Hat Closed",
|
||||
"Drum Hi-Hat Half-Open",
|
||||
"Drum Left Pedal",
|
||||
"Drum Snare",
|
||||
"Drum Hi-Tom",
|
||||
"Drum Low-Tom",
|
||||
"Drum Right Cymbal",
|
||||
"Drum Bass Pedal",
|
||||
"Drum Left Cymbal",
|
||||
"Drum Left Pedal",
|
||||
"Drum Floor Tom"
|
||||
"Drum Low-Tom",
|
||||
"Drum Floor Tom",
|
||||
"Drum Right Cymbal",
|
||||
"Drum Hi-Hat Closed",
|
||||
"Drum Hi-Hat Half-Open"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,9 +108,18 @@ std::string games::gitadora::get_buttons_help() {
|
||||
// keep to max 100 characters wide
|
||||
return
|
||||
"guitar:\n"
|
||||
"\n"
|
||||
" < R G B Y P --- Pick ] \n"
|
||||
"\n"
|
||||
" If you normally hold your guitar left-handed (frets on right hand):\n"
|
||||
" * here, hold the guitar as right-handed (frets on left hand),\n"
|
||||
" and bind the buttons, pick, and wail controls\n"
|
||||
" * in Options tab, turn on GitaDora Lefty Guitar\n"
|
||||
" * finally, use the in-game option to turn on LEFT mode\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"drums:\n"
|
||||
"\n"
|
||||
" LeftCymbal RightCymbal\n"
|
||||
" HiHat HiTom LowTom \n"
|
||||
" Snare FloorTom \n"
|
||||
@@ -116,10 +127,28 @@ std::string games::gitadora::get_buttons_help() {
|
||||
" Left Bass\n"
|
||||
" Pedal Pedal\n"
|
||||
"\n"
|
||||
"Drums are NOT velocity-sensitive!\n"
|
||||
" For MIDI drums with Open/Closed HiHat configurations or pads with\n"
|
||||
" multiple hit zones, ensure you bind all variation using the Pages\n"
|
||||
" button at the bottom."
|
||||
;
|
||||
}
|
||||
std::string games::gitadora::get_analogs_help() {
|
||||
// keep to max 100 characters wide
|
||||
return
|
||||
"guitar:\n"
|
||||
"\n"
|
||||
"For MIDI drums with Open/Closed HiHat configurations, bind variations below. "
|
||||
"v2_drum algorithm might work better for those drums."
|
||||
"X axis: 0% when body is held facing monitor, 50% when flat on a table\n"
|
||||
"Y axis: 50% when held horizontal, 0% when neck is raised, 100% pointing down\n"
|
||||
"Z axis: 50% at rest, 100% when swinging neck toward monitor (only used in XG series)\n"
|
||||
"\n"
|
||||
"You need both X and Y axis for up/down wail to work correctly in-game.\n"
|
||||
"If you only have Y axis, consider using digital wailing instead.\n"
|
||||
"\n"
|
||||
"Ensure you clear all analog bindings if you want to use digital wailing.\n"
|
||||
"\n"
|
||||
"If you hold your guitar left-handed (frets on the right hand),\n"
|
||||
"analog bindings will likely not work as intended.\n"
|
||||
"Consider using digital wailing in Buttons tab."
|
||||
;
|
||||
}
|
||||
|
||||
@@ -129,88 +158,102 @@ std::vector<Light> &games::gitadora::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("GitaDora");
|
||||
|
||||
GameAPI::Lights::sortLights(&lights,
|
||||
"Guitar P1 Motor",
|
||||
"Guitar P2 Motor",
|
||||
GameAPI::Lights::sortLightsWithCategory(&lights,
|
||||
{
|
||||
{"Guitar", "Guitar P1 Motor"},
|
||||
{"Guitar", "Guitar P2 Motor"},
|
||||
|
||||
"P1 Start",
|
||||
"P1 Menu Up Down (DX)",
|
||||
"P1 Menu Left Right (DX)",
|
||||
"P1 Help (DX)",
|
||||
{"Menu", "P1 Start"},
|
||||
{"Menu", "P1 Menu Up Down (DX)"},
|
||||
{"Menu", "P1 Menu Left Right (DX)"},
|
||||
{"Menu", "P1 Help (DX)"},
|
||||
|
||||
"P2 Start",
|
||||
"P2 Menu Up Down (DX)",
|
||||
"P2 Menu Left Right (DX)",
|
||||
"P2 Help (DX)",
|
||||
{"Menu", "P2 Start"},
|
||||
{"Menu", "P2 Menu Up Down (DX)"},
|
||||
{"Menu", "P2 Menu Left Right (DX)"},
|
||||
{"Menu", "P2 Help (DX)"},
|
||||
|
||||
"Drum Left Cymbal",
|
||||
"Drum Hi-Hat",
|
||||
"Drum Snare",
|
||||
"Drum High Tom",
|
||||
"Drum Low Tom",
|
||||
"Drum Floor Tom",
|
||||
"Drum Right Cymbal",
|
||||
{"Drums", "Drum Left Cymbal"},
|
||||
{"Drums", "Drum Hi-Hat"},
|
||||
{"Drums", "Drum Snare"},
|
||||
{"Drums", "Drum High Tom"},
|
||||
{"Drums", "Drum Low Tom"},
|
||||
{"Drums", "Drum Floor Tom"},
|
||||
{"Drums", "Drum Right Cymbal"},
|
||||
|
||||
"Drum Woofer R",
|
||||
"Drum Woofer G",
|
||||
"Drum Woofer B",
|
||||
{"DX/SD", "Drum Woofer R"},
|
||||
{"DX/SD", "Drum Woofer G"},
|
||||
{"DX/SD", "Drum Woofer B"},
|
||||
|
||||
"Drum Stage R (DX)",
|
||||
"Drum Stage G (DX)",
|
||||
"Drum Stage B (DX)",
|
||||
{"DX/SD", "Drum Stage R (DX)"},
|
||||
{"DX/SD", "Drum Stage G (DX)"},
|
||||
{"DX/SD", "Drum Stage B (DX)"},
|
||||
|
||||
"Spot Left (DX)",
|
||||
"Spot Right (DX)",
|
||||
"Spot Center Left (DX)",
|
||||
"Spot Center Right (DX)",
|
||||
{"DX/SD", "Spot Left (DX)"},
|
||||
{"DX/SD", "Spot Right (DX)"},
|
||||
{"DX/SD", "Spot Center Left (DX)"},
|
||||
{"DX/SD", "Spot Center Right (DX)"},
|
||||
|
||||
"Drum Spot Rear Left (DX)",
|
||||
"Drum Spot Rear Right (DX)",
|
||||
{"DX/SD", "Drum Spot Rear Left (DX)"},
|
||||
{"DX/SD", "Drum Spot Rear Right (DX)"},
|
||||
|
||||
"Guitar Lower Left R (DX)",
|
||||
"Guitar Lower Left G (DX)",
|
||||
"Guitar Lower Left B (DX)",
|
||||
"Guitar Lower Right R (DX)",
|
||||
"Guitar Lower Right G (DX)",
|
||||
"Guitar Lower Right B (DX)",
|
||||
{"DX/SD", "Guitar Lower Left R (DX)"},
|
||||
{"DX/SD", "Guitar Lower Left G (DX)"},
|
||||
{"DX/SD", "Guitar Lower Left B (DX)"},
|
||||
{"DX/SD", "Guitar Lower Right R (DX)"},
|
||||
{"DX/SD", "Guitar Lower Right G (DX)"},
|
||||
{"DX/SD", "Guitar Lower Right B (DX)"},
|
||||
|
||||
"Guitar Left Speaker Upper R (DX)",
|
||||
"Guitar Left Speaker Upper G (DX)",
|
||||
"Guitar Left Speaker Upper B (DX)",
|
||||
"Guitar Left Speaker Mid Up Left R (DX)",
|
||||
"Guitar Left Speaker Mid Up Left G (DX)",
|
||||
"Guitar Left Speaker Mid Up Left B (DX)",
|
||||
"Guitar Left Speaker Mid Up Right R (DX)",
|
||||
"Guitar Left Speaker Mid Up Right G (DX)",
|
||||
"Guitar Left Speaker Mid Up Right B (DX)",
|
||||
"Guitar Left Speaker Mid Low Left R (DX)",
|
||||
"Guitar Left Speaker Mid Low Left G (DX)",
|
||||
"Guitar Left Speaker Mid Low Left B (DX)",
|
||||
"Guitar Left Speaker Mid Low Right R (DX)",
|
||||
"Guitar Left Speaker Mid Low Right G (DX)",
|
||||
"Guitar Left Speaker Mid Low Right B (DX)",
|
||||
"Guitar Left Speaker Lower R (DX)",
|
||||
"Guitar Left Speaker Lower G (DX)",
|
||||
"Guitar Left Speaker Lower B (DX)",
|
||||
{"DX/SD", "Guitar Left Speaker Upper R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Upper G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Upper B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Left R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Left G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Left B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Right R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Right G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Up Right B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Left R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Left G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Left B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Right R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Right G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Mid Low Right B (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Lower R (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Lower G (DX)"},
|
||||
{"DX/SD", "Guitar Left Speaker Lower B (DX)"},
|
||||
|
||||
"Guitar Right Speaker Upper R (DX)",
|
||||
"Guitar Right Speaker Upper G (DX)",
|
||||
"Guitar Right Speaker Upper B (DX)",
|
||||
"Guitar Right Speaker Mid Up Left R (DX)",
|
||||
"Guitar Right Speaker Mid Up Left G (DX)",
|
||||
"Guitar Right Speaker Mid Up Left B (DX)",
|
||||
"Guitar Right Speaker Mid Up Right R (DX)",
|
||||
"Guitar Right Speaker Mid Up Right G (DX)",
|
||||
"Guitar Right Speaker Mid Up Right B (DX)",
|
||||
"Guitar Right Speaker Mid Low Left R (DX)",
|
||||
"Guitar Right Speaker Mid Low Left G (DX)",
|
||||
"Guitar Right Speaker Mid Low Left B (DX)",
|
||||
"Guitar Right Speaker Mid Low Right R (DX)",
|
||||
"Guitar Right Speaker Mid Low Right G (DX)",
|
||||
"Guitar Right Speaker Mid Low Right B (DX)",
|
||||
"Guitar Right Speaker Lower R (DX)",
|
||||
"Guitar Right Speaker Lower G (DX)",
|
||||
"Guitar Right Speaker Lower B (DX)"
|
||||
{"DX/SD", "Guitar Right Speaker Upper R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Upper G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Upper B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Left R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Left G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Left B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Right R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Right G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Up Right B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Left R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Left G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Left B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Right R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Right G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Mid Low Right B (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Lower R (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Lower G (DX)"},
|
||||
{"DX/SD", "Guitar Right Speaker Lower B (DX)"},
|
||||
|
||||
{"Arena Model", "Title Average R (Arena)"},
|
||||
{"Arena Model", "Title Average G (Arena)"},
|
||||
{"Arena Model", "Title Average B (Arena)"},
|
||||
|
||||
{"Arena Model", "Woofer R (Arena)"},
|
||||
{"Arena Model", "Woofer G (Arena)"},
|
||||
{"Arena Model", "Woofer B (Arena)"},
|
||||
|
||||
{"Arena Model", "Card Reader R (Arena)"},
|
||||
{"Arena Model", "Card Reader G (Arena)"},
|
||||
{"Arena Model", "Card Reader B (Arena)"}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace games::gitadora {
|
||||
Service,
|
||||
Test,
|
||||
Coin,
|
||||
Headphone,
|
||||
GuitarP1Start,
|
||||
GuitarP1Up,
|
||||
GuitarP1Down,
|
||||
@@ -65,18 +66,21 @@ namespace games::gitadora {
|
||||
DrumHelp,
|
||||
DrumButtonExtra1,
|
||||
DrumButtonExtra2,
|
||||
// note: this is the order they appear in the test menu
|
||||
// note: this is the order of drums in the default layout
|
||||
DrumLeftCymbal,
|
||||
DrumHiHat,
|
||||
DrumHiHatClosed,
|
||||
DrumHiHatHalfOpen,
|
||||
DrumLeftPedal,
|
||||
DrumSnare,
|
||||
DrumHiTom,
|
||||
DrumLowTom,
|
||||
DrumRightCymbal,
|
||||
DrumBassPedal,
|
||||
DrumLeftCymbal,
|
||||
DrumLeftPedal,
|
||||
DrumFloorTom
|
||||
DrumLowTom,
|
||||
DrumFloorTom,
|
||||
DrumRightCymbal,
|
||||
|
||||
// these only exist for back compat
|
||||
// (created before it was possible to map multiple input to a button)
|
||||
DrumHiHatClosed,
|
||||
DrumHiHatHalfOpen
|
||||
};
|
||||
}
|
||||
|
||||
@@ -190,13 +194,25 @@ namespace games::gitadora {
|
||||
GuitarRightSpeakerMidLowRightB,
|
||||
GuitarRightSpeakerLowerR,
|
||||
GuitarRightSpeakerLowerG,
|
||||
GuitarRightSpeakerLowerB
|
||||
GuitarRightSpeakerLowerB,
|
||||
|
||||
// arena model (common for both guitar and drum)
|
||||
ArenaTitleAvgR,
|
||||
ArenaTitleAvgG,
|
||||
ArenaTitleAvgB,
|
||||
ArenaWooferR,
|
||||
ArenaWooferG,
|
||||
ArenaWooferB,
|
||||
ArenaCardR,
|
||||
ArenaCardG,
|
||||
ArenaCardB,
|
||||
} gitadora_lights_t;
|
||||
}
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::string get_buttons_help();
|
||||
std::string get_analogs_help();
|
||||
std::vector<Analog> &get_analogs();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
#include "io.h"
|
||||
#include "j32d.h"
|
||||
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
games::gitadora::J32DSerialDevice::J32DSerialDevice() {
|
||||
this->node_count = 1;
|
||||
log_info("gitadora", "J32D device created.");
|
||||
}
|
||||
|
||||
bool games::gitadora::J32DSerialDevice::parse_msg(
|
||||
MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "GTDR ADDR: {}, CMD: 0x{:04x}", msg_in->addr, msg_in->cmd.code);
|
||||
#endif
|
||||
|
||||
static uint8_t pid = 0;
|
||||
// check command
|
||||
switch (msg_in->cmd.code) {
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
this->set_version(msg, 0x20a, 0, 0, 1, 0, "J32D");
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_STARTUP:
|
||||
case ACIO_CMD_CLEAR:
|
||||
case 0x0120: {
|
||||
if (msg_in->cmd.code == 0x0120) {
|
||||
pid = msg_in->cmd.pid;
|
||||
this->is_ready = true;
|
||||
}
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
// IO
|
||||
case 0x012F: {
|
||||
auto msg = this->create_msg(msg_in, 0x11);
|
||||
msg->cmd.pid = pid;
|
||||
// don't know if it's needed/correct,
|
||||
// without it the game seems to work just fine
|
||||
pid += 1;
|
||||
J32D_IO_STATUS payload;
|
||||
memset(&payload, 0, sizeof(J32D_IO_STATUS));
|
||||
|
||||
auto &buttons = get_buttons();
|
||||
auto high_tom = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiTom]) * 1023);
|
||||
auto low_tom = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLowTom]) * 1023);
|
||||
auto snare = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumSnare]) * 1023);
|
||||
auto floor_tom = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumFloorTom]) * 1023);
|
||||
auto left_cymbal = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLeftCymbal]) * 1023);
|
||||
auto right_cymbal = (uint16_t) (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumRightCymbal]) * 1023);
|
||||
auto hihat = (uint16_t) ((GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHat]) ||
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHatClosed]) ||
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumHiHatHalfOpen])) * 1023);
|
||||
|
||||
#define ENCODE_ANALOG(__val) (((__val & 0x3f) << 10) | ((__val & 0x3c0) >> 6))
|
||||
payload.high_tom = ENCODE_ANALOG(high_tom);
|
||||
payload.low_tom = ENCODE_ANALOG(low_tom);
|
||||
payload.snare = ENCODE_ANALOG(snare);
|
||||
payload.floor_tom = ENCODE_ANALOG(floor_tom);
|
||||
payload.left_cymbal = ENCODE_ANALOG(left_cymbal);
|
||||
payload.right_cymbal = ENCODE_ANALOG(right_cymbal);
|
||||
payload.hihat = ENCODE_ANALOG(hihat);
|
||||
#undef ENCODE_ANALOG
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumBassPedal])) {
|
||||
payload.pedals |= 1;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::DrumLeftPedal])) {
|
||||
payload.pedals |= 2;
|
||||
}
|
||||
|
||||
memcpy(msg->cmd.raw, &payload, sizeof(J32D_IO_STATUS));
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Unhandled cmd {} received.", msg_in->cmd.code);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "acioemu/device.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
class J32DSerialDevice : public GitadoraDevice {
|
||||
private:
|
||||
uint8_t coin_counter = 0;
|
||||
public:
|
||||
J32DSerialDevice();
|
||||
bool parse_msg(acioemu::MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) override;
|
||||
struct J32D_IO_STATUS {
|
||||
uint16_t high_tom;
|
||||
uint16_t low_tom;
|
||||
uint16_t snare;
|
||||
uint16_t floor_tom;
|
||||
uint16_t left_cymbal;
|
||||
uint16_t right_cymbal;
|
||||
uint16_t hihat;
|
||||
uint16_t unk7;
|
||||
uint8_t pedals;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "io.h"
|
||||
#include "j33i.h"
|
||||
|
||||
#define GUITAR_BTN_R 0x7
|
||||
#define GUITAR_BTN_G 0x6
|
||||
#define GUITAR_BTN_B 0x5
|
||||
#define GUITAR_BTN_Y 0x4
|
||||
#define GUITAR_BTN_P 0x3
|
||||
#define GUITAR_PICK_DOWN 0xC
|
||||
#define GUITAR_PICK_UP 0xD
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
// why this number?? no clue, but this is what the test menu shows as the max
|
||||
// value in the green box with the red dot; the value display itself can go
|
||||
// higher but the red dot will reach the edge at +-189
|
||||
#define ARENA_MODEL_WAIL_MAX 189
|
||||
|
||||
static bool GFDM_GF_PICK_STATE_UP;
|
||||
static bool GFDM_GF_PICK_STATE_DOWN;
|
||||
|
||||
games::gitadora::J33ISerialDevice::J33ISerialDevice() {
|
||||
this->node_count = 1;
|
||||
log_info("gitadora", "J33I device created.");
|
||||
}
|
||||
|
||||
bool games::gitadora::J33ISerialDevice::parse_msg(
|
||||
MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "GTDR ADDR: {}, CMD: 0x{:04x}", msg_in->addr, msg_in->cmd.code);
|
||||
#endif
|
||||
|
||||
static uint8_t pid = 0;
|
||||
// check command
|
||||
switch (msg_in->cmd.code)
|
||||
{
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
this->set_version(msg, 0x10a, 0, 1, 0, 2, "J33I");
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_STARTUP:
|
||||
case ACIO_CMD_CLEAR:
|
||||
case 0x0120: {
|
||||
if (msg_in->cmd.code == 0x0120) {
|
||||
pid = msg_in->cmd.pid;
|
||||
this->is_ready = true;
|
||||
}
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
// IO
|
||||
case 0x012F: {
|
||||
auto msg = this->create_msg(msg_in, 0x8);
|
||||
msg->cmd.pid = pid;
|
||||
pid += 1;
|
||||
J33I_IO_STATUS payload;
|
||||
memset(&payload, 0, sizeof(J33I_IO_STATUS));
|
||||
|
||||
auto &buttons = get_buttons();
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1R])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_R;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1G])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_G;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1B])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_B;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1Y])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_Y;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1P])) {
|
||||
payload.buttons |= 1 << GUITAR_BTN_P;
|
||||
}
|
||||
|
||||
|
||||
const auto pick_up = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickUp]);
|
||||
const auto pick_down = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickDown]);
|
||||
if (!games::gitadora::PICK_ALGO.has_value()) {
|
||||
// legacy behavior (detect rising edges only, input for 1 frame)
|
||||
if (pick_up) {
|
||||
if (!GFDM_GF_PICK_STATE_UP) {
|
||||
GFDM_GF_PICK_STATE_UP = true;
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_UP = false;
|
||||
}
|
||||
|
||||
if (pick_down) {
|
||||
if (!GFDM_GF_PICK_STATE_DOWN) {
|
||||
GFDM_GF_PICK_STATE_DOWN = true;
|
||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_DOWN = false;
|
||||
}
|
||||
} else {
|
||||
const auto socd = socd::socd_clean(
|
||||
0,
|
||||
pick_up,
|
||||
pick_down,
|
||||
get_performance_milliseconds(),
|
||||
games::gitadora::PICK_ALGO.value()
|
||||
);
|
||||
if (socd == socd::SocdCCW) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
} else if (socd == socd::SocdCW) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||
} else if (socd == socd::SocdBoth) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
auto &analogs = get_analogs();
|
||||
|
||||
// get x,y,z analog values [-0.5f, 0.5f]
|
||||
float x = is_player_lefty(0) ? 0.3f : -0.5f;
|
||||
auto x_analog = analogs[Analogs::GuitarP1WailX];
|
||||
if (x_analog.isSet()) {
|
||||
x = GameAPI::Analogs::getState(RI_MGR, x_analog) - 0.5f;
|
||||
}
|
||||
|
||||
float y = 0.f;
|
||||
auto y_analog = analogs[Analogs::GuitarP1WailY];
|
||||
if (y_analog.isSet()) {
|
||||
y = GameAPI::Analogs::getState(RI_MGR, y_analog) - 0.5f;
|
||||
}
|
||||
|
||||
float z = 0.f;
|
||||
auto z_analog = analogs[Analogs::GuitarP1WailZ];
|
||||
if (z_analog.isSet()) {
|
||||
z = GameAPI::Analogs::getState(RI_MGR, z_analog) - 0.5f;
|
||||
}
|
||||
|
||||
// handle digital wail up/down
|
||||
const auto wail_up = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailUp]);
|
||||
const auto wail_down = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1WailDown]);
|
||||
const auto wail_result =
|
||||
socd::get_guitar_wail(0, wail_up, wail_down, get_performance_milliseconds());
|
||||
if (!is_player_lefty(0)) {
|
||||
// righty
|
||||
if (wail_result == socd::TiltUp) {
|
||||
x = 0;
|
||||
y = -0.5f;
|
||||
} else if (wail_result == socd::TiltDown) {
|
||||
x = -0.5f;
|
||||
y = 0.5f;
|
||||
}
|
||||
} else {
|
||||
// lefty
|
||||
if (wail_result == socd::TiltUp) {
|
||||
x = 0.f;
|
||||
y = 0.f;
|
||||
} else if (wail_result == socd::TiltDown) {
|
||||
x = 0.5f;
|
||||
y = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
// convert x,y,z to unsigned integer values that the game expects
|
||||
const uint16_t x_int = x * ARENA_MODEL_WAIL_MAX * 2;
|
||||
const uint16_t y_int = y * ARENA_MODEL_WAIL_MAX * 2;
|
||||
const uint16_t z_int = z * ARENA_MODEL_WAIL_MAX * 2;
|
||||
payload.gyroscope_x = static_cast<uint16_t>(((x_int >> 3) & (0xFF)) | ((x_int & 0x07) << 12));
|
||||
payload.gyroscope_y = static_cast<uint16_t>(((y_int >> 3) & (0xFF)) | ((y_int & 0x07) << 12));
|
||||
payload.gyroscope_z = static_cast<uint16_t>(((z_int >> 3) & (0xFF)) | ((z_int & 0x07) << 12));
|
||||
|
||||
memcpy(msg->cmd.raw, &payload, sizeof(J33I_IO_STATUS));
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("gitadora", "Unhandled cmd {} received.", msg_in->cmd.code);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/device.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
class J33ISerialDevice : public GitadoraDevice {
|
||||
private:
|
||||
uint8_t coin_counter = 0;
|
||||
public:
|
||||
J33ISerialDevice();
|
||||
bool parse_msg(acioemu::MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) override;
|
||||
|
||||
// there may be more, but the game doesn't seem to care
|
||||
// TODO: add support for gyroscope, knob and wail
|
||||
struct J33I_IO_STATUS {
|
||||
uint16_t gyroscope_x;
|
||||
uint16_t gyroscope_y;
|
||||
uint16_t gyroscope_z;
|
||||
uint16_t buttons;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -195,11 +195,11 @@ bool games::iidx::IIDXFMSerialHandle::open(LPCWSTR lpFileName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("iidx", "Opened COM2 (FM DEVICE)");
|
||||
log_info("iidx", "Opened COM2 (FM DEVICE / bi2a)");
|
||||
games::iidx::update_io_emulation_state(games::iidx::iidx_aio_emulation_state::bi2a_com2);
|
||||
|
||||
// ACIO device
|
||||
acio_emu.add_device(new FMSerialDevice());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ int games::iidx::IIDXFMSerialHandle::device_io(DWORD dwIoControlCode, LPVOID lpI
|
||||
}
|
||||
|
||||
bool games::iidx::IIDXFMSerialHandle::close() {
|
||||
log_info("iidx", "Closed COM2 (FM DEVICE)");
|
||||
log_info("iidx", "Closed COM2 (FM DEVICE / bi2a)");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <cstdint>
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
@@ -24,17 +26,28 @@ namespace games::iidx {
|
||||
uint8_t data[0x10];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t dummy0[0x50];
|
||||
struct AIO_NMGR_IOB2_VTABLE {
|
||||
// other functions here, but they never get called
|
||||
uint8_t dummy[0x50];
|
||||
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
||||
uint8_t dummy1[0x9A0];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
AIO_NMGR_IOB2_VTABLE *vptr;
|
||||
uint8_t dummy[0x9F0];
|
||||
};
|
||||
|
||||
// confirmed 0x9F8 in iidx27, iidx32 aioNMgrIob2_Create
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0x9F8);
|
||||
|
||||
struct AIO_IOB2_BI2X_TDJ {
|
||||
// who knows
|
||||
uint8_t data[0x13F8];
|
||||
};
|
||||
|
||||
// confirmed 0x13F8 in iidx27, iidx32 in aioIob2Bi2xTDJ_Create
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_TDJ) == 0x13F8);
|
||||
|
||||
struct AIO_IOB2_BI2X_TDJ__DEVSTATUS {
|
||||
// of course you could work with variables here
|
||||
uint8_t buffer[0xCA];
|
||||
@@ -78,7 +91,7 @@ namespace games::iidx {
|
||||
// libaio-iob.dll
|
||||
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
||||
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
||||
typedef AIO_NMGR_IOB2** (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
||||
typedef AIO_NMGR_IOB2* (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
||||
|
||||
/*
|
||||
* function pointers
|
||||
@@ -120,6 +133,11 @@ namespace games::iidx {
|
||||
|
||||
static AIO_IOB2_BI2X_TDJ* __fastcall aioIob2Bi2xTDJ_Create(AIO_NMGR_IOB2 *nmgr, int a2) {
|
||||
if (!BI2X_PASSTHROUGH) {
|
||||
|
||||
log_info("bi2x_hook", "aioIob2Bi2xTDJ_Create called (TDJ I/O emulation active)");
|
||||
games::iidx::update_io_emulation_state(
|
||||
games::iidx::iidx_aio_emulation_state::bi2x_hook);
|
||||
|
||||
custom_node = new AIO_IOB2_BI2X_TDJ;
|
||||
memset(&custom_node->data, 0, sizeof(custom_node->data));
|
||||
|
||||
@@ -438,7 +456,7 @@ namespace games::iidx {
|
||||
}
|
||||
}
|
||||
|
||||
static AC_HNDLIF* __fastcall aioIob2Bi2X_OpenSciUsbCdc(uint8_t device_num) {
|
||||
static AC_HNDLIF* __fastcall aioIob2Bi2x_OpenSciUsbCdc(uint8_t device_num) {
|
||||
if (acHndlif == nullptr) {
|
||||
acHndlif = new AC_HNDLIF;
|
||||
memset(acHndlif->data, 0x0, sizeof(acHndlif->data));
|
||||
@@ -451,14 +469,16 @@ namespace games::iidx {
|
||||
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
||||
};
|
||||
|
||||
static AIO_NMGR_IOB2** __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
||||
static AIO_NMGR_IOB2* __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
||||
if (aioNmgrIob2 == nullptr) {
|
||||
aioNmgrIob2 = new AIO_NMGR_IOB2;
|
||||
memset(aioNmgrIob2, 0x0, sizeof(AIO_NMGR_IOB2));
|
||||
aioNmgrIob2->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||
aioNmgrIob2 = new AIO_NMGR_IOB2{};
|
||||
aioNmgrIob2->vptr = new AIO_NMGR_IOB2_VTABLE{};
|
||||
aioNmgrIob2->vptr->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||
}
|
||||
log_info("bi2x_hook", "aioNMgrIob2_Create");
|
||||
return &aioNmgrIob2;
|
||||
log_info("bi2x_hook", "aioNMgrIob2_Create returned {}, size=0x{:x}, vptr @ {}",
|
||||
fmt::ptr(aioNmgrIob2), sizeof(*aioNmgrIob2), fmt::ptr(aioNmgrIob2->vptr));
|
||||
|
||||
return aioNmgrIob2;
|
||||
}
|
||||
|
||||
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
||||
@@ -515,10 +535,12 @@ namespace games::iidx {
|
||||
// hook IOB
|
||||
const auto libaioIobDll = "libaio-iob.dll";
|
||||
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_OpenSciUsbCdc",
|
||||
aioIob2Bi2X_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
|
||||
aioIob2Bi2x_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
|
||||
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_WriteFirmGetState",
|
||||
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
||||
detour::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
|
||||
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::iidx {
|
||||
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "hooks/sleephook.h"
|
||||
#include "launcher/options.h"
|
||||
#include "touch/touch.h"
|
||||
#include "misc/nativetouchhook.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/detour.h"
|
||||
@@ -28,6 +29,8 @@
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "launcher/signal.h"
|
||||
|
||||
@@ -78,6 +81,11 @@ namespace games::iidx {
|
||||
char IIDXIO_LED_TICKER[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\x00'};
|
||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||
std::mutex IIDX_LED_TICKER_LOCK;
|
||||
bool IIDX_TDJ_MONITOR_WARNING = false;
|
||||
iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE = iidx_aio_emulation_state::unknown;
|
||||
|
||||
// io
|
||||
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
||||
|
||||
tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL] = {
|
||||
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" },
|
||||
@@ -246,6 +254,8 @@ namespace games::iidx {
|
||||
}
|
||||
}
|
||||
|
||||
#if SPICE64
|
||||
|
||||
typedef void* (*aioIob2Bi2x_CreateWriteFirmContext_t)(unsigned int, int);
|
||||
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
|
||||
|
||||
@@ -264,6 +274,8 @@ namespace games::iidx {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
IIDXGame::IIDXGame() : Game("Beatmania IIDX") {
|
||||
logger::hook_add(log_hook, this);
|
||||
}
|
||||
@@ -310,13 +322,16 @@ namespace games::iidx {
|
||||
memcpy(settings2.interface_detail, interface_detail2, sizeof(interface_detail2));
|
||||
setupapihook_add(settings2);
|
||||
|
||||
// IIDX 25-27 BIO2 BI2A input device
|
||||
// IIDX 25+ BIO2 BI2A input device
|
||||
devicehook_add(new IIDXFMSerialHandle());
|
||||
}
|
||||
|
||||
#if SPICE64
|
||||
|
||||
// check for new I/O DLL
|
||||
auto aio = libutils::try_library("libaio.dll");
|
||||
if (aio != nullptr) {
|
||||
HAS_LIBAIO = true;
|
||||
|
||||
// check TDJ mode
|
||||
TDJ_MODE |= fileutils::text_read("C:\\000rom.txt") == "TDJ-JA";
|
||||
@@ -332,7 +347,9 @@ namespace games::iidx {
|
||||
// need to hook `avs2-core.dll` so AVS win32fs operations go through rom hook
|
||||
devicehook_init(avs::core::DLL_INSTANCE);
|
||||
|
||||
if (!NATIVE_TOUCH) {
|
||||
if (NATIVE_TOUCH) {
|
||||
nativetouchhook::hook(avs::game::DLL_INSTANCE);
|
||||
} else {
|
||||
wintouchemu::FORCE = true;
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
|
||||
@@ -376,6 +393,8 @@ namespace games::iidx {
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (hooks::audio::ENABLED) {
|
||||
apply_audio_hacks();
|
||||
} else {
|
||||
@@ -435,7 +454,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
// windowed subscreen, enabled by default, unless turned off by user
|
||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
|
||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool() && TDJ_MODE) {
|
||||
GRAPHICS_IIDX_WSUB = true;
|
||||
}
|
||||
|
||||
@@ -454,7 +473,7 @@ namespace games::iidx {
|
||||
"!!! please do the following instead: !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! Revert your changes to XML file so it says !!!\n"
|
||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
||||
"!!! in command line !!!\n"
|
||||
@@ -468,9 +487,8 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
// check -monitor + TDJ mode
|
||||
if (!GRAPHICS_WINDOWED &&
|
||||
options->at(launcher::Options::DisplayAdapter).is_active() &&
|
||||
TDJ_MODE) {
|
||||
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && TDJ_MODE) {
|
||||
IIDX_TDJ_MONITOR_WARNING = true;
|
||||
log_warning(
|
||||
"iidx",
|
||||
"\n\n!!! using -monitor option with TDJ is NOT recommended due to known !!!\n"
|
||||
@@ -492,6 +510,16 @@ namespace games::iidx {
|
||||
" monitor in Windows settings before launching the game"
|
||||
});
|
||||
}
|
||||
|
||||
// socd
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::Neutral;
|
||||
if (options->at(launcher::Options::IIDXDigitalTTSocd).is_active()) {
|
||||
if (options->at(launcher::Options::IIDXDigitalTTSocd).value_text() == "last") {
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||
} else if (options->at(launcher::Options::IIDXDigitalTTSocd).value_text() == "first") {
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferFirst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IIDXGame::detach() {
|
||||
@@ -715,13 +743,12 @@ namespace games::iidx {
|
||||
bool ttpm_alt = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
||||
player != 0 ? Buttons::P2_TTPlusMinusAlt : Buttons::P1_TTPlusMinusAlt));
|
||||
|
||||
// TT+
|
||||
if (ttp)
|
||||
const auto tt_socd = socd::socd_clean(player, ttm, ttp, get_performance_milliseconds());
|
||||
if (tt_socd == socd::SocdCW) {
|
||||
IIDXIO_TT_STATE[player] += change;
|
||||
|
||||
// TT-
|
||||
if (ttm)
|
||||
} else if (tt_socd == socd::SocdCCW) {
|
||||
IIDXIO_TT_STATE[player] -= change;
|
||||
}
|
||||
|
||||
// TT+/-
|
||||
bool ttpm_rising_edge = !IIDXIO_TT_PRESSED[player] && ttpm;
|
||||
@@ -791,7 +818,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
bool is_tdj_fhd() {
|
||||
return TDJ_MODE && avs::game::is_ext(2022101900, MAXINT);
|
||||
return TDJ_MODE && avs::game::is_ext(2022101900, INT_MAX);
|
||||
}
|
||||
|
||||
void apply_audio_hacks() {
|
||||
@@ -857,12 +884,12 @@ namespace games::iidx {
|
||||
|
||||
// patch iidx32+ for asio compatibility
|
||||
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
||||
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
||||
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
||||
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
||||
if (avs::game::is_ext(2024090100, MAXINT) &&
|
||||
if (avs::game::is_ext(2024090100, INT_MAX) &&
|
||||
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
||||
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
||||
|
||||
|
||||
// in iidx32 final:
|
||||
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
||||
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
|
||||
@@ -892,4 +919,54 @@ namespace games::iidx {
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void update_io_emulation_state(iidx_aio_emulation_state state) {
|
||||
// <iidx27, don't care, since tdj wasn't a thing before
|
||||
// this is also how we detect that iidx module is attached
|
||||
if (!HAS_LIBAIO) {
|
||||
return;
|
||||
}
|
||||
|
||||
// in iidx27-30, most DLL types can handle both, caching things in nvram and possibly end
|
||||
// up with wrong i/o (temporarily until nvram is initialized again)
|
||||
// to avoid false positives, only do this for iidx31+, since we know for sure that iidx31
|
||||
// has a clean 010/012 split
|
||||
if (avs::game::is_ext(0, 2023101000)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CURRENT_IO_EMULATION_STATE = state;
|
||||
|
||||
// log_warning below could be turned into log_fatal in the future once we gain confidence
|
||||
// that this isn't triggering when it's not supposed to
|
||||
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
||||
deferredlogs::defer_error_messages({
|
||||
"IIDX I/O mode conflict",
|
||||
" game is currently configured to use LDJ I/O;",
|
||||
" however, you turned on IIDX TDJ Mode (-iidxtdj)",
|
||||
" two options to fix this:",
|
||||
" For TDJ mode (120Hz): import patches and enable Force TDJ Mode patch",
|
||||
" For LDJ mode (60Hz): turn off IIDX TDJ Mode option",
|
||||
});
|
||||
log_warning(
|
||||
"iidx",
|
||||
"game is using LDJ I/O but spice TDJ mode is turned on! "
|
||||
"enable Force TDJ I/O patch, OR turn off -iidxtdj");
|
||||
} else if (state == iidx_aio_emulation_state::bi2x_hook && !TDJ_MODE) {
|
||||
deferredlogs::defer_error_messages({
|
||||
"IIDX I/O mode conflict",
|
||||
" game is currently configured to use TDJ I/O;",
|
||||
" however, you turned off IIDX TDJ Mode (-iidxtdj)",
|
||||
" two options to fix this:",
|
||||
" For TDJ mode (120Hz): turn on IIDX TDJ Mode option",
|
||||
" For LDJ mode (60Hz): import patches and enable Force LDJ Mode patch",
|
||||
});
|
||||
log_warning(
|
||||
"iidx",
|
||||
"game is using TDJ I/O but spice TDJ mode is turned off! "
|
||||
"turn on -iidxtdj, OR enable Force LDJ I/O patch");
|
||||
} else {
|
||||
log_misc("iidx", "i/o mode validation passed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
namespace games::iidx {
|
||||
|
||||
enum class iidx_aio_emulation_state {
|
||||
unknown,
|
||||
bi2a_com2,
|
||||
bi2x_hook
|
||||
};
|
||||
|
||||
// settings
|
||||
|
||||
extern bool FLIP_CAMS;
|
||||
@@ -33,10 +39,12 @@ namespace games::iidx {
|
||||
extern char IIDXIO_LED_TICKER[10];
|
||||
extern bool IIDXIO_LED_TICKER_READONLY;
|
||||
extern std::mutex IIDX_LED_TICKER_LOCK;
|
||||
extern bool IIDX_TDJ_MONITOR_WARNING;
|
||||
|
||||
constexpr int IIDX_TAPELED_TOTAL = 17;
|
||||
// data mapping
|
||||
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
||||
extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE;
|
||||
|
||||
class IIDXGame : public games::Game {
|
||||
public:
|
||||
@@ -62,4 +70,6 @@ namespace games::iidx {
|
||||
const char* get_16seg();
|
||||
bool is_tdj_fhd();
|
||||
void apply_audio_hacks();
|
||||
|
||||
void update_io_emulation_state(iidx_aio_emulation_state state);
|
||||
}
|
||||
|
||||
@@ -83,103 +83,105 @@ std::vector<Light> &games::iidx::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
GameAPI::Lights::sortLightsWithCategory(
|
||||
&lights,
|
||||
"P1 1",
|
||||
"P1 2",
|
||||
"P1 3",
|
||||
"P1 4",
|
||||
"P1 5",
|
||||
"P1 6",
|
||||
"P1 7",
|
||||
"P2 1",
|
||||
"P2 2",
|
||||
"P2 3",
|
||||
"P2 4",
|
||||
"P2 5",
|
||||
"P2 6",
|
||||
"P2 7",
|
||||
"P1 Start",
|
||||
"P2 Start",
|
||||
"VEFX",
|
||||
"Effect",
|
||||
"Spot Light 1",
|
||||
"Spot Light 2",
|
||||
"Spot Light 3",
|
||||
"Spot Light 4",
|
||||
"Spot Light 5",
|
||||
"Spot Light 6",
|
||||
"Spot Light 7",
|
||||
"Spot Light 8",
|
||||
"Neon Lamp",
|
||||
"Woofer R",
|
||||
"Woofer G",
|
||||
"Woofer B",
|
||||
"IC Card Reader P1 R",
|
||||
"IC Card Reader P1 G",
|
||||
"IC Card Reader P1 B",
|
||||
"IC Card Reader P2 R",
|
||||
"IC Card Reader P2 G",
|
||||
"IC Card Reader P2 B",
|
||||
"Turntable P1 R",
|
||||
"Turntable P1 G",
|
||||
"Turntable P1 B",
|
||||
"Turntable P2 R",
|
||||
"Turntable P2 G",
|
||||
"Turntable P2 B",
|
||||
"Turntable P1 Resistance",
|
||||
"Turntable P2 Resistance",
|
||||
"Stage Left Avg R",
|
||||
"Stage Left Avg G",
|
||||
"Stage Left Avg B",
|
||||
"Stage Right Avg R",
|
||||
"Stage Right Avg G",
|
||||
"Stage Right Avg B",
|
||||
"Cabinet Left Avg R",
|
||||
"Cabinet Left Avg G",
|
||||
"Cabinet Left Avg B",
|
||||
"Cabinet Right Avg R",
|
||||
"Cabinet Right Avg G",
|
||||
"Cabinet Right Avg B",
|
||||
"Control Panel Under Avg R",
|
||||
"Control Panel Under Avg G",
|
||||
"Control Panel Under Avg B",
|
||||
"Ceiling Left Avg R",
|
||||
"Ceiling Left Avg G",
|
||||
"Ceiling Left Avg B",
|
||||
"Title Left Avg R",
|
||||
"Title Left Avg G",
|
||||
"Title Left Avg B",
|
||||
"Title Right Avg R",
|
||||
"Title Right Avg G",
|
||||
"Title Right Avg B",
|
||||
"Ceiling Right Avg R",
|
||||
"Ceiling Right Avg G",
|
||||
"Ceiling Right Avg B",
|
||||
"Touch Panel Left Avg R",
|
||||
"Touch Panel Left Avg G",
|
||||
"Touch Panel Left Avg B",
|
||||
"Touch Panel Right Avg R",
|
||||
"Touch Panel Right Avg G",
|
||||
"Touch Panel Right Avg B",
|
||||
"Side Panel Left Inner Avg R",
|
||||
"Side Panel Left Inner Avg G",
|
||||
"Side Panel Left Inner Avg B",
|
||||
"Side Panel Left Outer Avg R",
|
||||
"Side Panel Left Outer Avg G",
|
||||
"Side Panel Left Outer Avg B",
|
||||
"Side Panel Left Avg R",
|
||||
"Side Panel Left Avg G",
|
||||
"Side Panel Left Avg B",
|
||||
"Side Panel Right Outer Avg R",
|
||||
"Side Panel Right Outer Avg G",
|
||||
"Side Panel Right Outer Avg B",
|
||||
"Side Panel Right Inner Avg R",
|
||||
"Side Panel Right Inner Avg G",
|
||||
"Side Panel Right Inner Avg B",
|
||||
"Side Panel Right Avg R",
|
||||
"Side Panel Right Avg G",
|
||||
"Side Panel Right Avg B"
|
||||
{
|
||||
{"Common", "P1 1"},
|
||||
{"Common", "P1 2"},
|
||||
{"Common", "P1 3"},
|
||||
{"Common", "P1 4"},
|
||||
{"Common", "P1 5"},
|
||||
{"Common", "P1 6"},
|
||||
{"Common", "P1 7"},
|
||||
{"Common", "P2 1"},
|
||||
{"Common", "P2 2"},
|
||||
{"Common", "P2 3"},
|
||||
{"Common", "P2 4"},
|
||||
{"Common", "P2 5"},
|
||||
{"Common", "P2 6"},
|
||||
{"Common", "P2 7"},
|
||||
{"Common", "P1 Start"},
|
||||
{"Common", "P2 Start"},
|
||||
{"Common", "VEFX"},
|
||||
{"Common", "Effect"},
|
||||
{"DX", "Spot Light 1"},
|
||||
{"DX", "Spot Light 2"},
|
||||
{"DX", "Spot Light 3"},
|
||||
{"DX", "Spot Light 4"},
|
||||
{"DX", "Spot Light 5"},
|
||||
{"DX", "Spot Light 6"},
|
||||
{"DX", "Spot Light 7"},
|
||||
{"DX", "Spot Light 8"},
|
||||
{"DX", "Neon Lamp"},
|
||||
{"Lightning Model", "Woofer R"},
|
||||
{"Lightning Model", "Woofer G"},
|
||||
{"Lightning Model", "Woofer B"},
|
||||
{"Lightning Model", "IC Card Reader P1 R"},
|
||||
{"Lightning Model", "IC Card Reader P1 G"},
|
||||
{"Lightning Model", "IC Card Reader P1 B"},
|
||||
{"Lightning Model", "IC Card Reader P2 R"},
|
||||
{"Lightning Model", "IC Card Reader P2 G"},
|
||||
{"Lightning Model", "IC Card Reader P2 B"},
|
||||
{"Lightning Model", "Turntable P1 R"},
|
||||
{"Lightning Model", "Turntable P1 G"},
|
||||
{"Lightning Model", "Turntable P1 B"},
|
||||
{"Lightning Model", "Turntable P2 R"},
|
||||
{"Lightning Model", "Turntable P2 G"},
|
||||
{"Lightning Model", "Turntable P2 B"},
|
||||
{"Lightning Model", "Turntable P1 Resistance"},
|
||||
{"Lightning Model", "Turntable P2 Resistance"},
|
||||
{"Lightning Model", "Stage Left Avg R"},
|
||||
{"Lightning Model", "Stage Left Avg G"},
|
||||
{"Lightning Model", "Stage Left Avg B"},
|
||||
{"Lightning Model", "Stage Right Avg R"},
|
||||
{"Lightning Model", "Stage Right Avg G"},
|
||||
{"Lightning Model", "Stage Right Avg B"},
|
||||
{"Lightning Model", "Cabinet Left Avg R"},
|
||||
{"Lightning Model", "Cabinet Left Avg G"},
|
||||
{"Lightning Model", "Cabinet Left Avg B"},
|
||||
{"Lightning Model", "Cabinet Right Avg R"},
|
||||
{"Lightning Model", "Cabinet Right Avg G"},
|
||||
{"Lightning Model", "Cabinet Right Avg B"},
|
||||
{"Lightning Model", "Control Panel Under Avg R"},
|
||||
{"Lightning Model", "Control Panel Under Avg G"},
|
||||
{"Lightning Model", "Control Panel Under Avg B"},
|
||||
{"Lightning Model", "Ceiling Left Avg R"},
|
||||
{"Lightning Model", "Ceiling Left Avg G"},
|
||||
{"Lightning Model", "Ceiling Left Avg B"},
|
||||
{"Lightning Model", "Title Left Avg R"},
|
||||
{"Lightning Model", "Title Left Avg G"},
|
||||
{"Lightning Model", "Title Left Avg B"},
|
||||
{"Lightning Model", "Title Right Avg R"},
|
||||
{"Lightning Model", "Title Right Avg G"},
|
||||
{"Lightning Model", "Title Right Avg B"},
|
||||
{"Lightning Model", "Ceiling Right Avg R"},
|
||||
{"Lightning Model", "Ceiling Right Avg G"},
|
||||
{"Lightning Model", "Ceiling Right Avg B"},
|
||||
{"Lightning Model", "Touch Panel Left Avg R"},
|
||||
{"Lightning Model", "Touch Panel Left Avg G"},
|
||||
{"Lightning Model", "Touch Panel Left Avg B"},
|
||||
{"Lightning Model", "Touch Panel Right Avg R"},
|
||||
{"Lightning Model", "Touch Panel Right Avg G"},
|
||||
{"Lightning Model", "Touch Panel Right Avg B"},
|
||||
{"Lightning Model", "Side Panel Left Inner Avg R"},
|
||||
{"Lightning Model", "Side Panel Left Inner Avg G"},
|
||||
{"Lightning Model", "Side Panel Left Inner Avg B"},
|
||||
{"Lightning Model", "Side Panel Left Outer Avg R"},
|
||||
{"Lightning Model", "Side Panel Left Outer Avg G"},
|
||||
{"Lightning Model", "Side Panel Left Outer Avg B"},
|
||||
{"Lightning Model", "Side Panel Left Avg R"},
|
||||
{"Lightning Model", "Side Panel Left Avg G"},
|
||||
{"Lightning Model", "Side Panel Left Avg B"},
|
||||
{"Lightning Model", "Side Panel Right Outer Avg R"},
|
||||
{"Lightning Model", "Side Panel Right Outer Avg G"},
|
||||
{"Lightning Model", "Side Panel Right Outer Avg B"},
|
||||
{"Lightning Model", "Side Panel Right Inner Avg R"},
|
||||
{"Lightning Model", "Side Panel Right Inner Avg G"},
|
||||
{"Lightning Model", "Side Panel Right Inner Avg B"},
|
||||
{"Lightning Model", "Side Panel Right Avg R"},
|
||||
{"Lightning Model", "Side Panel Right Avg G"},
|
||||
{"Lightning Model", "Side Panel Right Avg B"}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace games::iidx {
|
||||
protected:
|
||||
virtual ~IIDXLocalCamera() {};
|
||||
|
||||
ULONG m_nRefCount;
|
||||
LONG m_nRefCount;
|
||||
CRITICAL_SECTION m_critsec;
|
||||
|
||||
std::string m_name;
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace games {
|
||||
static robin_hood::unordered_map<std::string, std::vector<Button>> buttons_keypads;
|
||||
static robin_hood::unordered_map<std::string, std::vector<Button>> buttons_overlay;
|
||||
static robin_hood::unordered_map<std::string, std::string> buttons_help;
|
||||
static robin_hood::unordered_map<std::string, std::string> analogs_help;
|
||||
static robin_hood::unordered_map<std::string, std::vector<Analog> &> analogs;
|
||||
static robin_hood::unordered_map<std::string, std::vector<Light> &> lights;
|
||||
static robin_hood::unordered_map<std::string, std::vector<Option>> options;
|
||||
@@ -107,6 +108,7 @@ namespace games {
|
||||
games.push_back(gitadora);
|
||||
buttons.insert({ gitadora, gitadora::get_buttons() });
|
||||
buttons_help.insert({ gitadora, gitadora::get_buttons_help() });
|
||||
analogs_help.insert({ gitadora, gitadora::get_analogs_help() });
|
||||
analogs.insert({ gitadora, gitadora::get_analogs() });
|
||||
lights.insert({ gitadora, gitadora::get_lights() });
|
||||
file_hints[gitadora].emplace_back("gdxg.dll");
|
||||
@@ -254,6 +256,7 @@ namespace games {
|
||||
const std::string otoca("Otoca D'or");
|
||||
games.push_back(otoca);
|
||||
buttons.insert({ otoca, otoca::get_buttons() });
|
||||
buttons_help.insert({ otoca, otoca::get_buttons_help() });
|
||||
file_hints[otoca].emplace_back("arkkep.dll");
|
||||
|
||||
// winning eleven
|
||||
@@ -322,7 +325,7 @@ namespace games {
|
||||
buttons_help.insert({ pc, pc::get_buttons_help() });
|
||||
analogs.insert({ pc, pc::get_analogs() });
|
||||
lights.insert({ pc, pc::get_lights() });
|
||||
file_hints[pc].emplace_back("game/svm_Data");
|
||||
file_hints[pc].emplace_back("game/svm.exe");
|
||||
}
|
||||
|
||||
const std::vector<std::string> &get_games() {
|
||||
@@ -349,6 +352,15 @@ namespace games {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string get_analogs_help(const std::string &game) {
|
||||
initialize();
|
||||
auto it = analogs_help.find(game);
|
||||
if (it == analogs_help.end()) {
|
||||
return "";
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static std::vector<Button> gen_buttons_keypads(const std::string &game) {
|
||||
auto buttons = GameAPI::Buttons::getButtons(game);
|
||||
std::vector<std::string> names;
|
||||
@@ -389,7 +401,8 @@ namespace games {
|
||||
vkey_defaults.push_back(unit == 0 ? VK_NUMPAD9 : 0xFF);
|
||||
|
||||
names.emplace_back(prefix + "00");
|
||||
vkey_defaults.push_back(unit == 0 ? VK_RETURN : 0xFF);
|
||||
// this used to be VK_RETURN, but that shares a value with normal Enter key
|
||||
vkey_defaults.push_back(unit == 0 ? VK_SUBTRACT : 0xFF);
|
||||
|
||||
names.emplace_back(prefix + "Decimal");
|
||||
vkey_defaults.push_back(unit == 0 ? VK_DECIMAL : 0xFF);
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace games {
|
||||
const std::vector<std::string> &get_games();
|
||||
std::vector<Button> *get_buttons(const std::string &game);
|
||||
std::string get_buttons_help(const std::string &game);
|
||||
std::string get_analogs_help(const std::string &game);
|
||||
std::vector<Button> *get_buttons_keypads(const std::string &game);
|
||||
std::vector<Button> *get_buttons_overlay(const std::string &game);
|
||||
std::vector<Analog> *get_analogs(const std::string &game);
|
||||
|
||||
@@ -23,6 +23,11 @@ std::vector<Button> &games::otoca::get_buttons() {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string games::otoca::get_buttons_help() {
|
||||
// keep to max 100 characters wide
|
||||
return " BT_Left Lever BT_Right";
|
||||
}
|
||||
|
||||
std::vector<Light> &games::otoca::get_lights() {
|
||||
static std::vector<Light> lights;
|
||||
|
||||
|
||||
@@ -30,5 +30,6 @@ namespace games::otoca {
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::string get_buttons_help();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,121 @@
|
||||
#include "otoca.h"
|
||||
#include "p4io.h"
|
||||
#include "games/shared/printer.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#define OTOCA_DEBUG_VERBOSE 0
|
||||
#if OTOCA_DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
namespace games::otoca {
|
||||
|
||||
bool BYPASS_CAMERA = false;
|
||||
|
||||
// for dev only
|
||||
static bool CAMLIB_CALL_ORIGINAL = false;
|
||||
|
||||
OtocaGame::OtocaGame() : Game("Otoca D'or") {
|
||||
}
|
||||
|
||||
OtocaGame::~OtocaGame() {
|
||||
}
|
||||
|
||||
typedef void (__cdecl *LibCameraInit_t)();
|
||||
static LibCameraInit_t LibCameraInit_orig = nullptr;
|
||||
static void __cdecl LibCameraInit_hook() {
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
LibCameraInit_orig();
|
||||
log_debug("otoca::cam", "LibCameraInit returned");
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (__cdecl *LibCameraOpen_t)(double, int, int);
|
||||
static LibCameraOpen_t LibCameraOpen_orig = nullptr;
|
||||
static int __cdecl LibCameraOpen_hook(double a1, int a2, int a3) {
|
||||
int ret = 0;
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
// with webcam, (15, 2, 0) returned 0
|
||||
ret = LibCameraOpen_orig(a1, a2, a3);
|
||||
log_debug("otoca::cam", "LibCameraOpen({}, {}, {}) returned {}", a1, a2, a3, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef int (__cdecl *LibCameraStop_t)(int);
|
||||
static LibCameraStop_t LibCameraStop_orig = nullptr;
|
||||
static int __cdecl LibCameraStop_hook(int a1) {
|
||||
int ret = 0;
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
// with webcam,
|
||||
ret = LibCameraStop_orig(a1);
|
||||
log_debug("otoca::cam", "LibCameraStop({}) returned {}", a1, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef void (__cdecl *LibCameraGetCameraNr_t)();
|
||||
static LibCameraGetCameraNr_t LibCameraGetCameraNr_orig = nullptr;
|
||||
static void __cdecl LibCameraGetCameraNr_hook() {
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
LibCameraGetCameraNr_orig();
|
||||
log_debug("otoca::cam", "LibCameraGetCameraNr returned");
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (__cdecl *LibCameraRun_t)(int);
|
||||
static LibCameraRun_t LibCameraRun_orig = nullptr;
|
||||
static int __cdecl LibCameraRun_hook(int a1) {
|
||||
int ret = 0;
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
// with webcam, (0) returned 0
|
||||
ret = LibCameraRun_orig(a1);
|
||||
log_debug("otoca::cam", "LibCameraRun({}) returned {}", a1, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef int (__cdecl *LibCameraGetImage_t)(int, void *);
|
||||
static LibCameraGetImage_t LibCameraGetImage_orig = nullptr;
|
||||
static int __cdecl LibCameraGetImage_hook(int a1, void *a2) {
|
||||
int ret = 0;
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
// not sure if this is called
|
||||
ret = LibCameraGetImage_orig(a1, a2);
|
||||
log_debug("otoca::cam", "LibCameraGetImage({}, {}) returned {}", a1, fmt::ptr(a2), ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef int (__cdecl *LibCameraGetBrightness_t)(int *, int);
|
||||
static LibCameraGetBrightness_t LibCameraGetBrightness_orig = nullptr;
|
||||
static int __cdecl LibCameraGetBrightness_hook(int *a1, int a2) {
|
||||
int ret = 0;
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
// with webcam, (ptr,) returned
|
||||
ret = LibCameraGetBrightness_orig(a1, a2);
|
||||
}
|
||||
log_debug("otoca::cam", "LibCameraGetBrightness({}, {}) returned {}", fmt::ptr(a1), a2, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef int (__cdecl *LibCameraGetBrightnessRange_t)(int *, int *, int);
|
||||
static LibCameraGetBrightnessRange_t LibCameraGetBrightnessRange_orig = nullptr;
|
||||
static int __cdecl LibCameraGetBrightnessRange_hook(int *a1, int *a2, int a3) {
|
||||
int ret = 0;
|
||||
if (CAMLIB_CALL_ORIGINAL) {
|
||||
// with webcam, (ptr, ptr, 0) returned 0
|
||||
ret = LibCameraGetBrightnessRange_orig(a1, a2, a3);
|
||||
log_debug("otoca::cam", "LibCameraGetBrightnessRange({}, {}, {}) returned {}", fmt::ptr(a1), fmt::ptr(a2), a3, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OtocaGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
@@ -23,5 +128,42 @@ namespace games::otoca {
|
||||
// initialize IO hooks
|
||||
p4io_hook();
|
||||
games::shared::printer_attach();
|
||||
|
||||
if (BYPASS_CAMERA) {
|
||||
libutils::try_library("libcamera.dll");
|
||||
const auto libcamera = "libcamera.dll";
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraInit@@YAXXZ",
|
||||
LibCameraInit_hook, &LibCameraInit_orig);
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraOpen@@YA?AW4LIBCAMERA_STATUS@@NHPAUt_libcamera_open_param@@@Z",
|
||||
LibCameraOpen_hook, &LibCameraOpen_orig);
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraStop@@YA?AW4LIBCAMERA_STATUS@@H@Z",
|
||||
LibCameraStop_hook, &LibCameraStop_orig);
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraGetCameraNr@@YAHXZ",
|
||||
LibCameraGetCameraNr_hook, &LibCameraGetCameraNr_orig);
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraRun@@YA?AW4LIBCAMERA_STATUS@@H@Z",
|
||||
LibCameraRun_hook, &LibCameraRun_orig);
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraGetImage@@YA?AW4LIBCAMERA_STATUS@@HPAX@Z",
|
||||
LibCameraGetImage_hook, &LibCameraGetImage_orig);
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraGetBrightness@@YA?AW4LIBCAMERA_STATUS@@PAJH@Z",
|
||||
LibCameraGetBrightness_hook, &LibCameraGetBrightness_orig);
|
||||
detour::trampoline_try(
|
||||
libcamera,
|
||||
"?LibCameraGetBrightnessRange@@YA?AW4LIBCAMERA_STATUS@@PAJ0H@Z",
|
||||
LibCameraGetBrightnessRange_hook, &LibCameraGetBrightnessRange_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
namespace games::otoca {
|
||||
|
||||
extern bool BYPASS_CAMERA;
|
||||
|
||||
class OtocaGame : public games::Game {
|
||||
public:
|
||||
OtocaGame();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include "util/execexe.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
@@ -9,9 +11,10 @@
|
||||
#include "util/tapeled.h"
|
||||
#include "pc.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/time.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
|
||||
namespace games::pc {
|
||||
bool PC_KNOB_MODE = false;
|
||||
|
||||
constexpr float RELATIVE_DECAY = 0.02f;
|
||||
struct FADER_RELATIVE {
|
||||
@@ -75,18 +78,22 @@ namespace games::pc {
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0xf8];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t data[0xe30];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
uint8_t data[0x4538];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__INPUT
|
||||
@@ -191,6 +198,12 @@ namespace games::pc {
|
||||
AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20];
|
||||
};
|
||||
|
||||
// verified with XIF-2024122300
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4538);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
@@ -338,14 +351,21 @@ namespace games::pc {
|
||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left])) {
|
||||
|
||||
const auto now = get_performance_milliseconds();
|
||||
|
||||
const auto fader_l_socd = socd::socd_clean(
|
||||
0, // Fader-L
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]),
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right]),
|
||||
now);
|
||||
|
||||
if (fader_l_socd == socd::SocdCCW) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
} else if (fader_l_socd == socd::SocdCW) {
|
||||
val = +1.0f;
|
||||
}
|
||||
|
||||
o_DevStatus->Input.CN15_7 = (val < 0.2f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_6 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_5 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||
@@ -363,14 +383,19 @@ namespace games::pc {
|
||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left])) {
|
||||
|
||||
const auto fader_r_socd = socd::socd_clean(
|
||||
1, // Fader-R
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]),
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right]),
|
||||
now);
|
||||
|
||||
if (fader_r_socd == socd::SocdCCW) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
} else if (fader_r_socd == socd::SocdCW) {
|
||||
val = +1.0f;
|
||||
}
|
||||
|
||||
o_DevStatus->Input.CN11_20 = (val < 0.2f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN11_19 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN9_10 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||
@@ -737,3 +762,5 @@ namespace games::pc {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::pc {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// QueryDisplayConfig
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "pc.h"
|
||||
|
||||
#include <format>
|
||||
@@ -12,10 +15,12 @@
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
|
||||
namespace games::pc {
|
||||
std::string PC_INJECT_ARGS = "";
|
||||
bool PC_NO_IO = false;
|
||||
bool PC_KNOB_MODE = false;
|
||||
|
||||
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||
static std::wstring portName = L"COM1";
|
||||
@@ -30,10 +35,10 @@ namespace games::pc {
|
||||
if (pRawInputDevices &&
|
||||
(uiNumDevices > 0) &&
|
||||
(pRawInputDevices[0].hwndTarget == RI_MGR->input_hwnd)) {
|
||||
|
||||
|
||||
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
|
||||
}
|
||||
|
||||
|
||||
// otherwise, it must be the game; prevent the game from registering for raw input
|
||||
// and hijacking WM_INPUT messages.
|
||||
SetLastError(0xDEADBEEF);
|
||||
@@ -56,11 +61,16 @@ namespace games::pc {
|
||||
}
|
||||
|
||||
LONG WINAPI QueryDisplayConfig_hook(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*, DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*) {
|
||||
// make unity fallback to EnumDisplaySettingsW as I don't
|
||||
// make unity fallback to EnumDisplaySettingsW as I don't
|
||||
// want to deal with this api which is way more complex
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void PCGame::pre_attach() {
|
||||
// SOCD
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||
}
|
||||
|
||||
void PCGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
@@ -78,17 +88,22 @@ namespace games::pc {
|
||||
execexe::load_library("libaio-iob2_video.dll");
|
||||
execexe::load_library("win10actlog.dll");
|
||||
|
||||
#if SPICE64
|
||||
|
||||
if (!PC_NO_IO) {
|
||||
bi2x_hook_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
const auto user32Dll = "user32.dll";
|
||||
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
|
||||
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
|
||||
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
||||
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
||||
QueryDisplayConfig_hook, &QueryDisplayConfig_orig);
|
||||
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
||||
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
||||
EnumDisplaySettingsW_hook, &EnumDisplaySettingsW_orig);
|
||||
|
||||
if (GRAPHICS_SHOW_CURSOR) {
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace games::pc {
|
||||
public:
|
||||
PCGame() : Game("Polaris Chord") {}
|
||||
|
||||
virtual void pre_attach() override;
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
@@ -15,15 +17,19 @@ namespace games::qks {
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0xf8];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB {
|
||||
uint8_t data[0xa00];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
uint8_t data[0x3908];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB__NODEINFO {
|
||||
@@ -174,6 +180,12 @@ namespace games::qks {
|
||||
AIO_IOB2_BI2X_AC1__SETTING Setting;
|
||||
};
|
||||
|
||||
// verified with UKS-001-2024030600
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x3908);
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||
static_assert(sizeof(AIO_NMGR_IOB) == 0xa00);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
@@ -602,4 +614,6 @@ namespace games::qks {
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace games::qks {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,9 +86,13 @@ namespace games::qks {
|
||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
|
||||
execexe_CreateFileA_hook,&execexe_CreateFileA_orig);
|
||||
|
||||
#if SPICE64
|
||||
|
||||
// insert BI2X hooks
|
||||
bi2x_hook_init();
|
||||
|
||||
#endif
|
||||
|
||||
// add card reader
|
||||
acioHandle = new acioemu::ACIOHandle(portName.c_str());
|
||||
devicehook_init_trampoline();
|
||||
|
||||
@@ -88,10 +88,10 @@ namespace games::qma {
|
||||
// create quiz directory
|
||||
static bool dirs_created = false;
|
||||
if (!dirs_created && _wcsnicmp(lpFileName, L"E:\\LMA", 6) == 0) {
|
||||
dirs_created = true;
|
||||
fileutils::dir_create_recursive("E:\\LMA\\quiz11");
|
||||
fileutils::dir_create_recursive("E:\\LMA\\quiz13");
|
||||
fileutils::dir_create_recursive("E:\\LMA\\quiz15");
|
||||
dirs_created = true;
|
||||
}
|
||||
|
||||
// return result
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <cstdint>
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
@@ -10,12 +12,22 @@
|
||||
#include "launcher/options.h"
|
||||
#include "io.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/tapeled.h"
|
||||
#include "util/time.h"
|
||||
#include "acioemu/icca.h"
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
namespace games::sdvx {
|
||||
constexpr bool BI2X_PASSTHROUGH = false;
|
||||
bool BI2X_INITIALIZED = false;
|
||||
|
||||
/*
|
||||
* class definitions
|
||||
@@ -26,20 +38,54 @@ namespace games::sdvx {
|
||||
uint8_t data[0x10];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t dummy0[0x50];
|
||||
struct AIO_NMGR_IOB2_VTABLE {
|
||||
// other functions here, but they never get called
|
||||
uint8_t dummy[0x50];
|
||||
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
||||
uint8_t dummy1[0x9A0];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
AIO_NMGR_IOB2_VTABLE *vptr;
|
||||
uint8_t dummy[0x9F0];
|
||||
};
|
||||
|
||||
// confirmed in EG final in aioNMgrIob2_Create
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0x9F8);
|
||||
|
||||
struct AIO_IOB2_BI2X_UFC {
|
||||
// who knows
|
||||
uint8_t data[0x13F8];
|
||||
uint8_t data[0x39D8];
|
||||
};
|
||||
|
||||
// confirmed in EG final in aioIob2Bi2xUFC_Create
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_UFC) == 0x39D8);
|
||||
|
||||
struct AIO_IOB2_BI2X_UFC__INPUT {
|
||||
uint8_t DevIoCounter;
|
||||
uint8_t bExIoAErr;
|
||||
uint8_t bExIoBErr;
|
||||
uint8_t bPcPowerOn;
|
||||
uint8_t bPcPowerCheck;
|
||||
uint8_t CoinCount;
|
||||
uint8_t bTest;
|
||||
uint8_t bService;
|
||||
uint8_t bCoinSw;
|
||||
uint8_t bCoinJam;
|
||||
uint8_t bHPDetect;
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_UFC__DEVSTATUS {
|
||||
// of course you could work with variables here
|
||||
uint8_t buffer[0x19E];
|
||||
uint8_t InputCounter;
|
||||
uint8_t OutputCounter;
|
||||
uint8_t IoResetCounter;
|
||||
uint8_t TapeLedCounter;
|
||||
uint8_t TapeLedRate[8];
|
||||
AIO_IOB2_BI2X_UFC__INPUT Input;
|
||||
uint8_t unk_1[289];
|
||||
uint16_t AnalogLeft;
|
||||
uint16_t AnalogRight;
|
||||
uint8_t Buttons;
|
||||
uint8_t unk_2[97];
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -66,7 +112,7 @@ namespace games::sdvx {
|
||||
// libaio-iob.dll
|
||||
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
||||
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
||||
typedef AIO_NMGR_IOB2** (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
||||
typedef AIO_NMGR_IOB2* (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
||||
|
||||
/*
|
||||
* function pointers
|
||||
@@ -133,8 +179,8 @@ namespace games::sdvx {
|
||||
AIO_IOB2_BI2X_UFC__GetDeviceStatus_orig(This, status);
|
||||
}
|
||||
|
||||
status->buffer[0] = count;
|
||||
status->buffer[12] = count;
|
||||
status->InputCounter = count;
|
||||
status->Input.DevIoCounter = count;
|
||||
count++;
|
||||
|
||||
// get buttons
|
||||
@@ -142,41 +188,51 @@ namespace games::sdvx {
|
||||
|
||||
// control buttons
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]))
|
||||
status->buffer[18] = 0x01;
|
||||
status->Input.bTest = 0x01;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]))
|
||||
status->buffer[19] = 0x01;
|
||||
status->Input.bService = 0x01;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]))
|
||||
status->buffer[20] = 0x01;
|
||||
status->Input.bCoinSw = 0x01;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Start]))
|
||||
status->buffer[316] |= 0x01;
|
||||
status->Buttons |= 0x01;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_A]))
|
||||
status->buffer[316] |= 0x02;
|
||||
status->Buttons |= 0x02;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_B]))
|
||||
status->buffer[316] |= 0x04;
|
||||
status->Buttons |= 0x04;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_C]))
|
||||
status->buffer[316] |= 0x08;
|
||||
status->Buttons |= 0x08;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BT_D]))
|
||||
status->buffer[316] |= 0x10;
|
||||
status->Buttons |= 0x10;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FX_L]))
|
||||
status->buffer[316] |= 0x20;
|
||||
status->Buttons |= 0x20;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FX_R]))
|
||||
status->buffer[316] |= 0x40;
|
||||
status->Buttons |= 0x40;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]))
|
||||
status->buffer[22] = 0x01;
|
||||
status->Input.bHPDetect = 0x01;
|
||||
|
||||
status->Input.CoinCount += eamuse_coin_get_stock();
|
||||
|
||||
const auto now = get_performance_milliseconds();
|
||||
|
||||
// volume left
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Left])) {
|
||||
const auto vol_l_state = socd::socd_clean(0,
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Left]),
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Right]),
|
||||
now);
|
||||
if (vol_l_state == socd::SocdCCW) {
|
||||
VOL_L -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_L_Right])) {
|
||||
} else if (vol_l_state == socd::SocdCW) {
|
||||
VOL_L += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||
}
|
||||
|
||||
// volume right
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Left])) {
|
||||
const auto vol_r_state = socd::socd_clean(1,
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Left]),
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Right]),
|
||||
now);
|
||||
if (vol_r_state == socd::SocdCCW) {
|
||||
VOL_R -= ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::VOL_R_Right])) {
|
||||
} else if (vol_r_state == socd::SocdCW) {
|
||||
VOL_R += ((uint16_t)DIGITAL_KNOB_SENS * 4);
|
||||
}
|
||||
|
||||
@@ -189,8 +245,14 @@ namespace games::sdvx {
|
||||
vol_right += (uint16_t) (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::VOL_R]) * 65535);
|
||||
}
|
||||
|
||||
*((uint16_t*) &status->buffer[312]) = vol_left;
|
||||
*((uint16_t*) &status->buffer[314]) = vol_right;
|
||||
status->AnalogLeft = vol_left;
|
||||
status->AnalogRight = vol_right;
|
||||
|
||||
log_debug(
|
||||
"bi2x_hook",
|
||||
"knobs = {} {}",
|
||||
status->AnalogLeft,
|
||||
status->AnalogRight);
|
||||
}
|
||||
|
||||
static void __fastcall AIO_IOB2_BI2X_UFC__IoReset(AIO_IOB2_BI2X_UFC *This,
|
||||
@@ -345,21 +407,21 @@ namespace games::sdvx {
|
||||
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
||||
}
|
||||
|
||||
static AIO_NMGR_IOB2** __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
||||
static AIO_NMGR_IOB2* __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
||||
if (aioNmgrIob2 == nullptr) {
|
||||
aioNmgrIob2 = new AIO_NMGR_IOB2;
|
||||
memset(aioNmgrIob2, 0x0, sizeof(AIO_NMGR_IOB2));
|
||||
aioNmgrIob2->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||
aioNmgrIob2 = new AIO_NMGR_IOB2{};
|
||||
aioNmgrIob2->vptr = new AIO_NMGR_IOB2_VTABLE{};
|
||||
aioNmgrIob2->vptr->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||
}
|
||||
log_info("bi2x_hook", "aioNMgrIob2_Create");
|
||||
BI2X_INITIALIZED = true;
|
||||
log_info("bi2x_hook", "aioNMgrIob2_Create returned {}, size=0x{:x}, vptr @ {}",
|
||||
fmt::ptr(aioNmgrIob2), sizeof(*aioNmgrIob2), fmt::ptr(aioNmgrIob2->vptr));
|
||||
|
||||
// enable hack to make PIN pad work for KFC in BI2X mode
|
||||
// this explicit check in the I/O init path is necessary
|
||||
// (as opposed to just doing a check for "isValkyrieCabMode?")
|
||||
// because there are hex edits that allow you to use legacy (KFC/BIO2) IO while in Valk mode
|
||||
acioemu::ICCA_DEVICE_HACK = true;
|
||||
return &aioNmgrIob2;
|
||||
return aioNmgrIob2;
|
||||
}
|
||||
|
||||
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
||||
@@ -411,3 +473,5 @@ namespace games::sdvx {
|
||||
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::sdvx {
|
||||
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -65,76 +65,78 @@ std::vector<Light> &games::sdvx::get_lights() {
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Sound Voltex");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
GameAPI::Lights::sortLightsWithCategory(
|
||||
&lights,
|
||||
"BT-A",
|
||||
"BT-B",
|
||||
"BT-C",
|
||||
"BT-D",
|
||||
"FX-L",
|
||||
"FX-R",
|
||||
"Start",
|
||||
"Wing Left Up R",
|
||||
"Wing Left Up G",
|
||||
"Wing Left Up B",
|
||||
"Wing Right Up R",
|
||||
"Wing Right Up G",
|
||||
"Wing Right Up B",
|
||||
"Wing Left Low R",
|
||||
"Wing Left Low G",
|
||||
"Wing Left Low B",
|
||||
"Wing Right Low R",
|
||||
"Wing Right Low G",
|
||||
"Wing Right Low B",
|
||||
"Woofer R",
|
||||
"Woofer G",
|
||||
"Woofer B",
|
||||
"Controller R",
|
||||
"Controller G",
|
||||
"Controller B",
|
||||
"Generator R",
|
||||
"Generator G",
|
||||
"Generator B",
|
||||
"Pop",
|
||||
"Title Left",
|
||||
"Title Right",
|
||||
"Volume Sound",
|
||||
"Volume Headphone",
|
||||
"Volume External",
|
||||
"Volume Woofer",
|
||||
"IC Card Reader R",
|
||||
"IC Card Reader G",
|
||||
"IC Card Reader B",
|
||||
"Title Avg R",
|
||||
"Title Avg G",
|
||||
"Title Avg B",
|
||||
"Upper Left Speaker Avg R",
|
||||
"Upper Left Speaker Avg G",
|
||||
"Upper Left Speaker Avg B",
|
||||
"Upper Right Speaker Avg R",
|
||||
"Upper Right Speaker Avg G",
|
||||
"Upper Right Speaker Avg B",
|
||||
"Left Wing Avg R",
|
||||
"Left Wing Avg G",
|
||||
"Left Wing Avg B",
|
||||
"Right Wing Avg R",
|
||||
"Right Wing Avg G",
|
||||
"Right Wing Avg B",
|
||||
"Lower Left Speaker Avg R",
|
||||
"Lower Left Speaker Avg G",
|
||||
"Lower Left Speaker Avg B",
|
||||
"Lower Right Speaker Avg R",
|
||||
"Lower Right Speaker Avg G",
|
||||
"Lower Right Speaker Avg B",
|
||||
"Control Panel Avg R",
|
||||
"Control Panel Avg G",
|
||||
"Control Panel Avg B",
|
||||
"Woofer Avg R",
|
||||
"Woofer Avg G",
|
||||
"Woofer Avg B",
|
||||
"V Unit Avg R",
|
||||
"V Unit Avg G",
|
||||
"V Unit Avg B"
|
||||
{
|
||||
{"Common", "BT-A"},
|
||||
{"Common", "BT-B"},
|
||||
{"Common", "BT-C"},
|
||||
{"Common", "BT-D"},
|
||||
{"Common", "FX-L"},
|
||||
{"Common", "FX-R"},
|
||||
{"Common", "Start"},
|
||||
{"Nemsys", "Wing Left Up R"},
|
||||
{"Nemsys", "Wing Left Up G"},
|
||||
{"Nemsys", "Wing Left Up B"},
|
||||
{"Nemsys", "Wing Right Up R"},
|
||||
{"Nemsys", "Wing Right Up G"},
|
||||
{"Nemsys", "Wing Right Up B"},
|
||||
{"Nemsys", "Wing Left Low R"},
|
||||
{"Nemsys", "Wing Left Low G"},
|
||||
{"Nemsys", "Wing Left Low B"},
|
||||
{"Nemsys", "Wing Right Low R"},
|
||||
{"Nemsys", "Wing Right Low G"},
|
||||
{"Nemsys", "Wing Right Low B"},
|
||||
{"Nemsys", "Woofer R"},
|
||||
{"Nemsys", "Woofer G"},
|
||||
{"Nemsys", "Woofer B"},
|
||||
{"Nemsys", "Controller R"},
|
||||
{"Nemsys", "Controller G"},
|
||||
{"Nemsys", "Controller B"},
|
||||
{"Nemsys", "Generator R"},
|
||||
{"Nemsys", "Generator G"},
|
||||
{"Nemsys", "Generator B"},
|
||||
{"Nemsys", "Pop"},
|
||||
{"Nemsys", "Title Left"},
|
||||
{"Nemsys", "Title Right"},
|
||||
{"System", "Volume Sound"},
|
||||
{"System", "Volume Headphone"},
|
||||
{"System", "Volume External"},
|
||||
{"System", "Volume Woofer"},
|
||||
{"Valkyrie", "IC Card Reader R"},
|
||||
{"Valkyrie", "IC Card Reader G"},
|
||||
{"Valkyrie", "IC Card Reader B"},
|
||||
{"Valkyrie", "Title Avg R"},
|
||||
{"Valkyrie", "Title Avg G"},
|
||||
{"Valkyrie", "Title Avg B"},
|
||||
{"Valkyrie", "Upper Left Speaker Avg R"},
|
||||
{"Valkyrie", "Upper Left Speaker Avg G"},
|
||||
{"Valkyrie", "Upper Left Speaker Avg B"},
|
||||
{"Valkyrie", "Upper Right Speaker Avg R"},
|
||||
{"Valkyrie", "Upper Right Speaker Avg G"},
|
||||
{"Valkyrie", "Upper Right Speaker Avg B"},
|
||||
{"Valkyrie", "Left Wing Avg R"},
|
||||
{"Valkyrie", "Left Wing Avg G"},
|
||||
{"Valkyrie", "Left Wing Avg B"},
|
||||
{"Valkyrie", "Right Wing Avg R"},
|
||||
{"Valkyrie", "Right Wing Avg G"},
|
||||
{"Valkyrie", "Right Wing Avg B"},
|
||||
{"Valkyrie", "Lower Left Speaker Avg R"},
|
||||
{"Valkyrie", "Lower Left Speaker Avg G"},
|
||||
{"Valkyrie", "Lower Left Speaker Avg B"},
|
||||
{"Valkyrie", "Lower Right Speaker Avg R"},
|
||||
{"Valkyrie", "Lower Right Speaker Avg G"},
|
||||
{"Valkyrie", "Lower Right Speaker Avg B"},
|
||||
{"Valkyrie", "Control Panel Avg R"},
|
||||
{"Valkyrie", "Control Panel Avg G"},
|
||||
{"Valkyrie", "Control Panel Avg B"},
|
||||
{"Valkyrie", "Woofer Avg R"},
|
||||
{"Valkyrie", "Woofer Avg G"},
|
||||
{"Valkyrie", "Woofer Avg B"},
|
||||
{"Valkyrie", "V Unit Avg R"},
|
||||
{"Valkyrie", "V Unit Avg G"},
|
||||
{"Valkyrie", "V Unit Avg B"}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
#include "games/io.h"
|
||||
#include "hooks/audio/audio.h"
|
||||
@@ -19,9 +18,12 @@
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/libutils.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/nativetouchhook.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "bi2x_hook.h"
|
||||
#include "camera.h"
|
||||
#include "io.h"
|
||||
@@ -48,21 +50,27 @@ namespace games::sdvx {
|
||||
SdvxOverlayPosition OVERLAY_POS = SDVX_OVERLAY_BOTTOM;
|
||||
bool ENABLE_COM_PORT_SCAN_HOOK = false;
|
||||
|
||||
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
||||
bool USE_ASIO = false;
|
||||
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
||||
|
||||
// states
|
||||
bool SHOW_VM_MONITOR_WARNING = false;
|
||||
static HKEY real_asio_reg_handle = nullptr;
|
||||
static HKEY real_asio_device_reg_handle = nullptr;
|
||||
|
||||
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
|
||||
if (lpSubKey != nullptr && phkResult != nullptr) {
|
||||
if (hKey == HKEY_LOCAL_MACHINE &&
|
||||
ASIO_DRIVER.has_value() &&
|
||||
_stricmp(lpSubKey, "software\\asio") == 0)
|
||||
{
|
||||
*phkResult = PARENT_ASIO_REG_HANDLE;
|
||||
if (lpSubKey != nullptr &&
|
||||
phkResult != nullptr &&
|
||||
hKey == HKEY_LOCAL_MACHINE &&
|
||||
_stricmp(lpSubKey, "software\\asio") == 0) {
|
||||
|
||||
// prevent the game from randomly picking an ASIO device
|
||||
if (!USE_ASIO) {
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
// convince the game to use our hook (so we can swap in the preferred device)
|
||||
if (ASIO_DRIVER.has_value()) {
|
||||
*phkResult = PARENT_ASIO_REG_HANDLE;
|
||||
return RegOpenKeyA_orig(hKey, lpSubKey, &real_asio_reg_handle);
|
||||
}
|
||||
}
|
||||
@@ -109,7 +117,7 @@ namespace games::sdvx {
|
||||
lpSubKey != nullptr && phkResult != nullptr &&
|
||||
_stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) {
|
||||
log_info("sdvx::io", "hook access to HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA");
|
||||
return 2; // ERROR_FILE_NOT_FOUND
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult);
|
||||
@@ -226,9 +234,56 @@ namespace games::sdvx {
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool logged_missing_layer_error = false;
|
||||
// this is raw SHIFT-JIS text for
|
||||
// W:BM2D: CreateLayer() 指定したレイヤーは存在しません
|
||||
if (!logged_missing_layer_error &&
|
||||
data.find(
|
||||
"\x57\x3A\x42\x4D\x32\x44\x3A\x20\x43\x72\x65\x61\x74\x65\x4C\x61\x79\x65\x72\x28\x29\x20"
|
||||
"\x8E\x77\x92\xE8\x82\xB5\x82\xBD\x83\x8C\x83\x43\x83\x84\x81\x5B\x82\xCD\x91\xB6\x8D\xDD"
|
||||
"\x82\xB5\x82\xDC\x82\xB9\x82\xF1") != std::string::npos) {
|
||||
|
||||
logged_missing_layer_error = true;
|
||||
deferredlogs::defer_error_messages({
|
||||
"sdvx game engine detected missing or corrupt file(s); raw SHIFT-JIS error was:",
|
||||
std::string(" ") + data,
|
||||
" this will almost certainly cause your game to crash",
|
||||
" this is often caused by mismatched game DLLs or missing resources from game updates",
|
||||
" double check the integrity of your game data and try again"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDVXGame::detect_sound_output_device() {
|
||||
USE_ASIO = false;
|
||||
// if the user specified a value for -sdvxasio, use asio
|
||||
if (ASIO_DRIVER.has_value()) {
|
||||
log_misc(
|
||||
"sdvx",
|
||||
"-sdvxasio is set to \"{}\"", ASIO_DRIVER.value());
|
||||
USE_ASIO = true;
|
||||
} else {
|
||||
// see if XONAR AE is present; if so, use that
|
||||
HKEY subkey;
|
||||
LSTATUS result;
|
||||
result = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\ASIO\\XONAR SOUND CARD(64)", &subkey);
|
||||
if (result == ERROR_SUCCESS) {
|
||||
RegCloseKey(subkey);
|
||||
USE_ASIO = true;
|
||||
log_misc(
|
||||
"sdvx",
|
||||
"found HKLM\\SOFTWARE\\ASIO\\XONAR SOUND CARD(64)");
|
||||
}
|
||||
}
|
||||
if (!USE_ASIO) {
|
||||
log_misc("sdvx", "asio not configured");
|
||||
}
|
||||
}
|
||||
|
||||
typedef void **(__fastcall *volume_set_t)(uint64_t, uint64_t, uint64_t);
|
||||
static volume_set_t volume_set_orig = nullptr;
|
||||
static void **__fastcall volume_set_hook(uint64_t vol_sound, uint64_t vol_woofer, uint64_t vol_headphone) {
|
||||
@@ -295,12 +350,9 @@ namespace games::sdvx {
|
||||
}
|
||||
|
||||
#ifdef SPICE64 // SDVX5+ specific code
|
||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
||||
auto options = games::get_options(eamuse_get_game());
|
||||
// check -monitor + UFC mode
|
||||
if (!GRAPHICS_WINDOWED &&
|
||||
options->at(launcher::Options::DisplayAdapter).is_active() &&
|
||||
isValkyrieCabinetMode) {
|
||||
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && is_valkyrie_model()) {
|
||||
SHOW_VM_MONITOR_WARNING = true;
|
||||
log_warning(
|
||||
"sdvx",
|
||||
"\n\n!!! using -monitor option with VM mode is NOT recommended due to !!!\n"
|
||||
@@ -324,16 +376,29 @@ namespace games::sdvx {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SPICE64
|
||||
this->detect_sound_output_device();
|
||||
#endif
|
||||
|
||||
// SOCD
|
||||
auto options = games::get_options(eamuse_get_game());
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||
if (options->at(launcher::Options::SDVXDigitalKnobSocd).is_active()) {
|
||||
if (options->at(launcher::Options::SDVXDigitalKnobSocd).value_text() == "neutral") {
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::Neutral;
|
||||
} else if (options->at(launcher::Options::SDVXDigitalKnobSocd).value_text() == "last") {
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDVXGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
#ifdef SPICE64 // SDVX5+ specific code
|
||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
||||
|
||||
// LCD handle
|
||||
if (!isValkyrieCabinetMode) {
|
||||
if (!is_valkyrie_model()) {
|
||||
devicehook_init();
|
||||
devicehook_add(new games::shared::LCDHandle());
|
||||
}
|
||||
@@ -372,11 +437,12 @@ namespace games::sdvx {
|
||||
nvapi_hook::initialize(avs::game::DLL_INSTANCE);
|
||||
}
|
||||
|
||||
// check for Valkyrie cabinet mode
|
||||
if (isValkyrieCabinetMode) {
|
||||
// hook touch window
|
||||
// in windowed mode, game can accept mouse input on the second screen
|
||||
if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||
if (is_valkyrie_model()) {
|
||||
if (NATIVETOUCH) {
|
||||
nativetouchhook::hook(avs::game::DLL_INSTANCE);
|
||||
} else if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||
// hook touch window
|
||||
// in windowed mode, game can accept mouse input on the second screen
|
||||
wintouchemu::FORCE = true;
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook_title_ends(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/game.h"
|
||||
|
||||
namespace games::sdvx {
|
||||
@@ -20,9 +21,18 @@ namespace games::sdvx {
|
||||
extern bool NATIVETOUCH;
|
||||
extern uint8_t DIGITAL_KNOB_SENS;
|
||||
extern std::optional<std::string> ASIO_DRIVER;
|
||||
extern bool BI2X_INITIALIZED;
|
||||
extern SdvxOverlayPosition OVERLAY_POS;
|
||||
|
||||
// states
|
||||
extern bool SHOW_VM_MONITOR_WARNING;
|
||||
|
||||
static inline bool is_valkyrie_model() {
|
||||
return (
|
||||
avs::game::is_model("KFC") &&
|
||||
(avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H')
|
||||
);
|
||||
}
|
||||
|
||||
class SDVXGame : public games::Game {
|
||||
public:
|
||||
SDVXGame();
|
||||
@@ -30,5 +40,7 @@ namespace games::sdvx {
|
||||
virtual void attach() override;
|
||||
virtual void post_attach() override;
|
||||
virtual void detach() override;
|
||||
private:
|
||||
void detect_sound_output_device();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace games::shared {
|
||||
image_data[index + 2] = tmp;
|
||||
}
|
||||
|
||||
if (avs::game::is_model({"KLP", "KFC"})) {
|
||||
if (avs::game::is_model({"KLP", "KFC", "NCG"})) {
|
||||
// rotate image clockwise
|
||||
log_misc("printer", "rotate clockwise...");
|
||||
auto rotated = new uint8_t[image_width * image_height * 3];
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace hooks::audio {
|
||||
bool USE_DUMMY = false;
|
||||
WAVEFORMATEXTENSIBLE FORMAT {};
|
||||
std::optional<Backend> BACKEND = std::nullopt;
|
||||
size_t ASIO_DRIVER_ID = 0;
|
||||
std::optional<size_t> ASIO_DRIVER_ID = std::nullopt;
|
||||
std::string ASIO_DRIVER_NAME = "";
|
||||
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
||||
|
||||
// private globals
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
@@ -20,7 +21,8 @@ namespace hooks::audio {
|
||||
extern bool USE_DUMMY;
|
||||
extern WAVEFORMATEXTENSIBLE FORMAT;
|
||||
extern std::optional<Backend> BACKEND;
|
||||
extern size_t ASIO_DRIVER_ID;
|
||||
extern std::optional<size_t> ASIO_DRIVER_ID;
|
||||
extern std::string ASIO_DRIVER_NAME;
|
||||
extern bool ASIO_FORCE_UNLOAD_ON_STOP;
|
||||
extern bool LOW_LATENCY_SHARED_WASAPI;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "hooks/audio/audio_private.h"
|
||||
#include "hooks/audio/backends/mmdevice/audio_endpoint_volume.h"
|
||||
#include "hooks/audio/backends/wasapi/audio_client.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#define PRINT_FAILED_RESULT(name, ret) \
|
||||
do { \
|
||||
@@ -56,6 +57,7 @@ ULONG STDMETHODCALLTYPE WrappedIMMDevice::Release() {
|
||||
ULONG refs = pReal != nullptr ? pReal->Release() : 0;
|
||||
|
||||
if (refs == 0) {
|
||||
log_misc("audio::mmdevice", "WrappedIMMDevice::Release");
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -69,7 +71,7 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
||||
PROPVARIANT *pActivationParams,
|
||||
void **ppInterface)
|
||||
{
|
||||
log_misc("audio::mmdevice", "WrappedIMMDevice::Activate");
|
||||
log_misc("audio::mmdevice", "WrappedIMMDevice::Activate {}", guid2s(iid));
|
||||
|
||||
// call original
|
||||
HRESULT ret = pReal->Activate(iid, dwClsCtx, pActivationParams, ppInterface);
|
||||
@@ -80,7 +82,10 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (iid == IID_IAudioClient) {
|
||||
// almost all games request IAudioClient
|
||||
// so far we have not seen any games request IAudioClient2
|
||||
// SDVX EG Final uses IID_IAudioClient3, but only when shared mode patch is on
|
||||
if (iid == IID_IAudioClient || iid == IID_IAudioClient3) {
|
||||
|
||||
// prevent initialization recursion when using some ASIO backends that proxy to DirectSound, WASAPI, or WDM
|
||||
// like ASIO4All or FlexASIO
|
||||
@@ -90,23 +95,18 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
||||
}
|
||||
std::lock_guard initialize_guard(hooks::audio::INITIALIZE_LOCK, std::adopt_lock);
|
||||
|
||||
auto client = reinterpret_cast<IAudioClient *>(*ppInterface);
|
||||
|
||||
// release old audio client if initialized
|
||||
if (hooks::audio::CLIENT) {
|
||||
hooks::audio::CLIENT->Release();
|
||||
}
|
||||
|
||||
/*
|
||||
ret = wrap_audio_client(pReal, dwClsCtx, pActivationParams, &client);
|
||||
if (FAILED(ret)) {
|
||||
return ret;
|
||||
IAudioClient *client = nullptr;
|
||||
if (iid == IID_IAudioClient) {
|
||||
client = wrap_audio_client(reinterpret_cast<IAudioClient *>(*ppInterface));
|
||||
} else { // IID_IAudioClient3
|
||||
client = wrap_audio_client3(reinterpret_cast<IAudioClient3 *>(*ppInterface));
|
||||
}
|
||||
*/
|
||||
client = wrap_audio_client(client);
|
||||
|
||||
*ppInterface = client;
|
||||
|
||||
// persist the audio client
|
||||
hooks::audio::CLIENT = client;
|
||||
hooks::audio::CLIENT->AddRef();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user