mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b930ad95f | |||
| 3c60f3966b | |||
| c59d399ab8 | |||
| f69e40fa26 | |||
| e9dcc5717e | |||
| 5bec3d5db7 | |||
| 6ec2b47345 | |||
| ebda16beae | |||
| c29a26f183 | |||
| 2014e9cf75 | |||
| 33f2e1d4b7 | |||
| eb037542b4 | |||
| 039b42ad53 | |||
| 33b4718744 | |||
| 71ba9b6b47 | |||
| 1957917270 | |||
| b9ccddf47d | |||
| cd0ba51b5a | |||
| 1f23d88c7a | |||
| 46e43ab09c | |||
| 96950b6b4e | |||
| 5f94ef4478 | |||
| c4193eddc5 | |||
| c8962a0e77 | |||
| b6a749e5db | |||
| 7dbbe9809e | |||
| 79a3e32d5d | |||
| c5a4e954f9 | |||
| 7b862768b0 | |||
| af8d8dae9f | |||
| d9d5823fdb | |||
| 82dda33800 | |||
| 3876e38636 | |||
| 6bc1357d6a | |||
| 37218e7fe0 | |||
| 678e11eade | |||
| f72313fe45 | |||
| 06374ef78a | |||
| bdde1ec6b8 | |||
| d2176fe4f0 | |||
| fb9eef904a | |||
| 48903998a5 | |||
| f2dca0265f | |||
| 1147ed9858 | |||
| 19d93f4ffd | |||
| 15bfffa1d3 | |||
| 18a9a47c33 | |||
| 6838e69ebe | |||
| 26b3b64794 | |||
| aadd2bdd59 | |||
| d0e24e31ca | |||
| 4fb7f20c7b | |||
| 6ddae70c5a | |||
| b38160d6c2 | |||
| 231c998cc0 | |||
| 127d31a85c | |||
| a8419bbaa0 | |||
| 0fc430ceb3 | |||
| e59a50b64c | |||
| 2042b629e9 | |||
| 440b9ed749 | |||
| 9936088286 | |||
| 4d8a6c1952 | |||
| af69cf9251 | |||
| 1d5550b1e8 | |||
| ec3f6639b7 | |||
| 727b99effd | |||
| 8afff3aaca | |||
| b281517429 | |||
| 54ec9f272e | |||
| a159691097 | |||
| ca70f7b19c | |||
| 7b4227d2a9 | |||
| f89efa2558 | |||
| 6f4824b648 | |||
| 45debeb5c9 | |||
| a01f452eba | |||
| e452734bc1 | |||
| d1779b93fa | |||
| fe9aac7e29 | |||
| f3d887ebcf | |||
| 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 |
+13
-3
@@ -1,8 +1,7 @@
|
|||||||
## Contributing
|
## Contributing
|
||||||
Baseline rules for patch submissions are as follows. Any patches violating the rules below will not be accepted.
|
Baseline rules for patch submissions are as follows. Any patches violating the rules below will not be accepted.
|
||||||
|
|
||||||
### New as of March 2025
|
To contribute, fork the repo (just the main branch), make changes in your fork, and contribute to upstream by opening a pull request to the main repo.
|
||||||
Pull requests in GitHub are open. You don't need to bother with patch file submissions anymore. To contribute, fork the repo (just the main branch), make changes in your fork, and contribute to upstream by opening a pull request to the main repo.
|
|
||||||
|
|
||||||
### Adding support for games
|
### Adding support for games
|
||||||
|
|
||||||
@@ -12,6 +11,14 @@ Pull requests in GitHub are open. You don't need to bother with patch file submi
|
|||||||
* For a new game that was never supported (not a new version of a game, but rather a new series): please wait 1 year after official AC release in Japan.
|
* For a new game that was never supported (not a new version of a game, but rather a new series): please wait 1 year after official AC release in Japan.
|
||||||
* For new version of an already supported game: proceed with caution and use generally-accepted community guidelines.
|
* For new version of an already supported game: proceed with caution and use generally-accepted community guidelines.
|
||||||
|
|
||||||
|
### Forbidden "features" that will never be accepted
|
||||||
|
|
||||||
|
* Performing decryption of encrypted files
|
||||||
|
* Including (bundling), distributing, or linking to game data, or making it easier to find/download game data
|
||||||
|
* Any features that [phone home](https://en.wikipedia.org/wiki/Phoning_home)
|
||||||
|
* This includes automatic updaters.
|
||||||
|
* Exception: we have allowed the "patch import from URL" feature, but this has only been done under very careful considerations and plenty of warnings to the user.
|
||||||
|
|
||||||
### Avoiding regressions
|
### Avoiding regressions
|
||||||
|
|
||||||
The biggest risk in making code changes to spice is the risk of regressions. There is a **lot** of shared code used by many different games, reaching back game versions that are more than a decade old. It is practically impossible to test all supported games and versions.
|
The biggest risk in making code changes to spice is the risk of regressions. There is a **lot** of shared code used by many different games, reaching back game versions that are more than a decade old. It is practically impossible to test all supported games and versions.
|
||||||
@@ -22,7 +29,7 @@ Therefore, when making code changes, please be extremely careful about containin
|
|||||||
|
|
||||||
#### Config file compatibility
|
#### Config file compatibility
|
||||||
|
|
||||||
Do not change the names of options, buttons binds, analogs, etc - since they are used as unique identifiers in config files. If they are changed, you will introduce an incompatibility with previous versions of config file.
|
Do not change the names of buttons binds, analogs, strings for command line parameters, etc - since they are used as unique identifiers in config files. If they are changed, you will introduce an incompatibility with previous versions of config file.
|
||||||
|
|
||||||
### Code quality requirements
|
### Code quality requirements
|
||||||
|
|
||||||
@@ -49,6 +56,9 @@ Watch out for legacy OS compatibility. Currently, the minimum support floor is W
|
|||||||
|
|
||||||
### Code style requirements
|
### Code style requirements
|
||||||
* Indents are four spaces.
|
* 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.
|
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
||||||
|
|
||||||
OK:
|
OK:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
> Feel free to remove this section after you have read it.
|
> Feel free to remove this section after you have read it.
|
||||||
|
|
||||||
## Link to GitHub Issue, if one exists
|
## Link to GitHub Issue or related Pull Request, if one exists
|
||||||
#0
|
#0
|
||||||
|
|
||||||
## Description of change
|
## Description of change
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|||||||
# RapidJSON does this
|
# RapidJSON does this
|
||||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||||
|
|
||||||
|
add_compile_options("-Wno-nontrivial-memcall") # imgui, rapidjson
|
||||||
|
add_compile_options("-Wno-deprecated-builtins") # robin_hood
|
||||||
|
add_compile_options("-Wno-deprecated-declarations") # rapidjson
|
||||||
|
add_compile_options("-Wno-inconsistent-missing-override") # headsocket
|
||||||
|
add_compile_options("-Wno-microsoft-exception-spec") # gcc/clang have different noexcept specifiers on d3d9 COM interfaces
|
||||||
|
|
||||||
# warnings
|
# warnings
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
||||||
|
|
||||||
@@ -118,6 +124,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static")
|
||||||
else()
|
else()
|
||||||
|
|
||||||
|
# warnings as errors
|
||||||
|
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
|
||||||
|
|
||||||
# warnings
|
# warnings
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # enable stuff
|
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 :)
|
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")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||||
|
|
||||||
# file prefix map for relative working directory
|
# file prefix map for relative working directory
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# release with debug info flags
|
# release with debug info flags
|
||||||
@@ -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/icon.rc PROPERTIES LANGUAGE RC)
|
||||||
set_source_files_properties(cfg/Win32D.rc PROPERTIES LANGUAGE RC)
|
set_source_files_properties(cfg/Win32D.rc PROPERTIES LANGUAGE RC)
|
||||||
set_source_files_properties(build/manifest64.rc PROPERTIES LANGUAGE RC)
|
set_source_files_properties(build/manifest64.rc PROPERTIES LANGUAGE RC)
|
||||||
|
set_source_files_properties(stubs/manifest.rc PROPERTIES LANGUAGE RC)
|
||||||
|
|
||||||
# sources
|
# sources
|
||||||
#########
|
#########
|
||||||
@@ -343,6 +353,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
cfg/configurator.cpp
|
cfg/configurator.cpp
|
||||||
cfg/configurator_wnd.cpp
|
cfg/configurator_wnd.cpp
|
||||||
cfg/screen_resize.cpp
|
cfg/screen_resize.cpp
|
||||||
|
cfg/controller_presets.cpp
|
||||||
|
|
||||||
# easrv
|
# easrv
|
||||||
easrv/easrv.cpp
|
easrv/easrv.cpp
|
||||||
@@ -372,6 +383,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/shared/twtouch.cpp
|
games/shared/twtouch.cpp
|
||||||
games/popn/popn.cpp
|
games/popn/popn.cpp
|
||||||
games/popn/io.cpp
|
games/popn/io.cpp
|
||||||
|
games/popn/bi3a_hook.cpp
|
||||||
games/bbc/bbc.cpp
|
games/bbc/bbc.cpp
|
||||||
games/bbc/io.cpp
|
games/bbc/io.cpp
|
||||||
games/hpm/hpm.cpp
|
games/hpm/hpm.cpp
|
||||||
@@ -398,6 +410,10 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/nost/poke.cpp
|
games/nost/poke.cpp
|
||||||
games/gitadora/gitadora.cpp
|
games/gitadora/gitadora.cpp
|
||||||
games/gitadora/io.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/mga.cpp
|
||||||
games/mga/io.cpp
|
games/mga/io.cpp
|
||||||
games/mga/gunio.cpp
|
games/mga/gunio.cpp
|
||||||
@@ -434,6 +450,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/scotto/io.cpp
|
games/scotto/io.cpp
|
||||||
games/drs/drs.cpp
|
games/drs/drs.cpp
|
||||||
games/drs/io.cpp
|
games/drs/io.cpp
|
||||||
|
games/drs/rgb_cam.cpp
|
||||||
games/we/we.cpp
|
games/we/we.cpp
|
||||||
games/we/io.cpp
|
games/we/io.cpp
|
||||||
games/we/touchpanel.cpp
|
games/we/touchpanel.cpp
|
||||||
@@ -470,10 +487,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
hooks/audio/acm.cpp
|
hooks/audio/acm.cpp
|
||||||
hooks/audio/audio.cpp
|
hooks/audio/audio.cpp
|
||||||
hooks/audio/buffer.cpp
|
hooks/audio/buffer.cpp
|
||||||
|
hooks/audio/mme.cpp
|
||||||
hooks/audio/util.cpp
|
hooks/audio/util.cpp
|
||||||
hooks/audio/backends/dsound/dsound_backend.cpp
|
hooks/audio/backends/dsound/dsound_backend.cpp
|
||||||
hooks/audio/backends/mmdevice/audio_endpoint_volume.cpp
|
hooks/audio/backends/mmdevice/audio_endpoint_volume.cpp
|
||||||
hooks/audio/backends/mmdevice/device.cpp
|
hooks/audio/backends/mmdevice/device.cpp
|
||||||
|
hooks/audio/backends/mmdevice/device_collection.cpp
|
||||||
hooks/audio/backends/mmdevice/device_enumerator.cpp
|
hooks/audio/backends/mmdevice/device_enumerator.cpp
|
||||||
hooks/audio/backends/wasapi/audio_client.cpp
|
hooks/audio/backends/wasapi/audio_client.cpp
|
||||||
hooks/audio/backends/wasapi/audio_render_client.cpp
|
hooks/audio/backends/wasapi/audio_render_client.cpp
|
||||||
@@ -504,6 +523,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
hooks/lang.cpp
|
hooks/lang.cpp
|
||||||
hooks/libraryhook.cpp
|
hooks/libraryhook.cpp
|
||||||
hooks/networkhook.cpp
|
hooks/networkhook.cpp
|
||||||
|
hooks/icmphook_net.cpp
|
||||||
|
hooks/icmphook_iphlpapi.cpp
|
||||||
hooks/powrprof.cpp
|
hooks/powrprof.cpp
|
||||||
#hooks/rom.cpp
|
#hooks/rom.cpp
|
||||||
hooks/setupapihook.cpp
|
hooks/setupapihook.cpp
|
||||||
@@ -530,6 +551,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
misc/sciunit.cpp
|
misc/sciunit.cpp
|
||||||
misc/sde.cpp
|
misc/sde.cpp
|
||||||
misc/wintouchemu.cpp
|
misc/wintouchemu.cpp
|
||||||
|
misc/ami2000.cpp
|
||||||
|
|
||||||
# nvapi
|
# nvapi
|
||||||
nvapi/nvapi.cpp
|
nvapi/nvapi.cpp
|
||||||
@@ -544,9 +566,11 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
overlay/windows/camera_control.cpp
|
overlay/windows/camera_control.cpp
|
||||||
overlay/windows/card_manager.cpp
|
overlay/windows/card_manager.cpp
|
||||||
overlay/windows/drs_dancefloor.cpp
|
overlay/windows/drs_dancefloor.cpp
|
||||||
|
overlay/windows/gfdm_sub.cpp
|
||||||
overlay/windows/screen_resize.cpp
|
overlay/windows/screen_resize.cpp
|
||||||
overlay/windows/sdvx_sub.cpp
|
overlay/windows/sdvx_sub.cpp
|
||||||
overlay/windows/config.cpp
|
overlay/windows/config.cpp
|
||||||
|
overlay/windows/controller_presets.cpp
|
||||||
overlay/windows/control.cpp
|
overlay/windows/control.cpp
|
||||||
overlay/windows/eadev.cpp
|
overlay/windows/eadev.cpp
|
||||||
overlay/windows/fps.cpp
|
overlay/windows/fps.cpp
|
||||||
@@ -561,6 +585,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
overlay/windows/log.cpp
|
overlay/windows/log.cpp
|
||||||
overlay/windows/midi.cpp
|
overlay/windows/midi.cpp
|
||||||
overlay/windows/patch_manager.cpp
|
overlay/windows/patch_manager.cpp
|
||||||
|
overlay/windows/popn_sub.cpp
|
||||||
overlay/windows/wnd_manager.cpp
|
overlay/windows/wnd_manager.cpp
|
||||||
|
|
||||||
# rawinput
|
# rawinput
|
||||||
@@ -575,6 +600,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
rawinput/smxstage.h
|
rawinput/smxstage.h
|
||||||
rawinput/smxdedicab.cpp
|
rawinput/smxdedicab.cpp
|
||||||
rawinput/smxdedicab.h
|
rawinput/smxdedicab.h
|
||||||
|
rawinput/xinput.cpp
|
||||||
|
rawinput/xinput.h
|
||||||
|
|
||||||
# reader
|
# reader
|
||||||
reader/reader.cpp
|
reader/reader.cpp
|
||||||
@@ -582,6 +609,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
reader/structuredmessage.cpp
|
reader/structuredmessage.cpp
|
||||||
reader/crypt.cpp
|
reader/crypt.cpp
|
||||||
|
|
||||||
|
# sdk
|
||||||
|
sdk/sdk.cpp
|
||||||
|
|
||||||
# stubs
|
# stubs
|
||||||
stubs/stubs.cpp
|
stubs/stubs.cpp
|
||||||
|
|
||||||
@@ -608,12 +638,14 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
util/time.cpp
|
util/time.cpp
|
||||||
util/cpuutils.cpp
|
util/cpuutils.cpp
|
||||||
util/netutils.cpp
|
util/netutils.cpp
|
||||||
|
util/precise_timer.cpp
|
||||||
util/sysutils.cpp
|
util/sysutils.cpp
|
||||||
util/lz77.cpp
|
util/lz77.cpp
|
||||||
util/tapeled.cpp
|
util/tapeled.cpp
|
||||||
util/execexe.cpp
|
util/execexe.cpp
|
||||||
util/dependencies.cpp
|
util/dependencies.cpp
|
||||||
util/deferlog.cpp
|
util/deferlog.cpp
|
||||||
|
util/socd_cleaner.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||||
@@ -792,7 +824,8 @@ endif()
|
|||||||
|
|
||||||
# nvcuda.dll
|
# nvcuda.dll
|
||||||
set(SOURCE_FILES stubs/nvcuda.cpp)
|
set(SOURCE_FILES stubs/nvcuda.cpp)
|
||||||
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} stubs/nvcuda.def)
|
set(RESOURCE_FILES stubs/manifest.rc)
|
||||||
|
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuda.def)
|
||||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES PREFIX "")
|
set_target_properties(spicetools_stubs_nvcuda PROPERTIES PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
|
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
|
||||||
|
|
||||||
@@ -802,7 +835,8 @@ endif()
|
|||||||
|
|
||||||
# nvcuvid.dll
|
# nvcuvid.dll
|
||||||
set(SOURCE_FILES stubs/nvcuvid.cpp)
|
set(SOURCE_FILES stubs/nvcuvid.cpp)
|
||||||
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} stubs/nvcuvid.def)
|
set(RESOURCE_FILES stubs/manifest.rc)
|
||||||
|
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuvid.def)
|
||||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES PREFIX "")
|
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
|
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
|
||||||
|
|
||||||
@@ -812,7 +846,8 @@ endif()
|
|||||||
|
|
||||||
# nvEncodeAPI64.dll
|
# nvEncodeAPI64.dll
|
||||||
set(SOURCE_FILES stubs/nvEncodeAPI64.cpp)
|
set(SOURCE_FILES stubs/nvEncodeAPI64.cpp)
|
||||||
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} stubs/nvEncodeAPI64.def)
|
set(RESOURCE_FILES stubs/manifest.rc)
|
||||||
|
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvEncodeAPI64.def)
|
||||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES PREFIX "")
|
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
|
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
|
||||||
|
|
||||||
@@ -822,7 +857,8 @@ endif()
|
|||||||
|
|
||||||
# cpusbxpkm.dll (32 bit)
|
# cpusbxpkm.dll (32 bit)
|
||||||
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
|
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
|
||||||
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} stubs/cpusbxpkm.def)
|
set(RESOURCE_FILES stubs/manifest.rc)
|
||||||
|
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/cpusbxpkm.def)
|
||||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES PREFIX "")
|
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES PREFIX "")
|
||||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
|
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
|
||||||
|
|
||||||
@@ -830,6 +866,36 @@ if(NOT MSVC)
|
|||||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# sdk_sample_v0_flat_c.dll (32 bit)
|
||||||
|
set(SOURCE_FILES sdk/sample/v0/flat_c/v0_flat_c.c)
|
||||||
|
add_library(spicetools_sdk_sample_v0_flat_c_32 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} sdk/sample/v0/flat_c/v0_flat_c.def)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_32 PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_32 PROPERTIES OUTPUT_NAME "sdk_sample_v0_flat_c")
|
||||||
|
|
||||||
|
if(NOT MSVC)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_32 PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# sdk_sample_v0_flat_c.dll (64 bit)
|
||||||
|
set(SOURCE_FILES sdk/sample/v0/flat_c/v0_flat_c.c)
|
||||||
|
add_library(spicetools_sdk_sample_v0_flat_c_64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} sdk/sample/v0/flat_c/v0_flat_c.def)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_64 PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_64 PROPERTIES OUTPUT_NAME "sdk_sample_v0_flat_c")
|
||||||
|
|
||||||
|
if(NOT MSVC)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# sdk_sample_v0_cpp.dll (64 bit)
|
||||||
|
set(SOURCE_FILES sdk/sample/v0/cpp/v0_cpp.cpp)
|
||||||
|
add_library(spicetools_sdk_sample_v0_cpp_64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} sdk/sample/v0/cpp/v0_cpp.def)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_cpp_64 PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_cpp_64 PROPERTIES OUTPUT_NAME "sdk_sample_v0_cpp")
|
||||||
|
|
||||||
|
if(NOT MSVC)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_cpp_64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||||
|
endif()
|
||||||
|
|
||||||
# output directories
|
# output directories
|
||||||
####################
|
####################
|
||||||
|
|
||||||
@@ -839,14 +905,14 @@ set_target_properties(spicetools_cfg spicetools_cfg_linux
|
|||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
||||||
|
|
||||||
# output 32bit
|
# output 32bit
|
||||||
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm
|
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm spicetools_sdk_sample_v0_flat_c_32
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
|
||||||
|
|
||||||
# output 64bit
|
# output 64bit
|
||||||
set_target_properties(spicetools_spice64 spicetools_spice64_linux spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvcuda spicetools_stubs_nvcuvid spicetools_stubs_nvEncodeAPI64
|
set_target_properties(spicetools_spice64 spicetools_spice64_linux spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvcuda spicetools_stubs_nvcuvid spicetools_stubs_nvEncodeAPI64 spicetools_sdk_sample_v0_flat_c_64 spicetools_sdk_sample_v0_cpp_64
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
|
||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
|
||||||
|
|||||||
@@ -283,6 +283,16 @@ Python is the only one that is fully spec compliant.
|
|||||||
Other libraries may be missing features and contain bugs; please feel free to
|
Other libraries may be missing features and contain bugs; please feel free to
|
||||||
contribute code to fill the gaps if you work on a project using these libraries.
|
contribute code to fill the gaps if you work on a project using these libraries.
|
||||||
|
|
||||||
|
## Spice SDK (for DLL hooks / plugins)
|
||||||
|
|
||||||
|
As an alternative to Spice API, Spice SDK is available.
|
||||||
|
|
||||||
|
This is a flat C, header-only library that can be included in your DLL. Once
|
||||||
|
initialized, you can call directly into helper routines provided by Spicetools
|
||||||
|
executable, making low-latency and high-throughput interactions possible.
|
||||||
|
|
||||||
|
Check the documentation on the wiki for more details.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Unless otherwise noted, all files are licensed under the GPLv3.
|
Unless otherwise noted, all files are licensed under the GPLv3.
|
||||||
See the LICENSE file for the full license text.
|
See the LICENSE file for the full license text.
|
||||||
|
|||||||
@@ -9,11 +9,22 @@
|
|||||||
#include "games/drs/drs.h"
|
#include "games/drs/drs.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "util/tapeled.h"
|
#include "util/tapeled.h"
|
||||||
|
|
||||||
using namespace GameAPI;
|
using namespace GameAPI;
|
||||||
|
|
||||||
|
#define DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
// state
|
// state
|
||||||
static uint8_t STATUS_BUFFER[272] {};
|
static uint8_t STATUS_BUFFER[272] {};
|
||||||
static bool STATUS_BUFFER_FREEZE = false;
|
static bool STATUS_BUFFER_FREEZE = false;
|
||||||
@@ -94,18 +105,25 @@ static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
const auto now = get_performance_milliseconds();
|
||||||
|
const auto vol_l_state = socd::socd_clean(0,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_l_state == socd::SocdCCW) {
|
||||||
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_l_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
|
||||||
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
const auto vol_r_state = socd::socd_clean(1,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_r_state == socd::SocdCCW) {
|
||||||
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_r_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
|
||||||
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +145,12 @@ static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
|||||||
// save volumes in buffer
|
// save volumes in buffer
|
||||||
*((uint16_t*) &STATUS_BUFFER[17]) = (uint16_t) ((vol_left) << 2);
|
*((uint16_t*) &STATUS_BUFFER[17]) = (uint16_t) ((vol_left) << 2);
|
||||||
*((uint16_t*) &STATUS_BUFFER[19]) = (uint16_t) ((vol_right) << 2);
|
*((uint16_t*) &STATUS_BUFFER[19]) = (uint16_t) ((vol_right) << 2);
|
||||||
|
|
||||||
|
log_debug(
|
||||||
|
"bi2a",
|
||||||
|
"knobs = {} {}",
|
||||||
|
*((uint16_t*) &STATUS_BUFFER[17]),
|
||||||
|
*((uint16_t*) &STATUS_BUFFER[19]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// DanceDanceRevolution
|
// DanceDanceRevolution
|
||||||
|
|||||||
+122
-20
@@ -35,24 +35,33 @@ struct ICCA_STATUS_LA9 {
|
|||||||
static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes");
|
static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes");
|
||||||
|
|
||||||
enum ICCA_WORKFLOW {
|
enum ICCA_WORKFLOW {
|
||||||
STEP,
|
STEP = 0,
|
||||||
SLEEP,
|
SLEEP = 1,
|
||||||
START,
|
START = 2,
|
||||||
INIT,
|
INIT = 3,
|
||||||
READY,
|
READY = 4,
|
||||||
GET_USERID,
|
GET_USERID = 5,
|
||||||
ACTIVE,
|
ACTIVE = 6,
|
||||||
EJECT,
|
EJECT = 7,
|
||||||
EJECT_CHECK,
|
EJECT_CHECK = 8,
|
||||||
END,
|
END = 9,
|
||||||
CLOSE_EJECT,
|
CLOSE_EJECT = 10,
|
||||||
CLOSE_E_CHK,
|
CLOSE_E_CHK = 11,
|
||||||
CLOSE_END,
|
CLOSE_END = 12,
|
||||||
ERR_GETUID = -2
|
ERR_GETUID = -2,
|
||||||
|
ERR_REMOVED = -11
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// used by RA as the status_code
|
||||||
|
enum ICCA_STATUS_CODE {
|
||||||
|
EMPTY = 1,
|
||||||
|
ENGAGED = 2
|
||||||
|
};
|
||||||
|
|
||||||
struct ICCA_UNIT {
|
struct ICCA_UNIT {
|
||||||
struct ICCA_STATUS status {};
|
struct ICCA_STATUS status {};
|
||||||
enum ICCA_WORKFLOW state = STEP;
|
enum ICCA_WORKFLOW state = STEP;
|
||||||
|
enum ICCA_WORKFLOW state_jdz = SLEEP;
|
||||||
bool card_cmd_pressed = false;
|
bool card_cmd_pressed = false;
|
||||||
bool card_in = false;
|
bool card_in = false;
|
||||||
double card_in_time = 0.0;
|
double card_in_time = 0.0;
|
||||||
@@ -75,6 +84,11 @@ static inline int icca_get_active_count() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline int icca_get_unit_id(int unit_id) {
|
static inline int icca_get_unit_id(int unit_id) {
|
||||||
|
// on RA the unit_id is zero-indexed
|
||||||
|
if (avs::game::is_model("JDZ")) {
|
||||||
|
return unit_id;
|
||||||
|
}
|
||||||
|
|
||||||
if (icca_get_active_count() < 2)
|
if (icca_get_active_count() < 2)
|
||||||
return 1;
|
return 1;
|
||||||
else {
|
else {
|
||||||
@@ -122,9 +136,15 @@ static inline void update_card(int unit_id) {
|
|||||||
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
||||||
|
|
||||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||||
|
unit->state_jdz = ACTIVE;
|
||||||
unit->status.error = 0;
|
unit->status.error = 0;
|
||||||
|
unit->status.front_sensor = 1;
|
||||||
|
unit->status.rear_sensor = 1;
|
||||||
} else {
|
} else {
|
||||||
unit->state = ERR_GETUID;
|
unit->state = ERR_GETUID;
|
||||||
|
unit->state_jdz = ERR_GETUID;
|
||||||
|
unit->status.front_sensor = 1;
|
||||||
|
unit->status.rear_sensor = 1;
|
||||||
memset(unit->status.uid, 0, 8);
|
memset(unit->status.uid, 0, 8);
|
||||||
}
|
}
|
||||||
unit->card_in = true;
|
unit->card_in = true;
|
||||||
@@ -132,10 +152,25 @@ static inline void update_card(int unit_id) {
|
|||||||
} else if (unit->state == EJECT_CHECK) {
|
} else if (unit->state == EJECT_CHECK) {
|
||||||
unit->state = SLEEP;
|
unit->state = SLEEP;
|
||||||
unit->card_in = false;
|
unit->card_in = false;
|
||||||
|
unit->status.front_sensor = 0;
|
||||||
|
unit->status.rear_sensor = 0;
|
||||||
|
} else if (unit->state_jdz == READY) {
|
||||||
|
unit->state = GET_USERID;
|
||||||
|
unit->state_jdz = GET_USERID;
|
||||||
|
unit->status.error = 0;
|
||||||
|
unit->status.front_sensor = 1;
|
||||||
|
unit->status.rear_sensor = 1;
|
||||||
|
unit->card_in = true;
|
||||||
|
unit->card_in_time = get_performance_seconds();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||||
|
if (unit->state_jdz == READY) {
|
||||||
|
unit->state_jdz = GET_USERID;
|
||||||
|
}
|
||||||
unit->status.error = 0;
|
unit->status.error = 0;
|
||||||
|
unit->status.front_sensor = 1;
|
||||||
|
unit->status.rear_sensor = 1;
|
||||||
unit->card_in = true;
|
unit->card_in = true;
|
||||||
unit->card_in_time = get_performance_seconds();
|
unit->card_in_time = get_performance_seconds();
|
||||||
}
|
}
|
||||||
@@ -144,6 +179,12 @@ static inline void update_card(int unit_id) {
|
|||||||
unit->state = CLOSE_EJECT;
|
unit->state = CLOSE_EJECT;
|
||||||
if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) {
|
if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) {
|
||||||
unit->card_in = false;
|
unit->card_in = false;
|
||||||
|
memset(unit->status.uid, 0, 8);
|
||||||
|
if (unit->state_jdz == ACTIVE || unit->state_jdz == GET_USERID) {
|
||||||
|
unit->state_jdz = ERR_REMOVED;
|
||||||
|
}
|
||||||
|
unit->status.front_sensor = 0;
|
||||||
|
unit->status.rear_sensor = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +343,11 @@ static char __cdecl ac_io_icca_get_status(void *a1, void *a2) {
|
|||||||
|
|
||||||
// copy state to output buffer
|
// copy state to output buffer
|
||||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||||
unit->status.status_code = unit->state;
|
if (avs::game::is_model("JDZ")) {
|
||||||
|
unit->status.status_code = unit->card_in ? ENGAGED : EMPTY;
|
||||||
|
} else {
|
||||||
|
unit->status.status_code = unit->state;
|
||||||
|
}
|
||||||
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
|
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
|
||||||
|
|
||||||
// funny workaround
|
// funny workaround
|
||||||
@@ -387,6 +432,11 @@ static char __cdecl ac_io_icca_req_uid(int unit_id) {
|
|||||||
static int __cdecl ac_io_icca_req_uid_isfinished(int unit_id, DWORD *read_state) {
|
static int __cdecl ac_io_icca_req_uid_isfinished(int unit_id, DWORD *read_state) {
|
||||||
unit_id = icca_get_unit_id(unit_id);
|
unit_id = icca_get_unit_id(unit_id);
|
||||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||||
|
if (avs::game::is_model("JDZ")) {
|
||||||
|
// hack for RA - only ever seen it as this
|
||||||
|
*read_state = 8;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (unit->card_in) {
|
if (unit->card_in) {
|
||||||
if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) {
|
if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) {
|
||||||
unit->state = END;
|
unit->state = END;
|
||||||
@@ -403,16 +453,61 @@ static int __cdecl ac_io_icca_send_keep_alive_packet(int a1, int a2, int a3) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __cdecl ac_io_icca_workflow_jdz(int workflow, int unit_id) {
|
||||||
|
|
||||||
|
unit_id = icca_get_unit_id(unit_id);
|
||||||
|
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||||
|
switch (workflow) {
|
||||||
|
// RA uses "STEP" as more of a polling thing it seems
|
||||||
|
case STEP:
|
||||||
|
switch (unit->state_jdz) {
|
||||||
|
case CLOSE_EJECT:
|
||||||
|
unit->state_jdz = CLOSE_E_CHK;
|
||||||
|
break;
|
||||||
|
case CLOSE_E_CHK:
|
||||||
|
unit->state_jdz = CLOSE_END;
|
||||||
|
break;
|
||||||
|
case CLOSE_END:
|
||||||
|
unit->state_jdz = SLEEP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SLEEP:
|
||||||
|
unit->state = SLEEP;
|
||||||
|
break;
|
||||||
|
case START:
|
||||||
|
unit->state_jdz = INIT;
|
||||||
|
break;
|
||||||
|
case INIT:
|
||||||
|
case READY:
|
||||||
|
unit->state_jdz = READY;
|
||||||
|
break;
|
||||||
|
case CLOSE_EJECT:
|
||||||
|
unit->state_jdz = CLOSE_EJECT;
|
||||||
|
return CLOSE_E_CHK;
|
||||||
|
case CLOSE_END:
|
||||||
|
unit->state_jdz = SLEEP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// should probably log a warning here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return unit->state_jdz;
|
||||||
|
}
|
||||||
|
|
||||||
static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
|
static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
|
||||||
|
// RA uses a different workflow process for the slotted readers; treat it differently
|
||||||
|
if (avs::game::is_model("JDZ")) {
|
||||||
|
return ac_io_icca_workflow_jdz(workflow, unit_id);
|
||||||
|
}
|
||||||
|
|
||||||
unit_id = icca_get_unit_id(unit_id);
|
unit_id = icca_get_unit_id(unit_id);
|
||||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||||
switch (workflow) {
|
switch (workflow) {
|
||||||
case STEP:
|
case STEP:
|
||||||
if (avs::game::is_model("JDZ"))
|
unit->state = STEP;
|
||||||
unit->state = SLEEP;
|
|
||||||
else
|
|
||||||
unit->state = STEP;
|
|
||||||
break;
|
break;
|
||||||
case SLEEP:
|
case SLEEP:
|
||||||
unit->state = SLEEP;
|
unit->state = SLEEP;
|
||||||
@@ -441,7 +536,6 @@ static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return unit->state;
|
return unit->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,6 +561,14 @@ acio::ICCAModule::ICCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModu
|
|||||||
void acio::ICCAModule::attach() {
|
void acio::ICCAModule::attach() {
|
||||||
ACIOModule::attach();
|
ACIOModule::attach();
|
||||||
|
|
||||||
|
// workaround for RA; ac_io_icca_cardunit_init is never called so treat like both are initialized
|
||||||
|
if (avs::game::is_model("JDZ")) {
|
||||||
|
ICCA_UNITS[0].initialized = true;
|
||||||
|
ICCA_UNITS[1].initialized = true;
|
||||||
|
|
||||||
|
CARD_TIMEOUT = 10.0; // keep the card in the slot for longer
|
||||||
|
}
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init);
|
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init);
|
||||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished);
|
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
|||||||
float pad3_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad3));
|
float pad3_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad3));
|
||||||
float pad4_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad4));
|
float pad4_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad4));
|
||||||
// FIXME(felix): this logic seems wrong for analog handling but correct for digital inputs
|
// FIXME(felix): this logic seems wrong for analog handling but correct for digital inputs
|
||||||
if (pad1_vel > 0.f) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad1))) {
|
||||||
STATUS_BUFFER[6] = (int) (51.f * pad1_vel + 0.5f);
|
STATUS_BUFFER[6] = (int) (51.f * pad1_vel + 0.5f);
|
||||||
}
|
}
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad2))) {
|
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad2))) {
|
||||||
@@ -67,10 +67,10 @@ static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
|||||||
|
|
||||||
// process analogs
|
// process analogs
|
||||||
auto &analogs = games::ftt::get_analogs();
|
auto &analogs = games::ftt::get_analogs();
|
||||||
auto pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
auto &pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
||||||
auto pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
auto &pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
||||||
auto pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
auto &pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
||||||
auto pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
auto &pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
||||||
if (pad1_analog.isSet()) {
|
if (pad1_analog.isSet()) {
|
||||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad1_analog) + 0.5f);
|
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad1_analog) + 0.5f);
|
||||||
STATUS_BUFFER[6] = MAX(STATUS_BUFFER[6], val);
|
STATUS_BUFFER[6] = MAX(STATUS_BUFFER[6], val);
|
||||||
|
|||||||
@@ -8,10 +8,21 @@
|
|||||||
#include "games/sdvx/io.h"
|
#include "games/sdvx/io.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
using namespace GameAPI;
|
using namespace GameAPI;
|
||||||
|
|
||||||
|
#define DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
// globals
|
// globals
|
||||||
uint8_t KFCA_VOL_SOUND = 96;
|
uint8_t KFCA_VOL_SOUND = 96;
|
||||||
uint8_t KFCA_VOL_HEADPHONE = 96;
|
uint8_t KFCA_VOL_HEADPHONE = 96;
|
||||||
@@ -342,18 +353,25 @@ static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// volume left
|
// volume left
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
const auto now = get_performance_milliseconds();
|
||||||
|
const auto vol_l_state = socd::socd_clean(0,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_l_state == socd::SocdCCW) {
|
||||||
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_l_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
|
||||||
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume right
|
// volume right
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
const auto vol_r_state = socd::socd_clean(1,
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||||
|
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||||
|
now);
|
||||||
|
if (vol_r_state == socd::SocdCCW) {
|
||||||
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
} else if (vol_r_state == socd::SocdCW) {
|
||||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
|
||||||
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,6 +389,8 @@ static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
|||||||
// proper loops
|
// proper loops
|
||||||
vol_left %= 1024;
|
vol_left %= 1024;
|
||||||
vol_right %= 1024;
|
vol_right %= 1024;
|
||||||
|
|
||||||
|
log_debug("kfca", "knobs = {} {}", vol_left, vol_right);
|
||||||
|
|
||||||
// save volumes in buffer
|
// save volumes in buffer
|
||||||
STATUS_BUFFER[input_offset + 16 + 0] |= (unsigned char) ((vol_left << 6) & 0xFF);
|
STATUS_BUFFER[input_offset + 16 + 0] |= (unsigned char) ((vol_left << 6) & 0xFF);
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
#include "mdxf.h"
|
#include "mdxf.h"
|
||||||
#include "mdxf_poll.h"
|
#include "mdxf_poll.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "games/ddr/ddr.h"
|
||||||
#include "games/ddr/io.h"
|
#include "games/ddr/io.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#define MDFX_DEBUG_VERBOSE 0
|
#define MDFX_DEBUG_VERBOSE 0
|
||||||
|
|
||||||
@@ -108,9 +111,10 @@ static void mdxf_thread_start() {
|
|||||||
MDXF_THREAD = std::thread([] {
|
MDXF_THREAD = std::thread([] {
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
while (MDXF_THREAD_RUNNING.load(std::memory_order_acquire)) {
|
while (MDXF_THREAD_RUNNING.load(std::memory_order_acquire)) {
|
||||||
mdxf_poll(false);
|
mdxf_poll(false);
|
||||||
std::this_thread::sleep_for(THREAD_PERIOD);
|
timer.sleep(THREAD_PERIOD);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -357,14 +361,17 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
|||||||
|
|
||||||
// decide on button map
|
// decide on button map
|
||||||
const size_t *button_map = nullptr;
|
const size_t *button_map = nullptr;
|
||||||
|
int player = 0;
|
||||||
switch (node) {
|
switch (node) {
|
||||||
case 17:
|
case 17:
|
||||||
case 25:
|
case 25:
|
||||||
button_map = &buttons_p1[0];
|
button_map = &buttons_p1[0];
|
||||||
|
player = 1;
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
case 26:
|
case 26:
|
||||||
button_map = &buttons_p2[0];
|
button_map = &buttons_p2[0];
|
||||||
|
player = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,19 +381,27 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
|||||||
if (source == EXTERNAL_POLL) {
|
if (source == EXTERNAL_POLL) {
|
||||||
// get buttons
|
// get buttons
|
||||||
auto &buttons = games::ddr::get_buttons();
|
auto &buttons = games::ddr::get_buttons();
|
||||||
|
|
||||||
|
// get analogs
|
||||||
|
bool analog_left = false;
|
||||||
|
bool analog_right = false;
|
||||||
|
games::ddr::get_analog_x_axis(player, analog_left, analog_right);
|
||||||
|
bool analog_up = false;
|
||||||
|
bool analog_down = false;
|
||||||
|
games::ddr::get_analog_y_axis(player, analog_up, analog_down);
|
||||||
|
|
||||||
uint8_t up_down = 0;
|
uint8_t up_down = 0;
|
||||||
uint8_t left_right = 0;
|
uint8_t left_right = 0;
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[0])) || analog_up) {
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[0]))) {
|
|
||||||
up_down |= 0xF0;
|
up_down |= 0xF0;
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[1]))) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[1])) || analog_down) {
|
||||||
up_down |= 0x0F;
|
up_down |= 0x0F;
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[2]))) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[2])) || analog_left) {
|
||||||
left_right |= 0xF0;
|
left_right |= 0xF0;
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[3]))) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[3])) || analog_right) {
|
||||||
left_right |= 0x0F;
|
left_right |= 0x0F;
|
||||||
}
|
}
|
||||||
current_state = (uint16_t(up_down) << 8) | left_right;
|
current_state = (uint16_t(up_down) << 8) | left_right;
|
||||||
@@ -500,4 +515,4 @@ acio::MDXFModule::~MDXFModule() {
|
|||||||
if (IS_THREAD_NEEDED) {
|
if (IS_THREAD_NEEDED) {
|
||||||
mdxf_thread_stop();
|
mdxf_thread_stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "util/circular_buffer.h"
|
#include "util/circular_buffer.h"
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#include "acio/icca/icca.h"
|
#include "acio/icca/icca.h"
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
using namespace acioemu;
|
using namespace acioemu;
|
||||||
@@ -45,11 +47,12 @@ ICCADevice::ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count)
|
|||||||
// keypad thread for faster polling
|
// keypad thread for faster polling
|
||||||
if (keypad_thread) {
|
if (keypad_thread) {
|
||||||
this->keypad_thread = new std::thread([this]() {
|
this->keypad_thread = new std::thread([this]() {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
while (this->cards) {
|
while (this->cards) {
|
||||||
for (int unit = 0; unit < this->node_count; unit++) {
|
for (int unit = 0; unit < this->node_count; unit++) {
|
||||||
this->update_keypad(unit, false);
|
this->update_keypad(unit, false);
|
||||||
}
|
}
|
||||||
Sleep(7);
|
timer.sleep(7);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -101,13 +104,10 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
|||||||
|
|
||||||
// send version data
|
// send version data
|
||||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||||
if (
|
if (avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
||||||
avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
games::sdvx::is_valkyrie_model()) {
|
||||||
// SDVX Valkyrie cabinet mode
|
|
||||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
|
||||||
) {
|
|
||||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
||||||
} else if (avs::game::is_model({"VFG"})) {
|
} else if (avs::game::is_model("VFG")) {
|
||||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
||||||
} else {
|
} else {
|
||||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||||
@@ -520,11 +520,7 @@ void ICCADevice::update_status(int unit) {
|
|||||||
buffer[1] = felica ? 0x01 : 0x00;
|
buffer[1] = felica ? 0x01 : 0x00;
|
||||||
buffer[10] = felica ? 0x01 : 0x00;
|
buffer[10] = felica ? 0x01 : 0x00;
|
||||||
|
|
||||||
} else if (
|
} else if (avs::game::is_model({"LDJ", "TBS", "XIF"}) || games::sdvx::is_valkyrie_model()) {
|
||||||
avs::game::is_model({"LDJ", "TBS", "XIF"}) ||
|
|
||||||
// SDVX Valkyrie cabinet mode
|
|
||||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))) {
|
|
||||||
|
|
||||||
// set status to 0 otherwise reader power on fails
|
// set status to 0 otherwise reader power on fails
|
||||||
buffer[0] = 0x00;
|
buffer[0] = 0x00;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "acio/mdxf/mdxf_poll.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -104,6 +105,7 @@ namespace api::modules {
|
|||||||
for (auto &analog : *this->analogs) {
|
for (auto &analog : *this->analogs) {
|
||||||
analog.override_enabled = false;
|
analog.override_enabled = false;
|
||||||
}
|
}
|
||||||
|
mdxf_poll(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -148,6 +150,7 @@ namespace api::modules {
|
|||||||
if (analog.getName() == name) {
|
if (analog.getName() == name) {
|
||||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||||
analog.override_enabled = true;
|
analog.override_enabled = true;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +170,7 @@ namespace api::modules {
|
|||||||
for (auto &analog : *this->analogs) {
|
for (auto &analog : *this->analogs) {
|
||||||
if (analog.getName() == name) {
|
if (analog.getName() == name) {
|
||||||
analog.override_enabled = false;
|
analog.override_enabled = false;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "acio/mdxf/mdxf_poll.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -107,6 +108,7 @@ namespace api::modules {
|
|||||||
for (auto &button : *this->buttons) {
|
for (auto &button : *this->buttons) {
|
||||||
button.override_enabled = false;
|
button.override_enabled = false;
|
||||||
}
|
}
|
||||||
|
mdxf_poll(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -153,6 +155,7 @@ namespace api::modules {
|
|||||||
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||||
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
||||||
button.override_enabled = true;
|
button.override_enabled = true;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,6 +175,7 @@ namespace api::modules {
|
|||||||
for (auto &button : *this->buttons) {
|
for (auto &button : *this->buttons) {
|
||||||
if (button.getName() == name) {
|
if (button.getName() == name) {
|
||||||
button.override_enabled = false;
|
button.override_enabled = false;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace api::modules {
|
|||||||
void DRS::touch_set(Request &req, Response &res) {
|
void DRS::touch_set(Request &req, Response &res) {
|
||||||
|
|
||||||
// get all touch points
|
// get all touch points
|
||||||
games::drs::drs_touch_t touches[16];
|
auto touches = std::make_unique<games::drs::drs_touch_t[]>(req.params.Size());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (Value ¶m : req.params.GetArray()) {
|
for (Value ¶m : req.params.GetArray()) {
|
||||||
|
|
||||||
@@ -86,6 +86,6 @@ namespace api::modules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// apply touch points
|
// apply touch points
|
||||||
games::drs::fire_touches(touches, i);
|
games::drs::fire_touches(touches.get(), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -58,6 +59,7 @@ namespace api::modules {
|
|||||||
// get params
|
// get params
|
||||||
auto keypad = req.params[0].GetUint();
|
auto keypad = req.params[0].GetUint();
|
||||||
auto input = std::string(req.params[1].GetString());
|
auto input = std::string(req.params[1].GetString());
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// process all chars
|
// process all chars
|
||||||
for (auto c : input) {
|
for (auto c : input) {
|
||||||
@@ -91,11 +93,11 @@ namespace api::modules {
|
|||||||
|
|
||||||
// set
|
// set
|
||||||
eamuse_set_keypad_overrides(keypad, state);
|
eamuse_set_keypad_overrides(keypad, state);
|
||||||
Sleep(sleep_time);
|
timer.sleep(sleep_time);
|
||||||
|
|
||||||
// unset
|
// unset
|
||||||
eamuse_set_keypad_overrides(keypad, 0);
|
eamuse_set_keypad_overrides(keypad, 0);
|
||||||
Sleep(sleep_time);
|
timer.sleep(sleep_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
// check if file exists in modules
|
// check if file exists in modules
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
return error(res, "Couldn't find " + dll_path.string());
|
return error(res, fmt::format("Couldn't find {}", dll_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get module
|
// get module
|
||||||
@@ -118,7 +118,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
// check if file exists in modules
|
// check if file exists in modules
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
return error(res, "Couldn't find " + dll_path.string());
|
return error(res, fmt::format("Couldn't find {}", dll_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get module
|
// get module
|
||||||
@@ -196,7 +196,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
// check if file exists in modules
|
// check if file exists in modules
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
return error(res, "Couldn't find " + dll_path.string());
|
return error(res, fmt::format("Couldn't find {}", dll_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get module
|
// get module
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ namespace api {
|
|||||||
controller->init_state(this->state);
|
controller->init_state(this->state);
|
||||||
this->thread = new std::thread([this] () {
|
this->thread = new std::thread([this] () {
|
||||||
log_warning("api::serial", "listening on {} (baud: {})", this->port, this->baud);
|
log_warning("api::serial", "listening on {} (baud: {})", this->port, this->baud);
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// read buffer
|
// read buffer
|
||||||
uint8_t read_buffer[16*1024];
|
uint8_t read_buffer[16*1024];
|
||||||
@@ -160,7 +162,7 @@ namespace api {
|
|||||||
|
|
||||||
// slow down on reconnect
|
// slow down on reconnect
|
||||||
if (this->running) {
|
if (this->running) {
|
||||||
Sleep(retry_time);
|
timer.sleep(retry_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "external/robin_hood.h"
|
#include "external/robin_hood.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "launcher/logger.h"
|
#include "launcher/logger.h"
|
||||||
#include "launcher/signal.h"
|
#include "launcher/signal.h"
|
||||||
#include "util/deferlog.h"
|
#include "util/deferlog.h"
|
||||||
@@ -1108,6 +1109,8 @@ namespace avs {
|
|||||||
|
|
||||||
// SOUND VOLTEX
|
// SOUND VOLTEX
|
||||||
{"soundvoltex.dll", 0x10000000},
|
{"soundvoltex.dll", 0x10000000},
|
||||||
|
// GITADORA
|
||||||
|
{"gdxg.dll", 0x2000000},
|
||||||
#endif
|
#endif
|
||||||
// jubeat
|
// jubeat
|
||||||
{"jubeat.dll", 0x2000000},
|
{"jubeat.dll", 0x2000000},
|
||||||
@@ -1165,7 +1168,7 @@ namespace avs {
|
|||||||
// Ongaku Paradise
|
// Ongaku Paradise
|
||||||
{"arkjc9.dll", 0xA00000},
|
{"arkjc9.dll", 0xA00000},
|
||||||
|
|
||||||
// KAMUNITY
|
// KAMUNITY
|
||||||
{"kamunity.dll", 33554432},
|
{"kamunity.dll", 33554432},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1177,6 +1180,15 @@ namespace avs {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hack, both hello! popn and popn hc using popn.dll
|
||||||
|
if (games::popn::is_pikapika_model()) {
|
||||||
|
auto old_size = HEAP_SIZE;
|
||||||
|
HEAP_SIZE = 0x20000000;
|
||||||
|
DEFAULT_HEAP_SIZE_SET = true;
|
||||||
|
log_info("avs-core", "updated heap size: {} -> {}", old_size, avs::core::HEAP_SIZE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (HEAP_SIZE_DEFAULTS.find(dll_name) != HEAP_SIZE_DEFAULTS.end()) {
|
if (HEAP_SIZE_DEFAULTS.find(dll_name) != HEAP_SIZE_DEFAULTS.end()) {
|
||||||
auto old_size = HEAP_SIZE;
|
auto old_size = HEAP_SIZE;
|
||||||
|
|
||||||
@@ -1232,6 +1244,7 @@ namespace avs {
|
|||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
new_style = logger::Style::RED;
|
new_style = logger::Style::RED;
|
||||||
|
deferredlogs::report_fatal_message();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
position = 0;
|
position = 0;
|
||||||
@@ -1300,6 +1313,7 @@ namespace avs {
|
|||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
style = logger::Style::RED;
|
style = logger::Style::RED;
|
||||||
|
deferredlogs::report_fatal_message();
|
||||||
break;
|
break;
|
||||||
case 'W':
|
case 'W':
|
||||||
style = logger::Style::YELLOW;
|
style = logger::Style::YELLOW;
|
||||||
@@ -1502,7 +1516,7 @@ namespace avs {
|
|||||||
" * It's also possible that you have incomplete game data\n"
|
" * It's also possible that you have incomplete game data\n"
|
||||||
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
||||||
"\n"
|
"\n"
|
||||||
, DLL_NAME, MODULE_PATH.string()) };
|
, DLL_NAME, MODULE_PATH) };
|
||||||
log_warning("avs-ea3", "{}", info_str);
|
log_warning("avs-ea3", "{}", info_str);
|
||||||
log_fatal("avs-ea3", "Failed to find critical avs DLL on disk (avs2-core.dll OR {})", DLL_NAME);
|
log_fatal("avs-ea3", "Failed to find critical avs DLL on disk (avs2-core.dll OR {})", DLL_NAME);
|
||||||
}
|
}
|
||||||
@@ -1743,9 +1757,9 @@ namespace avs {
|
|||||||
{
|
{
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
"AVS filesystem initialization failure was previously detected during boot!",
|
"AVS filesystem initialization failure was previously detected during boot!",
|
||||||
fmt::format(" ERROR: directory could not be created: {}", src_path.string().c_str()),
|
fmt::format(" ERROR: directory could not be created: {}", src_path),
|
||||||
fmt::format(" if you see a crash, it may have been caused by bad <mounttable> contents in {}", avs::core::CFG_PATH.c_str()),
|
fmt::format(" if you see a crash, it may have been caused by bad <mounttable> contents in {}", avs::core::CFG_PATH),
|
||||||
" fix the XML file and try again"
|
" fix the XML file and try again"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1766,14 +1780,14 @@ namespace avs {
|
|||||||
auto created = std::filesystem::create_directories(real_path, err);
|
auto created = std::filesystem::create_directories(real_path, err);
|
||||||
|
|
||||||
if (created) {
|
if (created) {
|
||||||
log_info("avs-core", "created '{}' at '{}'", avs_path, real_path.string());
|
log_info("avs-core", "created '{}' at '{}'", avs_path, real_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
avs_dir_err(real_path);
|
avs_dir_err(real_path);
|
||||||
log_warning("avs-core", "failed to create '{}' folder at '{}': {}",
|
log_warning("avs-core", "failed to create '{}' folder at '{}': {}",
|
||||||
avs_path,
|
avs_path,
|
||||||
real_path.string(),
|
real_path,
|
||||||
err.message());
|
err.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1907,6 +1921,15 @@ namespace avs {
|
|||||||
// create nvram and raw directories if possible for mounttable configurations
|
// create nvram and raw directories if possible for mounttable configurations
|
||||||
create_avs_config_fs_table(config, config_node);
|
create_avs_config_fs_table(config, config_node);
|
||||||
|
|
||||||
|
#if !SPICE64
|
||||||
|
// sdvx4 bad log config fix
|
||||||
|
if (avs::game::DLL_NAME == "soundvoltex.dll" && // it's too early for avs::game::is_model
|
||||||
|
property_search_safe(config, config_node, "/log/enable_console")) {
|
||||||
|
log_info("avs-core", "applying SDVX4 avs-config.xml fix for <log><enable_console>");
|
||||||
|
property_search_remove_safe(config, config_node, "/log/enable_console");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// set log level
|
// set log level
|
||||||
if (!LOG_LEVEL_CUSTOM.empty()) {
|
if (!LOG_LEVEL_CUSTOM.empty()) {
|
||||||
property_search_remove_safe(config, config_node, "/log/level");
|
property_search_remove_safe(config, config_node, "/log/level");
|
||||||
|
|||||||
+58
-7
@@ -5,8 +5,9 @@
|
|||||||
#include "build/defs.h"
|
#include "build/defs.h"
|
||||||
#include "easrv/easrv.h"
|
#include "easrv/easrv.h"
|
||||||
#include "easrv/smartea.h"
|
#include "easrv/smartea.h"
|
||||||
#include "games/mfc/mfc.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "hooks/avshook.h"
|
#include "hooks/avshook.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/deferlog.h"
|
#include "util/deferlog.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
@@ -150,8 +151,8 @@ namespace avs {
|
|||||||
" * It's also possible that you have incomplete game data\n"
|
" * It's also possible that you have incomplete game data\n"
|
||||||
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
||||||
"\n"
|
"\n"
|
||||||
, DLL_NAME, MODULE_PATH.string()) };
|
, DLL_NAME, MODULE_PATH) };
|
||||||
|
|
||||||
log_warning("avs-ea3", "{}", info_str);
|
log_warning("avs-ea3", "{}", info_str);
|
||||||
log_fatal("avs-ea3", "Failed to find critical ea3 DLL on disk (avs2-ea3.dll OR {})", DLL_NAME);
|
log_fatal("avs-ea3", "Failed to find critical ea3 DLL on disk (avs2-ea3.dll OR {})", DLL_NAME);
|
||||||
}
|
}
|
||||||
@@ -216,7 +217,25 @@ namespace avs {
|
|||||||
ea3_config_name = "prop/eamuse-config.xml";
|
ea3_config_name = "prop/eamuse-config.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("avs-ea3", "booting (using {})", ea3_config_name);
|
if (fileutils::file_exists(ea3_config_name)) {
|
||||||
|
log_info("avs-ea3", "found {} on disk", ea3_config_name);
|
||||||
|
} else if (CFG_PATH.size()) {
|
||||||
|
log_fatal("avs-ea3", "user-specified ea3 config file is missing: {}", ea3_config_name);
|
||||||
|
} else {
|
||||||
|
log_warning("avs-ea3", "looked for the following files in order:");
|
||||||
|
log_warning("avs-ea3", " * prop/ea3-config.xml");
|
||||||
|
log_warning("avs-ea3", " * prop/ea3-cfg.xml");
|
||||||
|
if (avs::game::DLL_NAME == "beatstream1.dll") {
|
||||||
|
log_warning("avs-ea3", " * prop/ea3-config-1.xml");
|
||||||
|
}
|
||||||
|
if (avs::game::DLL_NAME == "beatstream2.dll") {
|
||||||
|
log_warning("avs-ea3", " * prop/ea3-config-2.xml");
|
||||||
|
}
|
||||||
|
log_warning("avs-ea3", " * prop/eamuse-config.xml");
|
||||||
|
log_warning("avs-ea3", "none of the files above were found, game will fail to boot");
|
||||||
|
log_warning("avs-ea3", "usually, this means your game data is incomplete");
|
||||||
|
log_fatal("avs-ea3", "no ea3 config file found in prop directory; check log messages");
|
||||||
|
}
|
||||||
|
|
||||||
// remember new config path
|
// remember new config path
|
||||||
CFG_PATH = ea3_config_name;
|
CFG_PATH = ea3_config_name;
|
||||||
@@ -494,7 +513,9 @@ namespace avs {
|
|||||||
std::ostringstream init_code;
|
std::ostringstream init_code;
|
||||||
init_code << EA3_MODEL;
|
init_code << EA3_MODEL;
|
||||||
init_code << EA3_DEST;
|
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 << "G";
|
||||||
init_code << "B";
|
init_code << "B";
|
||||||
} else {
|
} else {
|
||||||
@@ -526,12 +547,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");
|
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
||||||
|
|
||||||
// call the game init
|
// call the game init
|
||||||
log_info("avs-ea3", "calling entry init");
|
log_info("avs-ea3", "calling dll_entry_init (init code = {})", init_code_str);
|
||||||
if (!avs::game::entry_init(init_code_str.data(), app_param)) {
|
if (!avs::game::entry_init(init_code_str.data(), app_param)) {
|
||||||
log_fatal("avs-ea3", "entry init failed :(");
|
for (size_t i = 0; i < 255; i++) {
|
||||||
|
log_warning("avs-ea3", "dll_entry_init failed :(");
|
||||||
|
}
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"dll_entry_init failed",
|
||||||
|
" init code: " + init_code_str,
|
||||||
|
" this is a fatal error that causes boot failure",
|
||||||
|
" most likely, your prop files have errors"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// accommodate changes to soft id code
|
// accommodate changes to soft id code
|
||||||
|
|||||||
@@ -73,11 +73,10 @@ namespace avs {
|
|||||||
|
|
||||||
// load game instance
|
// load game instance
|
||||||
const auto dll_path = MODULE_PATH / DLL_NAME;
|
const auto dll_path = MODULE_PATH / DLL_NAME;
|
||||||
const auto dll_path_s = dll_path.string();
|
log_info("avs-game", "DLL path: {}", dll_path);
|
||||||
log_info("avs-game", "DLL path: {}", dll_path_s.c_str());
|
|
||||||
|
|
||||||
// MAX_PATH is 260
|
// MAX_PATH is 260
|
||||||
if (130 <= dll_path_s.length()) {
|
if (const auto dll_path_len = dll_path.wstring().length(); 130 <= dll_path_len) {
|
||||||
log_warning(
|
log_warning(
|
||||||
"avs-game",
|
"avs-game",
|
||||||
"PATH TOO LONG WARNING\n\n"
|
"PATH TOO LONG WARNING\n\n"
|
||||||
@@ -93,7 +92,7 @@ namespace avs {
|
|||||||
"long, often resulting in random crashes. Move the game contents to\n"
|
"long, often resulting in random crashes. Move the game contents to\n"
|
||||||
"a directory with shorter path.\n"
|
"a directory with shorter path.\n"
|
||||||
"-------------------------------------------------------------------\n\n",
|
"-------------------------------------------------------------------\n\n",
|
||||||
dll_path_s, dll_path_s.length());
|
dll_path, dll_path_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ddr gamemdx.dll user error
|
// ddr gamemdx.dll user error
|
||||||
@@ -112,7 +111,7 @@ namespace avs {
|
|||||||
|
|
||||||
// file not found on disk
|
// file not found on disk
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path.string().c_str());
|
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path);
|
||||||
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
|
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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_ms="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>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges>
|
<requestedPrivileges>
|
||||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||||
</requestedPrivileges>
|
</requestedPrivileges>
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
|
|||||||
+109
-25
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# set to EN-US for consistency
|
# set to EN-US for consistency
|
||||||
LANG=en_us_8859_1
|
LANG=en_us_8859_1
|
||||||
@@ -41,12 +41,18 @@ done
|
|||||||
# settings
|
# settings
|
||||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
||||||
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
||||||
TOOLCHAIN_32="/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"
|
TOOLCHAIN_32="${TOOLCHAIN_32:-"/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"}"
|
||||||
TOOLCHAIN_64="/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"
|
TOOLCHAIN_64="${TOOLCHAIN_64:-"/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"}"
|
||||||
|
TOOLCHAIN_WINXP_32="${TOOLCHAIN_WINXP_32:-"/opt/llvm-mingw-xp/toolchain-files/cmake/i686-mingw32-clang.cmake"}"
|
||||||
|
TOOLCHAIN_WINXP_64="${TOOLCHAIN_WINXP_64:-"/opt/llvm-mingw-xp/toolchain-files/cmake/x86_64-mingw32-clang.cmake"}"
|
||||||
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
||||||
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
||||||
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||||
BUILDDIR_64_DEBUG="./cmake-build-debug-64"
|
BUILDDIR_64_DEBUG="./cmake-build-debug-64"
|
||||||
|
BUILDDIR_WINXP_32_RELEASE="./cmake-build-release-winxp-32"
|
||||||
|
BUILDDIR_WINXP_32_DEBUG="./cmake-build-debug-winxp-32"
|
||||||
|
BUILDDIR_WINXP_64_RELEASE="./cmake-build-release-winxp-64"
|
||||||
|
BUILDDIR_WINXP_64_DEBUG="./cmake-build-debug-winxp-64"
|
||||||
DEBUG=0
|
DEBUG=0
|
||||||
OUTDIR="./bin/spice2x"
|
OUTDIR="./bin/spice2x"
|
||||||
OUTDIR_EXTRAS="./bin/spice2x/extras"
|
OUTDIR_EXTRAS="./bin/spice2x/extras"
|
||||||
@@ -62,19 +68,36 @@ DIST_FOLDER="./dist"
|
|||||||
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
|
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
|
||||||
DIST_NAME_EXTRAS="spice2x-$(date +%y)-$(date +%m)-$(date +%d)-full.zip"
|
DIST_NAME_EXTRAS="spice2x-$(date +%y)-$(date +%m)-$(date +%d)-full.zip"
|
||||||
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
|
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
|
||||||
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_cfg_linux spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_cpusbxpkm"
|
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_cfg_linux spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_cpusbxpkm spicetools_sdk_sample_v0_flat_c_32"
|
||||||
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux"
|
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux spicetools_sdk_sample_v0_flat_c_64 spicetools_sdk_sample_v0_cpp_64"
|
||||||
|
TARGETS_XP32="spicetools_cfg spicetools_spice"
|
||||||
|
TARGETS_XP64="spicetools_spice64"
|
||||||
|
|
||||||
# determine build type
|
# determine build type
|
||||||
BUILD_TYPE="Release"
|
BUILD_TYPE="Release"
|
||||||
BUILDDIR_32=${BUILDDIR_32_RELEASE}
|
BUILDDIR_32=${BUILDDIR_32_RELEASE}
|
||||||
BUILDDIR_64=${BUILDDIR_64_RELEASE}
|
BUILDDIR_64=${BUILDDIR_64_RELEASE}
|
||||||
|
BUILDDIR_WINXP_32=${BUILDDIR_WINXP_32_RELEASE}
|
||||||
|
BUILDDIR_WINXP_64=${BUILDDIR_WINXP_64_RELEASE}
|
||||||
if ((DEBUG > 0))
|
if ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
BUILD_TYPE="Debug"
|
BUILD_TYPE="Debug"
|
||||||
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
||||||
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
||||||
DIST_NAME=$(echo ${DIST_NAME} | sed 's/\.[^.]*$/-dbg&/')
|
BUILDDIR_WINXP_32=${BUILDDIR_WINXP_32_DEBUG}
|
||||||
|
BUILDDIR_WINXP_64=${BUILDDIR_WINXP_64_DEBUG}
|
||||||
|
DIST_NAME=$(echo "${DIST_NAME}" | sed 's/\.[^.]*$/-dbg&/')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# is the XP-compatible toolchain installed?
|
||||||
|
XP_MUST_BUILD=0
|
||||||
|
BUILD_XP=0
|
||||||
|
if [ -f "$TOOLCHAIN_WINXP_32" ] && [ -f "$TOOLCHAIN_WINXP_64" ]; then
|
||||||
|
BUILD_XP=1;
|
||||||
|
elif ((XP_MUST_BUILD > 0))
|
||||||
|
then
|
||||||
|
echo "WinXP toolchain not available, aborting"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# determine number of cores
|
# determine number of cores
|
||||||
@@ -90,6 +113,13 @@ echo "Git Branch: $GIT_BRANCH"
|
|||||||
echo "Git Head: $GIT_HEAD"
|
echo "Git Head: $GIT_HEAD"
|
||||||
echo "Toolchain for 32bit targets: $TOOLCHAIN_32"
|
echo "Toolchain for 32bit targets: $TOOLCHAIN_32"
|
||||||
echo "Toolchain for 64bit targets: $TOOLCHAIN_64"
|
echo "Toolchain for 64bit targets: $TOOLCHAIN_64"
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
echo "Toolchain for WinXP 32bit targets: $TOOLCHAIN_WINXP_32"
|
||||||
|
echo "Toolchain for WinXP 64bit targets: $TOOLCHAIN_WINXP_64"
|
||||||
|
else
|
||||||
|
echo "WinXP toolchain not available, skipping WinXP builds"
|
||||||
|
fi
|
||||||
echo "Distribution Name: $DIST_NAME"
|
echo "Distribution Name: $DIST_NAME"
|
||||||
echo "Build Type: $BUILD_TYPE"
|
echo "Build Type: $BUILD_TYPE"
|
||||||
echo "Cores: $CORES"
|
echo "Cores: $CORES"
|
||||||
@@ -114,7 +144,7 @@ time (
|
|||||||
fi
|
fi
|
||||||
mkdir -p ${BUILDDIR_32}
|
mkdir -p ${BUILDDIR_32}
|
||||||
pushd ${BUILDDIR_32} > /dev/null
|
pushd ${BUILDDIR_32} > /dev/null
|
||||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
|
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_32}
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
# 64 bit
|
# 64 bit
|
||||||
@@ -127,14 +157,62 @@ time (
|
|||||||
fi
|
fi
|
||||||
mkdir -p ${BUILDDIR_64}
|
mkdir -p ${BUILDDIR_64}
|
||||||
pushd ${BUILDDIR_64} > /dev/null
|
pushd ${BUILDDIR_64} > /dev/null
|
||||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
|
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_64}
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
# 32 bit Windows XP
|
||||||
|
echo ""
|
||||||
|
echo "Building 32bit targets (WinXP toolchain)..."
|
||||||
|
echo "========================="
|
||||||
|
if ((CLEAN_BUILD > 0))
|
||||||
|
then
|
||||||
|
rm -rf ${BUILDDIR_WINXP_32}
|
||||||
|
fi
|
||||||
|
mkdir -p ${BUILDDIR_WINXP_32}
|
||||||
|
pushd ${BUILDDIR_WINXP_32} > /dev/null
|
||||||
|
CXXFLAGS="$CXXFLAGS -DSPICE_XP=1" cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_WINXP_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_XP32}
|
||||||
|
popd > /dev/null
|
||||||
|
|
||||||
|
# 64 bit Windows XP
|
||||||
|
echo ""
|
||||||
|
echo "Building 64bit targets (WinXP toolchain)..."
|
||||||
|
echo "========================="
|
||||||
|
if ((CLEAN_BUILD > 0))
|
||||||
|
then
|
||||||
|
rm -rf ${BUILDDIR_WINXP_64}
|
||||||
|
fi
|
||||||
|
mkdir -p ${BUILDDIR_WINXP_64}
|
||||||
|
pushd ${BUILDDIR_WINXP_64} > /dev/null
|
||||||
|
CXXFLAGS="$CXXFLAGS -DSPICE_XP=1" cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_WINXP_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_XP64}
|
||||||
|
popd > /dev/null
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "Skipping WinXP builds, toolchain not specified"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Compilation process done :)"
|
echo "Compilation process done :)"
|
||||||
echo "==========================="
|
echo "==========================="
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
echo ""
|
||||||
|
echo "Checking XP compatibility..."
|
||||||
|
echo "============================="
|
||||||
|
if ! command -v windows_dll_compat_checker &> /dev/null; then
|
||||||
|
echo "WARNING: windows_dll_compat_checker not found, skipping XP compatibility check"
|
||||||
|
else
|
||||||
|
windows_dll_compat_checker -s PREMADE/winxp_x86_64.ini \
|
||||||
|
${BUILDDIR_WINXP_64}/spicetools/64/spice64.exe
|
||||||
|
windows_dll_compat_checker -s PREMADE/winxp_x86_64_32bit_dlls.ini \
|
||||||
|
${BUILDDIR_WINXP_32}/spicetools/spicecfg.exe \
|
||||||
|
${BUILDDIR_WINXP_32}/spicetools/32/spice.exe
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# generate PDBs
|
# generate PDBs
|
||||||
if false # ((DEBUG > 0))
|
if false # ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
@@ -191,6 +269,12 @@ rm -rf ${OUTDIR_EXTRAS}
|
|||||||
mkdir -p ${OUTDIR_EXTRAS}
|
mkdir -p ${OUTDIR_EXTRAS}
|
||||||
mkdir -p ${OUTDIR_EXTRAS}/largeaddressaware
|
mkdir -p ${OUTDIR_EXTRAS}/largeaddressaware
|
||||||
mkdir -p ${OUTDIR_EXTRAS}/linux
|
mkdir -p ${OUTDIR_EXTRAS}/linux
|
||||||
|
mkdir -p ${OUTDIR_EXTRAS}/sdk/samples/32
|
||||||
|
mkdir -p ${OUTDIR_EXTRAS}/sdk/samples/64
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
mkdir -p ${OUTDIR_EXTRAS}/winxp
|
||||||
|
fi
|
||||||
|
|
||||||
if false # ((DEBUG > 0))
|
if false # ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
@@ -199,16 +283,8 @@ then
|
|||||||
cp ${BUILDDIR_32}/spicetools/spicecfg-pdb.pdb ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/spicecfg-pdb.pdb ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.pdb ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.pdb ${OUTDIR} 2>/dev/null
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.pdb ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.pdb ${OUTDIR} 2>/dev/null
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
else
|
else
|
||||||
# release files
|
# release files
|
||||||
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
|
||||||
@@ -216,16 +292,21 @@ else
|
|||||||
cp ${BUILDDIR_32}/spicetools/32/spice.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice_laa.exe ${OUTDIR_EXTRAS}/largeaddressaware/spice.exe 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice_laa.exe ${OUTDIR_EXTRAS}/largeaddressaware/spice.exe 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice_linux.exe ${OUTDIR_EXTRAS}/linux/spice.exe 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice_linux.exe ${OUTDIR_EXTRAS}/linux/spice.exe 2>/dev/null
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64_linux.exe ${OUTDIR_EXTRAS}/linux/spice64.exe 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64_linux.exe ${OUTDIR_EXTRAS}/linux/spice64.exe 2>/dev/null
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
|
||||||
cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
|
||||||
cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
||||||
|
cp ${BUILDDIR_32}/spicetools/32/sdk_sample_v0_flat_c.dll ${OUTDIR_EXTRAS}/sdk/samples/32/v0_flat_c.dll 2>/dev/null
|
||||||
|
cp ${BUILDDIR_64}/spicetools/64/sdk_sample_v0_flat_c.dll ${OUTDIR_EXTRAS}/sdk/samples/64/v0_flat_c.dll 2>/dev/null
|
||||||
|
cp ${BUILDDIR_64}/spicetools/64/sdk_sample_v0_cpp.dll ${OUTDIR_EXTRAS}/sdk/samples/64/v0_cpp.dll 2>/dev/null
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
cp ${BUILDDIR_WINXP_32}/spicetools/spicecfg.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null
|
||||||
|
cp ${BUILDDIR_WINXP_32}/spicetools/32/spice.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null
|
||||||
|
cp ${BUILDDIR_WINXP_64}/spicetools/64/spice64.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# pack source files to output directory
|
# pack source files to output directory
|
||||||
@@ -234,10 +315,13 @@ mkdir -p ${OUTDIR}/src
|
|||||||
if ((INCLUDE_SRC > 0))
|
if ((INCLUDE_SRC > 0))
|
||||||
then
|
then
|
||||||
echo "Generating source file archive..."
|
echo "Generating source file archive..."
|
||||||
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \
|
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > "${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz" 2>/dev/null || \
|
||||||
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# sdk headers
|
||||||
|
cp -r ./sdk/include ${OUTDIR_EXTRAS}/sdk
|
||||||
|
|
||||||
# copy resources
|
# copy resources
|
||||||
rm -rf ${OUTDIR_EXTRAS}/api
|
rm -rf ${OUTDIR_EXTRAS}/api
|
||||||
mkdir ${OUTDIR_EXTRAS}/api
|
mkdir ${OUTDIR_EXTRAS}/api
|
||||||
@@ -249,11 +333,11 @@ if ((DIST_ENABLE > 0))
|
|||||||
then
|
then
|
||||||
echo "Building dist..."
|
echo "Building dist..."
|
||||||
mkdir -p ${DIST_FOLDER}
|
mkdir -p ${DIST_FOLDER}
|
||||||
rm -rf ${DIST_FOLDER}/${DIST_NAME}
|
rm -rf "${DIST_FOLDER}/${DIST_NAME}"
|
||||||
pushd ${OUTDIR}/.. > /dev/null
|
pushd ${OUTDIR} > /dev/null
|
||||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME}" . -x "extras/*" -z <<< "$DIST_COMMENT"
|
||||||
echo "Building extras..."
|
echo "Building extras..."
|
||||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x -z <<< "$DIST_COMMENT"
|
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS}" . -z <<< "$DIST_COMMENT"
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
docker build --pull external/docker -t spicetools/deps
|
docker build --pull external/docker -t spicetools/deps
|
||||||
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice
|
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice
|
||||||
docker run --rm -it -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin -v %cd%/.ccache:/src/src/spice2x/.ccache spicetools/spice %*
|
docker run --rm -i -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin -v %cd%/.ccache:/src/src/spice2x/.ccache spicetools/spice %*
|
||||||
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
|
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
|
||||||
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
|
||||||
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
|
||||||
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice64.exe bin\spice2x\spice64-pdb.exe bin\spice2x\spice64-pdb.pdb
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice64.exe bin\spice2x\spice64-pdb.exe bin\spice2x\spice64-pdb.pdb
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env 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
|
set -eu
|
||||||
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
|
||||||
|
|
||||||
|
# Interactive TTY if available, so docker build can be Ctrl+C'd
|
||||||
|
DOCKER_FLAGS=""
|
||||||
|
[ -t 0 ] && DOCKER_FLAGS="-it"
|
||||||
|
|
||||||
|
docker run $DOCKER_FLAGS --rm -v "$PWD/dist:/src/src/spice2x/dist" -v "$PWD/bin:/src/src/spice2x/bin" -v "$PWD/.ccache:/src/src/spice2x/.ccache" spicetools/spice "$@"
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ IDR_LICENSES RCDATA "../licenses.txt"
|
|||||||
IDR_PATCHES RCDATA "../build/patches.json"
|
IDR_PATCHES RCDATA "../build/patches.json"
|
||||||
IDR_README RCDATA "../readme.txt"
|
IDR_README RCDATA "../readme.txt"
|
||||||
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
||||||
|
IDR_CONTROLLER_PRESETS_BUILTIN RCDATA "../build/controller_presets.xml"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+106
-15
@@ -90,6 +90,10 @@ std::string Analog::getDisplayString(rawinput::RawInputManager *manager) {
|
|||||||
return "MIDI Unknown Index " + indexString + " (" + device->desc + ")";
|
return "MIDI Unknown Index " + indexString + " (" + device->desc + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD:
|
||||||
|
return fmt::format("{} ({})",
|
||||||
|
xinput::get_analog_string(static_cast<xinput::XInputAnalogEnum>(index)),
|
||||||
|
device->desc);
|
||||||
case rawinput::DESTROYED:
|
case rawinput::DESTROYED:
|
||||||
return "Device unplugged (" + indexString + ")";
|
return "Device unplugged (" + indexString + ")";
|
||||||
default:
|
default:
|
||||||
@@ -203,22 +207,38 @@ float Analog::applyMultiplier(float value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float Analog::normalizeAnalogValue(float value) {
|
float Analog::normalizeAnalogValue(float value) {
|
||||||
// effectively the same as fmodf(value, 1.f)
|
if (getType() == GameAPI::Analogs::AnalogType::Circular) {
|
||||||
// for small values, this is MUCH faster than fmodf.
|
// effectively the same as fmodf(value, 1.f)
|
||||||
float new_value = value;
|
// for small values, this is MUCH faster than fmodf.
|
||||||
while (new_value > 1.f) {
|
float new_value = value;
|
||||||
new_value -= 1.f;
|
while (new_value > 1.f) {
|
||||||
|
new_value -= 1.f;
|
||||||
|
}
|
||||||
|
while (new_value < 0.f) {
|
||||||
|
new_value += 1.f;
|
||||||
|
}
|
||||||
|
return new_value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// clamp to [0, 1] range
|
||||||
|
return std::clamp(value, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
while (new_value < 0.f) {
|
|
||||||
new_value += 1.f;
|
|
||||||
}
|
|
||||||
return new_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Analog::applyDeadzone(float raw_value) {
|
float Analog::applyDeadzone(float raw_value) {
|
||||||
float value = raw_value;
|
float value = raw_value;
|
||||||
const auto deadzone = this->getDeadzone();
|
auto deadzone = this->getDeadzone();
|
||||||
if (deadzone > 0) {
|
|
||||||
|
// in the past, positive deadzone applied in the center, negative deadzone applied to 0
|
||||||
|
// after each analog value received a type (circular/linear) this has been simpliifed to
|
||||||
|
// positive values only since we can figure out where the rest value is
|
||||||
|
// for back compat, treat negative value as positive
|
||||||
|
if (deadzone < 0.f) {
|
||||||
|
deadzone = -deadzone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// relative mode assumes that user is using a stick, so center is neutral regardless of analog type
|
||||||
|
if (getType() != GameAPI::Analogs::AnalogType::LinearPositive || isRelativeMode()) {
|
||||||
|
|
||||||
// calculate values
|
// calculate values
|
||||||
const auto delta = value - 0.5f;
|
const auto delta = value - 0.5f;
|
||||||
@@ -251,7 +271,7 @@ float Analog::applyDeadzone(float raw_value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (deadzone < 0) {
|
} else {
|
||||||
|
|
||||||
// invert for mirror
|
// invert for mirror
|
||||||
if (this->getDeadzoneMirror()) {
|
if (this->getDeadzoneMirror()) {
|
||||||
@@ -259,8 +279,8 @@ float Analog::applyDeadzone(float raw_value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deadzone from minimum value
|
// deadzone from minimum value
|
||||||
if (deadzone > -1 && value > -deadzone) {
|
if (deadzone < 1.f && deadzone < value) {
|
||||||
value = std::min(1.f, (value + deadzone) / (1.f + deadzone));
|
value = std::max(0.f, (value - deadzone) / (1.f - deadzone));
|
||||||
} else {
|
} else {
|
||||||
value = 0.f;
|
value = 0.f;
|
||||||
}
|
}
|
||||||
@@ -271,4 +291,75 @@ float Analog::applyDeadzone(float raw_value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Analog::getRelativeModeValue(float raw_value) {
|
||||||
|
const auto now = get_performance_seconds();
|
||||||
|
auto delta_time = now - this->rel_mode_last_read_time_s;
|
||||||
|
|
||||||
|
if (this->rel_mode_last_read_time_s != 0.f) {
|
||||||
|
// some heuristics to prevent huge jumps:
|
||||||
|
// * if we went for more than 250ms without polls, discard it (e.g., during loading screens)
|
||||||
|
// * cap the delta at 100ms to prevent huge jumps in case of very infrequent polling
|
||||||
|
if (delta_time < 0.f || 0.25f < delta_time) {
|
||||||
|
delta_time = 0.f;
|
||||||
|
} else if (delta_time > 0.1f) {
|
||||||
|
delta_time = 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scale [0, 1] to [-1, 1] to simplify calculations
|
||||||
|
const auto delta_raw_value = (raw_value - 0.5f) * 2.f;
|
||||||
|
|
||||||
|
// target is one revolution per second at max speed at 1.0 sensitivity
|
||||||
|
auto adjusted_delta_value = delta_raw_value * delta_time;
|
||||||
|
|
||||||
|
// multiplier / divisor
|
||||||
|
if (this->getMultiplier() > 1) {
|
||||||
|
adjusted_delta_value *= this->getMultiplier();
|
||||||
|
} else if (this->getMultiplier() < -1) {
|
||||||
|
adjusted_delta_value /= -this->getMultiplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sensitivity
|
||||||
|
if (this->isSensitivitySet()) {
|
||||||
|
adjusted_delta_value *= this->getSensitivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the new absolute value
|
||||||
|
this->rel_mode_absolute_value += adjusted_delta_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update for next poll
|
||||||
|
this->rel_mode_last_read_time_s = now;
|
||||||
|
this->rel_mode_absolute_value = normalizeAnalogValue(this->rel_mode_absolute_value);
|
||||||
|
return this->rel_mode_absolute_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Analog::getDelayedValue(float raw_value) {
|
||||||
|
const double delay_ms = static_cast<double>(this->getDelayMs());
|
||||||
|
if (delay_ms == 0.0) {
|
||||||
|
return raw_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// always push a new value
|
||||||
|
const auto now = get_performance_milliseconds();
|
||||||
|
this->delayed_inputs.emplace(now, raw_value);
|
||||||
|
|
||||||
|
// drain the queue down to reasonable length to prevent unconstrained growth
|
||||||
|
// this would accommodate 1 second at ~1000Hz which is overkill
|
||||||
|
// (UI only allows for 250ms of delay)
|
||||||
|
while (this->delayed_inputs.size() > 1024) {
|
||||||
|
this->delayed_inputs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop until we find the oldest value still inside the delay window
|
||||||
|
while (this->delayed_inputs.size() > 1) {
|
||||||
|
const auto delta_t = now - this->delayed_inputs.front().time_in_ms;
|
||||||
|
if (delta_t <= delay_ms) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->delayed_inputs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->delayed_inputs.front().value;
|
||||||
|
}
|
||||||
|
|||||||
+47
-18
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -13,12 +14,32 @@ namespace rawinput {
|
|||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameAPI::Analogs {
|
||||||
|
enum class AnalogType {
|
||||||
|
// default; values wrap around (below 0 turns into 1, over 1 is 0)
|
||||||
|
// knobs, turntables
|
||||||
|
Circular = 0,
|
||||||
|
|
||||||
|
// typical joystick that rests at the center and caps at [0, 1]
|
||||||
|
LinearCentered = 1,
|
||||||
|
|
||||||
|
// one-directional value (sliders and instruments like piano/drum velocity)
|
||||||
|
// starts at 0 and goes up to 1
|
||||||
|
LinearPositive = 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct AnalogMovingAverage {
|
struct AnalogMovingAverage {
|
||||||
double time_in_ms;
|
double time_in_ms;
|
||||||
float sine;
|
float sine;
|
||||||
float cosine;
|
float cosine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AnalogDelayEntry {
|
||||||
|
double time_in_ms;
|
||||||
|
float value;
|
||||||
|
};
|
||||||
|
|
||||||
class Analog {
|
class Analog {
|
||||||
private:
|
private:
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -31,10 +52,11 @@ private:
|
|||||||
float last_state = 0.5f;
|
float last_state = 0.5f;
|
||||||
bool sensitivity_set = false;
|
bool sensitivity_set = false;
|
||||||
bool deadzone_set = false;
|
bool deadzone_set = false;
|
||||||
|
GameAPI::Analogs::AnalogType type = GameAPI::Analogs::AnalogType::Circular;
|
||||||
|
|
||||||
// smoothing function
|
// smoothing function
|
||||||
bool smoothing = false;
|
bool smoothing = false;
|
||||||
std::array<AnalogMovingAverage, ANALOG_HISTORY_CNT> vector_history;
|
std::array<AnalogMovingAverage, ANALOG_HISTORY_CNT> vector_history = {};
|
||||||
int vector_history_index = 0;
|
int vector_history_index = 0;
|
||||||
float smoothed_last_state = 0.f;
|
float smoothed_last_state = 0.f;
|
||||||
|
|
||||||
@@ -48,12 +70,13 @@ private:
|
|||||||
unsigned short divisor_region = 0;
|
unsigned short divisor_region = 0;
|
||||||
|
|
||||||
// relative input mode
|
// relative input mode
|
||||||
float absolute_value_for_rel_mode = 0.5f;
|
float rel_mode_absolute_value = 0.5f;
|
||||||
|
float rel_mode_last_read_time_s = 0.f;
|
||||||
bool relative_mode = false;
|
bool relative_mode = false;
|
||||||
|
|
||||||
// circular buffer (delayed input)
|
// delay
|
||||||
int delay_buffer_depth = 0;
|
uint32_t delay_ms = 0;
|
||||||
std::queue<float> delay_buffer;
|
std::queue<AnalogDelayEntry> delayed_inputs;
|
||||||
|
|
||||||
float calculateAngularDifference(float old_rads, float new_rads);
|
float calculateAngularDifference(float old_rads, float new_rads);
|
||||||
float normalizeAngle(float rads);
|
float normalizeAngle(float rads);
|
||||||
@@ -66,7 +89,8 @@ public:
|
|||||||
float override_state = 0.5f;
|
float override_state = 0.5f;
|
||||||
|
|
||||||
explicit Analog(std::string name) : name(std::move(name)) {
|
explicit Analog(std::string name) : name(std::move(name)) {
|
||||||
vector_history.fill({0.0, 0.f, 0.f});
|
};
|
||||||
|
explicit Analog(std::string name, GameAPI::Analogs::AnalogType type) : name(std::move(name)), type(type) {
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||||
@@ -74,6 +98,8 @@ public:
|
|||||||
float applyAngularSensitivity(float raw_rads);
|
float applyAngularSensitivity(float raw_rads);
|
||||||
float applyMultiplier(float raw_value);
|
float applyMultiplier(float raw_value);
|
||||||
float applyDeadzone(float raw_value);
|
float applyDeadzone(float raw_value);
|
||||||
|
float getRelativeModeValue(float raw_value);
|
||||||
|
float getDelayedValue(float raw_value);
|
||||||
|
|
||||||
inline bool isSet() {
|
inline bool isSet() {
|
||||||
if (this->override_enabled) {
|
if (this->override_enabled) {
|
||||||
@@ -90,7 +116,8 @@ public:
|
|||||||
deadzone_mirror = false;
|
deadzone_mirror = false;
|
||||||
setMultiplier(1);
|
setMultiplier(1);
|
||||||
setRelativeMode(false);
|
setRelativeMode(false);
|
||||||
setDelayBufferDepth(0);
|
setLastState(0.5f);
|
||||||
|
setDelayMs(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void clearBindings() {
|
inline void clearBindings() {
|
||||||
@@ -192,25 +219,27 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void setRelativeMode(bool relative_mode) {
|
inline void setRelativeMode(bool relative_mode) {
|
||||||
|
if (relative_mode) {
|
||||||
|
this->smoothing = false;
|
||||||
|
}
|
||||||
this->relative_mode = relative_mode;
|
this->relative_mode = relative_mode;
|
||||||
this->absolute_value_for_rel_mode = 0.5f;
|
this->rel_mode_absolute_value = 0.5f;
|
||||||
|
this->rel_mode_last_read_time_s = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float getAbsoluteValue(float relative_delta) {
|
inline GameAPI::Analogs::AnalogType getType() const {
|
||||||
this->absolute_value_for_rel_mode =
|
return this->type;
|
||||||
normalizeAnalogValue(this->absolute_value_for_rel_mode + relative_delta);
|
|
||||||
return this->absolute_value_for_rel_mode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int getDelayBufferDepth() const {
|
inline void setType(GameAPI::Analogs::AnalogType type) {
|
||||||
return this->delay_buffer_depth;
|
this->type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setDelayBufferDepth(int depth) {
|
inline uint32_t getDelayMs() const {
|
||||||
this->delay_buffer_depth = depth;
|
return this->delay_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::queue<float> &getDelayBuffer() {
|
inline void setDelayMs(uint32_t delay_ms) {
|
||||||
return this->delay_buffer;
|
this->delay_ms = delay_ms;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+211
-68
@@ -1,5 +1,6 @@
|
|||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "launcher/superexit.h"
|
#include "launcher/superexit.h"
|
||||||
@@ -37,7 +38,11 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
|||||||
for (auto &bt : buttons) {
|
for (auto &bt : buttons) {
|
||||||
if (name == bt.getName()) {
|
if (name == bt.getName()) {
|
||||||
button_found = true;
|
button_found = true;
|
||||||
sorted.push_back(bt);
|
Button button_new = bt;
|
||||||
|
if (vkey_defaults) {
|
||||||
|
button_new.setVKeyDefault(vkey_defaults->at(index));
|
||||||
|
}
|
||||||
|
sorted.push_back(button_new);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,6 +52,7 @@ std::vector<Button> GameAPI::Buttons::sortButtons(
|
|||||||
|
|
||||||
if (vkey_defaults) {
|
if (vkey_defaults) {
|
||||||
button.setVKey(vkey_defaults->at(index));
|
button.setVKey(vkey_defaults->at(index));
|
||||||
|
button.setVKeyDefault(vkey_defaults->at(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,9 +199,19 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
|||||||
if (vKey < value_states->size()) {
|
if (vKey < value_states->size()) {
|
||||||
auto value = value_states->at(vKey);
|
auto value = value_states->at(vKey);
|
||||||
if (current_button->getAnalogType() == BAT_POSITIVE) {
|
if (current_button->getAnalogType() == BAT_POSITIVE) {
|
||||||
state = value > 0.6f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
float threshold = 0.6f;
|
||||||
|
if (current_button->getBatThreshold() > 0) {
|
||||||
|
threshold = std::clamp(current_button->getBatThreshold() / 100.f, 0.01f, 0.99f);
|
||||||
|
}
|
||||||
|
state = value > threshold ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
|
||||||
} else if (current_button->getAnalogType() == BAT_NEGATIVE) {
|
} else if (current_button->getAnalogType() == BAT_NEGATIVE) {
|
||||||
state = value < 0.4f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
float threshold = 0.4f;
|
||||||
|
if (current_button->getBatThreshold() > 0) {
|
||||||
|
threshold = std::clamp((100 - current_button->getBatThreshold()) / 100.f, 0.01f, 0.99f);
|
||||||
|
}
|
||||||
|
state = value < threshold ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
state = value > 0.01f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
state = value > 0.01f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
}
|
}
|
||||||
@@ -384,6 +400,16 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
|||||||
}
|
}
|
||||||
case rawinput::PIUIO_DEVICE: {
|
case rawinput::PIUIO_DEVICE: {
|
||||||
state = device->piuioDev->IsPressed(vKey) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
state = device->piuioDev->IsPressed(vKey) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
assert(reinterpret_cast<uintptr_t>(device->handle) < XUSER_MAX_COUNT);
|
||||||
|
const auto player = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(device->handle));
|
||||||
|
state = manager->XINPUT_MGR->is_button_pressed(
|
||||||
|
player,
|
||||||
|
static_cast<xinput::XInputButtonEnum>(vKey)) ?
|
||||||
|
BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -581,7 +607,7 @@ float Buttons::getVelocity(std::unique_ptr<rawinput::RawInputManager> &manager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, rawinput::Device *device, Analog &analog) {
|
||||||
float value = 0.5f;
|
float value = 0.5f;
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return value;
|
return value;
|
||||||
@@ -644,88 +670,107 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
|||||||
case rawinput::HID: {
|
case rawinput::HID: {
|
||||||
|
|
||||||
// get value
|
// get value
|
||||||
if (inverted) {
|
if (index < device->hidInfo->value_states.size()) {
|
||||||
value = 1.f - device->hidInfo->value_states[index];
|
if (inverted) {
|
||||||
} else {
|
value = 1.f - device->hidInfo->value_states[index];
|
||||||
value = device->hidInfo->value_states[index];
|
} else {
|
||||||
|
value = device->hidInfo->value_states[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// deadzone
|
// deadzone
|
||||||
if (analog.isDeadzoneSet()) {
|
// do not apply deadzone to circular analogs since it doesn't make sense (except in relative mode)
|
||||||
|
if (analog.isDeadzoneSet() &&
|
||||||
|
(analog.getType() != AnalogType::Circular || analog.isRelativeMode())) {
|
||||||
value = analog.applyDeadzone(value);
|
value = analog.applyDeadzone(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analog.isRelativeMode()) {
|
if (analog.getType() == AnalogType::Circular) {
|
||||||
float relative_delta = value - 0.5f;
|
|
||||||
// built-in scaling to make values reasonable
|
|
||||||
relative_delta /= 80.f;
|
|
||||||
|
|
||||||
// integer multiplier/divisor
|
if (analog.isRelativeMode()) {
|
||||||
const auto mult = analog.getMultiplier();
|
value = analog.getRelativeModeValue(value);
|
||||||
if (mult < -1) {
|
|
||||||
relative_delta /= -mult;
|
|
||||||
} else if (1 < mult) {
|
|
||||||
relative_delta *= mult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sensitivity (ranges from 0.0 to 4.0)
|
} else {
|
||||||
if (analog.isSensitivitySet()) {
|
// integer multiplier
|
||||||
relative_delta *= analog.getSensitivity();
|
value = analog.applyMultiplier(value);
|
||||||
}
|
|
||||||
|
|
||||||
// translate relative movement to absolute value
|
// smoothing/sensitivity
|
||||||
value = analog.getAbsoluteValue(relative_delta);
|
if (analog.getSmoothing() || analog.isSensitivitySet()) {
|
||||||
|
float rads = value * (float) M_TAU;
|
||||||
|
|
||||||
} else {
|
// smoothing
|
||||||
// integer multiplier
|
if (analog.getSmoothing()) {
|
||||||
value = analog.applyMultiplier(value);
|
|
||||||
|
|
||||||
// smoothing/sensitivity
|
// preserve direction
|
||||||
if (analog.getSmoothing() || analog.isSensitivitySet()) {
|
if (rads >= M_TAU) {
|
||||||
float rads = value * (float) M_TAU;
|
rads -= 0.0001f;
|
||||||
|
}
|
||||||
|
|
||||||
// smoothing
|
// calculate angle
|
||||||
if (analog.getSmoothing()) {
|
rads = analog.getSmoothedValue(rads);
|
||||||
|
|
||||||
// preserve direction
|
|
||||||
if (rads >= M_TAU) {
|
|
||||||
rads -= 0.0001f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate angle
|
// sensitivity
|
||||||
rads = analog.getSmoothedValue(rads);
|
if (analog.isSensitivitySet()) {
|
||||||
|
rads = analog.applyAngularSensitivity(rads);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply to value
|
||||||
|
value = rads * (float) M_1_TAU;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// sensitivity
|
||||||
|
if (analog.isSensitivitySet()) {
|
||||||
|
// adjust curve
|
||||||
|
// values < 1.f : less sensitive around neutral
|
||||||
|
// values > 1.f : more sensitive around neutral
|
||||||
|
float curve = analog.getSensitivity();
|
||||||
|
if (curve <= 0.f) {
|
||||||
|
curve = 0.01f;
|
||||||
|
}
|
||||||
|
curve = 1.f / curve;
|
||||||
|
|
||||||
|
if (analog.getType() == AnalogType::LinearCentered) {
|
||||||
|
// convert 0.0..1.0 to -1.0..+1.0
|
||||||
|
float signed_raw = (value - 0.5f) * 2.0f;
|
||||||
|
// apply curve
|
||||||
|
float sign = signed_raw < 0.0f ? -1.0f : 1.0f;
|
||||||
|
float magnitude = fabsf(signed_raw);
|
||||||
|
float curved = sign * powf(magnitude, curve);
|
||||||
|
// convert back to 0.0..1.0
|
||||||
|
value = curved * 0.5f + 0.5f;
|
||||||
|
} else {
|
||||||
|
value = powf(value, curve);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sensitivity
|
value = std::clamp(value, 0.f, 1.f);
|
||||||
if (analog.isSensitivitySet()) {
|
}
|
||||||
rads = analog.applyAngularSensitivity(rads);
|
|
||||||
|
// multiplier / divisor
|
||||||
|
if (analog.getMultiplier() < -1) {
|
||||||
|
if (analog.getType() == AnalogType::LinearCentered) {
|
||||||
|
value = (value - 0.5f) / (-analog.getMultiplier()) + 0.5f;
|
||||||
|
} else {
|
||||||
|
value /= -analog.getMultiplier();
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply to value
|
value = std::clamp(value, 0.f, 1.f);
|
||||||
value = rads * (float) M_1_TAU;
|
|
||||||
|
} else if (analog.getMultiplier() > 1) {
|
||||||
|
if (analog.getType() == AnalogType::LinearCentered) {
|
||||||
|
value = (value - 0.5f) * analog.getMultiplier() + 0.5f;
|
||||||
|
} else {
|
||||||
|
value *= analog.getMultiplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
value = std::clamp(value, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delay
|
// delay
|
||||||
if (0 < analog.getDelayBufferDepth()) {
|
if (analog.getDelayMs() > 0) {
|
||||||
auto& queue = analog.getDelayBuffer();
|
value = analog.getDelayedValue(value);
|
||||||
|
|
||||||
// ensure the queue isn't too long; drop old values
|
|
||||||
while (analog.getDelayBufferDepth() <= (int)queue.size()) {
|
|
||||||
queue.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// always push new value
|
|
||||||
queue.push(value);
|
|
||||||
|
|
||||||
// get a new value to return
|
|
||||||
if ((int)queue.size() < analog.getDelayBufferDepth()) {
|
|
||||||
// not enough in the queue, stall for now, shouldn't happen often
|
|
||||||
value = analog.getLastState();
|
|
||||||
} else {
|
|
||||||
value = queue.front();
|
|
||||||
queue.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -760,6 +805,18 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
assert(reinterpret_cast<uintptr_t>(device->handle) < XUSER_MAX_COUNT);
|
||||||
|
const auto player = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(device->handle));
|
||||||
|
value = manager->XINPUT_MGR->get_analog_state(
|
||||||
|
player,
|
||||||
|
static_cast<xinput::XInputAnalogEnum>(index));
|
||||||
|
// invert value
|
||||||
|
if (inverted) {
|
||||||
|
value = 1.f - value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -799,6 +856,42 @@ std::vector<Analog> GameAPI::Analogs::sortAnalogs(
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<Analog> sortAnalogsWithTypeInternal(
|
||||||
|
std::vector<Analog> &analogs,
|
||||||
|
const std::initializer_list<GameAPI::Analogs::AnalogWithType> list) {
|
||||||
|
|
||||||
|
std::vector<Analog> sorted;
|
||||||
|
|
||||||
|
bool analog_found;
|
||||||
|
for (auto &a : list) {
|
||||||
|
analog_found = false;
|
||||||
|
|
||||||
|
for (auto &analog : analogs) {
|
||||||
|
if (a.name == analog.getName()) {
|
||||||
|
analog_found = true;
|
||||||
|
analog.setType(a.type);
|
||||||
|
sorted.push_back(analog);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!analog_found) {
|
||||||
|
sorted.emplace_back(a.name, a.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameAPI::Analogs::sortAnalogsWithType(
|
||||||
|
std::vector<Analog> *analogs,
|
||||||
|
const std::initializer_list<AnalogWithType> list) {
|
||||||
|
|
||||||
|
if (analogs) {
|
||||||
|
*analogs = sortAnalogsWithTypeInternal(*analogs, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, Analog &analog) {
|
float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, Analog &analog) {
|
||||||
|
|
||||||
// check override
|
// check override
|
||||||
@@ -815,7 +908,7 @@ float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, Analog &ana
|
|||||||
return analog.getLastState();
|
return analog.getLastState();
|
||||||
}
|
}
|
||||||
|
|
||||||
float state = getState(device, analog);
|
float state = getState(manager, device, analog);
|
||||||
analog.setLastState(state);
|
analog.setLastState(state);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
@@ -859,7 +952,45 @@ std::vector<Light> GameAPI::Lights::sortLights(
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
|
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::RawInputManager *manager, rawinput::Device *device, int index, float value) {
|
||||||
|
|
||||||
// check device
|
// check device
|
||||||
if (!device) {
|
if (!device) {
|
||||||
@@ -946,6 +1077,18 @@ void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float valu
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
assert(reinterpret_cast<uintptr_t>(device->handle) < XUSER_MAX_COUNT);
|
||||||
|
const auto player = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(device->handle));
|
||||||
|
if (index < static_cast<int>(xinput::XInputOutputEnum::COUNT)) {
|
||||||
|
manager->XINPUT_MGR->set_output_state(
|
||||||
|
player, static_cast<xinput::XInputOutputEnum>(index), value);
|
||||||
|
// no need to set output_pending; xinput output handled immediately
|
||||||
|
} else {
|
||||||
|
log_warning("api", "invalid xinput light index: {}", index);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -971,9 +1114,9 @@ void GameAPI::Lights::writeLight(rawinput::RawInputManager *manager, Light &ligh
|
|||||||
|
|
||||||
// write state
|
// write state
|
||||||
if (light.override_enabled) {
|
if (light.override_enabled) {
|
||||||
writeLight(device, light.getIndex(), light.override_state);
|
writeLight(manager, device, light.getIndex(), light.override_state);
|
||||||
} else {
|
} else {
|
||||||
writeLight(device, light.getIndex(), value);
|
writeLight(manager, device, light.getIndex(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-30
@@ -6,24 +6,20 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "option.h"
|
#include "option.h"
|
||||||
|
|
||||||
|
#include "button.h"
|
||||||
|
#include "analog.h"
|
||||||
|
#include "light.h"
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
struct Device;
|
struct Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Button;
|
|
||||||
class Analog;
|
|
||||||
class Light;
|
|
||||||
class Game;
|
class Game;
|
||||||
class Option;
|
class Option;
|
||||||
|
|
||||||
namespace GameAPI {
|
namespace GameAPI {
|
||||||
namespace Buttons {
|
namespace Buttons {
|
||||||
enum State {
|
|
||||||
BUTTON_PRESSED = true,
|
|
||||||
BUTTON_NOT_PRESSED = false
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the config and returns the buttons set by the user.
|
* Parses the config and returns the buttons set by the user.
|
||||||
*
|
*
|
||||||
@@ -89,14 +85,15 @@ namespace GameAPI {
|
|||||||
const std::vector<Analog> &analogs,
|
const std::vector<Analog> &analogs,
|
||||||
const std::vector<std::string> &analog_names);
|
const std::vector<std::string> &analog_names);
|
||||||
|
|
||||||
template<typename T>
|
struct AnalogWithType {
|
||||||
void sortAnalogs(std::vector<Analog> *analogs, T t) {
|
const std::string name;
|
||||||
const std::vector<std::string> analog_names { t };
|
const AnalogType type;
|
||||||
|
|
||||||
if (analogs) {
|
AnalogWithType(std::string name, AnalogType type) :
|
||||||
*analogs = GameAPI::Analogs::sortAnalogs(*analogs, analog_names);
|
name(std::move(name)), type(type) {}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
void sortAnalogsWithType(std::vector<Analog> *analogs, const std::initializer_list<AnalogWithType> list);
|
||||||
|
|
||||||
template<typename T, typename... Rest>
|
template<typename T, typename... Rest>
|
||||||
void sortAnalogs(std::vector<Analog> *analogs, T t, Rest... rest) {
|
void sortAnalogs(std::vector<Analog> *analogs, T t, Rest... rest) {
|
||||||
@@ -107,27 +104,19 @@ namespace GameAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float getState(rawinput::Device *device, Analog &analog);
|
float getState(rawinput::RawInputManager *manager, rawinput::Device *device, Analog &analog);
|
||||||
float getState(rawinput::RawInputManager *manager, Analog &analog);
|
float getState(rawinput::RawInputManager *manager, Analog &analog);
|
||||||
float getState(std::unique_ptr<rawinput::RawInputManager> &manager, Analog &analog);
|
float getState(std::unique_ptr<rawinput::RawInputManager> &manager, Analog &analog);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Lights {
|
namespace Lights {
|
||||||
|
|
||||||
std::vector<Light> getLights(const std::string &game_name);
|
std::vector<Light> getLights(const std::string &game_name);
|
||||||
|
|
||||||
std::vector<Light> sortLights(
|
std::vector<Light> sortLights(
|
||||||
const std::vector<Light> &lights,
|
const std::vector<Light> &lights,
|
||||||
const std::vector<std::string> &light_names);
|
const std::vector<std::string> &light_names);
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void sortLights(std::vector<Light> *lights, T t) {
|
|
||||||
const std::vector<std::string> light_names { t };
|
|
||||||
|
|
||||||
if (lights) {
|
|
||||||
*lights = GameAPI::Lights::sortLights(*lights, light_names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename... Rest>
|
template<typename T, typename... Rest>
|
||||||
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
|
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
|
||||||
const std::vector<std::string> light_names { t, rest... };
|
const std::vector<std::string> light_names { t, rest... };
|
||||||
@@ -137,7 +126,17 @@ namespace GameAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeLight(rawinput::Device *device, int index, float value);
|
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::RawInputManager *manager, rawinput::Device *device, int index, float value);
|
||||||
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
||||||
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
||||||
|
|
||||||
@@ -152,7 +151,3 @@ namespace GameAPI {
|
|||||||
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
|
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "button.h"
|
|
||||||
#include "analog.h"
|
|
||||||
#include "light.h"
|
|
||||||
|
|||||||
+60
-31
@@ -44,7 +44,7 @@ std::string Button::getVKeyString() {
|
|||||||
case 0x0C:
|
case 0x0C:
|
||||||
return "Clear";
|
return "Clear";
|
||||||
case 0x0D:
|
case 0x0D:
|
||||||
return "Enter";
|
return "Enter/Return";
|
||||||
case 0x10:
|
case 0x10:
|
||||||
return "Shift";
|
return "Shift";
|
||||||
case 0x11:
|
case 0x11:
|
||||||
@@ -58,8 +58,28 @@ std::string Button::getVKeyString() {
|
|||||||
return "Pause";
|
return "Pause";
|
||||||
case 0x14:
|
case 0x14:
|
||||||
return "Caps Lock";
|
return "Caps Lock";
|
||||||
|
case 0x15:
|
||||||
|
return "IME Kana/Hangul";
|
||||||
|
case 0x16:
|
||||||
|
return "IME On";
|
||||||
|
case 0x17:
|
||||||
|
return "IME Junja";
|
||||||
|
case 0x18:
|
||||||
|
return "IME Final";
|
||||||
|
case 0x19:
|
||||||
|
return "IME Hanja/Kanji";
|
||||||
|
case 0x1A:
|
||||||
|
return "IME Off";
|
||||||
case 0x1B:
|
case 0x1B:
|
||||||
return "Escape";
|
return "Escape";
|
||||||
|
case 0x1C:
|
||||||
|
return "IME Convert";
|
||||||
|
case 0x1D:
|
||||||
|
return "IME Nonconvert";
|
||||||
|
case 0x1E:
|
||||||
|
return "IME Accept";
|
||||||
|
case 0x1F:
|
||||||
|
return "IME Mode Change";
|
||||||
case 0x20:
|
case 0x20:
|
||||||
return "Space";
|
return "Space";
|
||||||
case 0x21:
|
case 0x21:
|
||||||
@@ -163,37 +183,37 @@ std::string Button::getVKeyString() {
|
|||||||
case 0x5D:
|
case 0x5D:
|
||||||
return "Apps";
|
return "Apps";
|
||||||
case 0x60:
|
case 0x60:
|
||||||
return "Num 0";
|
return "Numpad 0";
|
||||||
case 0x61:
|
case 0x61:
|
||||||
return "Num 1";
|
return "Numpad 1";
|
||||||
case 0x62:
|
case 0x62:
|
||||||
return "Num 2";
|
return "Numpad 2";
|
||||||
case 0x63:
|
case 0x63:
|
||||||
return "Num 3";
|
return "Numpad 3";
|
||||||
case 0x64:
|
case 0x64:
|
||||||
return "Num 4";
|
return "Numpad 4";
|
||||||
case 0x65:
|
case 0x65:
|
||||||
return "Num 5";
|
return "Numpad 5";
|
||||||
case 0x66:
|
case 0x66:
|
||||||
return "Num 6";
|
return "Numpad 6";
|
||||||
case 0x67:
|
case 0x67:
|
||||||
return "Num 7";
|
return "Numpad 7";
|
||||||
case 0x68:
|
case 0x68:
|
||||||
return "Num 8";
|
return "Numpad 8";
|
||||||
case 0x69:
|
case 0x69:
|
||||||
return "Num 9";
|
return "Numpad 9";
|
||||||
case 0x6A:
|
case 0x6A:
|
||||||
return "*";
|
return "Numpad *";
|
||||||
case 0x6B:
|
case 0x6B:
|
||||||
return "+";
|
return "Numpad +";
|
||||||
case 0x6C:
|
case 0x6C:
|
||||||
return "Seperator";
|
return "Numpad Seperator";
|
||||||
case 0x6D:
|
case 0x6D:
|
||||||
return "-";
|
return "Numpad -";
|
||||||
case 0x6E:
|
case 0x6E:
|
||||||
return ".";
|
return "Numpad .";
|
||||||
case 0x6F:
|
case 0x6F:
|
||||||
return "/";
|
return "Numpad /";
|
||||||
case 0x70:
|
case 0x70:
|
||||||
return "F1";
|
return "F1";
|
||||||
case 0x71:
|
case 0x71:
|
||||||
@@ -255,9 +275,11 @@ std::string Button::getVKeyString() {
|
|||||||
case 0xA3:
|
case 0xA3:
|
||||||
return "Right Control";
|
return "Right Control";
|
||||||
case 0xA4:
|
case 0xA4:
|
||||||
return "Left Menu";
|
return "Left Alt";
|
||||||
case 0xA5:
|
case 0xA5:
|
||||||
return "Right Menu";
|
return "Right Alt";
|
||||||
|
case INVALID_VKEY:
|
||||||
|
return "Invalid";
|
||||||
default:
|
default:
|
||||||
|
|
||||||
// check win API
|
// check win API
|
||||||
@@ -284,7 +306,10 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
std::string vKeyString = fmt::format("{:#x}", vKey);
|
std::string vKeyString = fmt::format("{:#x}", vKey);
|
||||||
|
|
||||||
// device must be existing
|
// device must be existing
|
||||||
if (this->device_identifier.empty() && vKey == INVALID_VKEY) {
|
if (this->isNaive() && vKey == INVALID_VKEY) {
|
||||||
|
if (this->getInvert()) {
|
||||||
|
return "(always on)";
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,17 +326,17 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
case rawinput::MOUSE: {
|
case rawinput::MOUSE: {
|
||||||
const char *btn = "Unknown";
|
const char *btn = "Unknown";
|
||||||
static const char *MOUSE_NAMES[] = {
|
static const std::array<const char *, 8>MOUSE_NAMES = {
|
||||||
"Left Mouse",
|
"Left Mouse",
|
||||||
"Right Mouse",
|
"Right Mouse",
|
||||||
"Middle Mouse",
|
"Middle Mouse",
|
||||||
"Mouse 1",
|
"Mouse 1",
|
||||||
"Mouse 2",
|
"Mouse 2",
|
||||||
"Mouse 3",
|
"Mouse 3",
|
||||||
"Mouse 4",
|
"Mouse 4",
|
||||||
"Mouse 5",
|
"Mouse 5",
|
||||||
};
|
};
|
||||||
if (vKey < sizeof(MOUSE_NAMES)) {
|
if (vKey < MOUSE_NAMES.size()) {
|
||||||
btn = MOUSE_NAMES[vKey];
|
btn = MOUSE_NAMES[vKey];
|
||||||
}
|
}
|
||||||
return fmt::format("{} ({})", btn, device->desc);
|
return fmt::format("{} ({})", btn, device->desc);
|
||||||
@@ -395,6 +420,10 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
}
|
}
|
||||||
case rawinput::PIUIO_DEVICE:
|
case rawinput::PIUIO_DEVICE:
|
||||||
return "PIUIO " + vKeyString;
|
return "PIUIO " + vKeyString;
|
||||||
|
case rawinput::XINPUT_GAMEPAD:
|
||||||
|
return fmt::format("{} ({})",
|
||||||
|
xinput::get_button_string(static_cast<xinput::XInputButtonEnum>(vKey)),
|
||||||
|
device->desc);
|
||||||
case rawinput::DESTROYED:
|
case rawinput::DESTROYED:
|
||||||
return "Device unplugged (" + vKeyString + ")";
|
return "Device unplugged (" + vKeyString + ")";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api.h"
|
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameAPI {
|
||||||
|
namespace Buttons {
|
||||||
|
enum State {
|
||||||
|
BUTTON_PRESSED = true,
|
||||||
|
BUTTON_NOT_PRESSED = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ButtonAnalogType {
|
enum ButtonAnalogType {
|
||||||
BAT_NONE = 0,
|
BAT_NONE = 0,
|
||||||
BAT_POSITIVE = 1,
|
BAT_POSITIVE = 1,
|
||||||
@@ -41,19 +49,25 @@ private:
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string device_identifier = "";
|
std::string device_identifier = "";
|
||||||
unsigned short vKey = INVALID_VKEY;
|
unsigned short vKey = INVALID_VKEY;
|
||||||
|
|
||||||
|
// default bindings are always naive
|
||||||
|
unsigned short vKey_default = INVALID_VKEY;
|
||||||
|
|
||||||
ButtonAnalogType analog_type = BAT_NONE;
|
ButtonAnalogType analog_type = BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
|
int bat_threshold = 0; // for positive/negative only, value in percentage, zero is default
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
|
bool is_temporary = false;
|
||||||
|
|
||||||
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||||
float last_velocity = 0.f;
|
float last_velocity = 0.f;
|
||||||
unsigned short velocity_threshold = 0;
|
unsigned short velocity_threshold = 0;
|
||||||
|
|
||||||
std::string getVKeyString();
|
|
||||||
std::string getMidiNoteString();
|
std::string getMidiNoteString();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
std::string getVKeyString();
|
||||||
|
|
||||||
// overrides
|
// overrides
|
||||||
bool override_enabled = false;
|
bool override_enabled = false;
|
||||||
@@ -70,12 +84,20 @@ public:
|
|||||||
if (this->override_enabled) {
|
if (this->override_enabled) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// has a device
|
||||||
|
if (!this->device_identifier.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// naive, and has a valid vKey
|
||||||
if (this->vKey != INVALID_VKEY) {
|
if (this->vKey != INVALID_VKEY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &alternative : this->alternatives) {
|
for (auto &alternative : this->alternatives) {
|
||||||
if (alternative.vKey != INVALID_VKEY) {
|
if (!alternative.getDeviceIdentifier().empty() ||
|
||||||
|
alternative.getVKey() != INVALID_VKEY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,6 +110,7 @@ public:
|
|||||||
alternatives.clear();
|
alternatives.clear();
|
||||||
device_identifier = "";
|
device_identifier = "";
|
||||||
analog_type = BAT_NONE;
|
analog_type = BAT_NONE;
|
||||||
|
bat_threshold = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||||
@@ -116,6 +139,14 @@ public:
|
|||||||
this->vKey = vKey;
|
this->vKey = vKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline unsigned short getVKeyDefault() const {
|
||||||
|
return this->vKey_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVKeyDefault(unsigned short vKey_default) {
|
||||||
|
this->vKey_default = vKey_default;
|
||||||
|
}
|
||||||
|
|
||||||
inline ButtonAnalogType getAnalogType() const {
|
inline ButtonAnalogType getAnalogType() const {
|
||||||
return this->analog_type;
|
return this->analog_type;
|
||||||
}
|
}
|
||||||
@@ -140,6 +171,14 @@ public:
|
|||||||
this->debounce_down = debounce_time_down;
|
this->debounce_down = debounce_time_down;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void setBatThreshold(int bat_threshold) {
|
||||||
|
this->bat_threshold = bat_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getBatThreshold() const {
|
||||||
|
return this->bat_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool getInvert() const {
|
inline bool getInvert() const {
|
||||||
return this->invert;
|
return this->invert;
|
||||||
}
|
}
|
||||||
@@ -164,6 +203,14 @@ public:
|
|||||||
this->last_velocity = last_velocity;
|
this->last_velocity = last_velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isTemporary() const {
|
||||||
|
return this->is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setTemporary(bool is_temporary) {
|
||||||
|
this->is_temporary = is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
inline unsigned short getVelocityThreshold() const {
|
inline unsigned short getVelocityThreshold() const {
|
||||||
return this->velocity_threshold;
|
return this->velocity_threshold;
|
||||||
}
|
}
|
||||||
@@ -172,6 +219,20 @@ public:
|
|||||||
this->velocity_threshold = velocity_threshold;
|
this->velocity_threshold = velocity_threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isValid() {
|
||||||
|
// temporarily created by UI
|
||||||
|
if (isTemporary()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing is set
|
||||||
|
if (getDeviceIdentifier().empty() && getVKey() == INVALID_VKEY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void getMidiVKey(int& channel, int& index);
|
void getMidiVKey(int& channel, int& index);
|
||||||
void setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
|
void setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
|
||||||
|
|
||||||
|
|||||||
+104
-17
@@ -1,5 +1,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "cfg/button.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This code absolutely sucks.
|
* This code absolutely sucks.
|
||||||
@@ -10,6 +11,14 @@
|
|||||||
// settings
|
// settings
|
||||||
std::string CONFIG_PATH_OVERRIDE = "";
|
std::string CONFIG_PATH_OVERRIDE = "";
|
||||||
|
|
||||||
|
#define CFG_DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if CFG_DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
/// Constructor ///
|
/// Constructor ///
|
||||||
@@ -19,7 +28,7 @@ Config::Config() {
|
|||||||
this->status = false;
|
this->status = false;
|
||||||
if (CONFIG_PATH_OVERRIDE.length() > 0) {
|
if (CONFIG_PATH_OVERRIDE.length() > 0) {
|
||||||
this->configLocation = CONFIG_PATH_OVERRIDE;
|
this->configLocation = CONFIG_PATH_OVERRIDE;
|
||||||
log_info("cfg", "using custom config file: {}", this->configLocation.string());
|
log_info("cfg", "using custom config file: {}", this->configLocation);
|
||||||
} else {
|
} else {
|
||||||
this->configLocation = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools.xml";
|
this->configLocation = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools.xml";
|
||||||
// avoids logging the expanded appdata path as it contains user name
|
// avoids logging the expanded appdata path as it contains user name
|
||||||
@@ -48,7 +57,7 @@ Config::Config() {
|
|||||||
this->firstFillConfigFile();
|
this->firstFillConfigFile();
|
||||||
break;
|
break;
|
||||||
case tinyxml2::XMLError::XML_ERROR_FILE_COULD_NOT_BE_OPENED:
|
case tinyxml2::XMLError::XML_ERROR_FILE_COULD_NOT_BE_OPENED:
|
||||||
log_fatal("cfg", "could not open config file: {}", this->configLocation.string());
|
log_fatal("cfg", "could not open config file: {}", this->configLocation);
|
||||||
break;
|
break;
|
||||||
case tinyxml2::XMLError::XML_ERROR_FILE_NOT_FOUND:
|
case tinyxml2::XMLError::XML_ERROR_FILE_NOT_FOUND:
|
||||||
this->createConfigFile();
|
this->createConfigFile();
|
||||||
@@ -63,7 +72,7 @@ Config::Config() {
|
|||||||
case tinyxml2::XMLError::XML_ERROR_PARSING_UNKNOWN:
|
case tinyxml2::XMLError::XML_ERROR_PARSING_UNKNOWN:
|
||||||
case tinyxml2::XMLError::XML_ERROR_MISMATCHED_ELEMENT:
|
case tinyxml2::XMLError::XML_ERROR_MISMATCHED_ELEMENT:
|
||||||
case tinyxml2::XMLError::XML_ERROR_PARSING:
|
case tinyxml2::XMLError::XML_ERROR_PARSING:
|
||||||
log_warning("cfg", "Couldn't read config file: {}", this->configLocation.string());
|
log_warning("cfg", "Couldn't read config file: {}", this->configLocation);
|
||||||
this->createConfigFile();
|
this->createConfigFile();
|
||||||
this->firstFillConfigFile();
|
this->firstFillConfigFile();
|
||||||
break;
|
break;
|
||||||
@@ -153,6 +162,7 @@ bool Config::addGame(Game &game) {
|
|||||||
auto analogType = (int) BAT_NONE;
|
auto analogType = (int) BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
|
int bat_threshold = 0;
|
||||||
int velocity_threshold = 0;
|
int velocity_threshold = 0;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
tinyxml2::XMLError attrError = gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
tinyxml2::XMLError attrError = gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
||||||
@@ -160,6 +170,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
||||||
|
gameButtonNode->QueryIntAttribute("bat_threshold", &bat_threshold);
|
||||||
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
||||||
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
||||||
if (attrError != tinyxml2::XMLError::XML_SUCCESS) {
|
if (attrError != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
@@ -171,6 +182,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->SetAttribute("devid", button->getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", button->getDeviceIdentifier().c_str());
|
||||||
gameButtonNode->SetAttribute("debounce_up", debounce_up);
|
gameButtonNode->SetAttribute("debounce_up", debounce_up);
|
||||||
gameButtonNode->SetAttribute("debounce_down", debounce_down);
|
gameButtonNode->SetAttribute("debounce_down", debounce_down);
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", bat_threshold);
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", velocity_threshold);
|
gameButtonNode->SetAttribute("velocity_threshold", velocity_threshold);
|
||||||
gameButtonNode->SetAttribute("invert", invert);
|
gameButtonNode->SetAttribute("invert", invert);
|
||||||
gameButtonsNode->InsertEndChild(gameButtonNode);
|
gameButtonsNode->InsertEndChild(gameButtonNode);
|
||||||
@@ -179,6 +191,7 @@ bool Config::addGame(Game &game) {
|
|||||||
button->setAnalogType((ButtonAnalogType) analogType);
|
button->setAnalogType((ButtonAnalogType) analogType);
|
||||||
button->setDebounceUp(debounce_up);
|
button->setDebounceUp(debounce_up);
|
||||||
button->setDebounceDown(debounce_down);
|
button->setDebounceDown(debounce_down);
|
||||||
|
button->setBatThreshold(bat_threshold);
|
||||||
button->setVelocityThreshold(velocity_threshold);
|
button->setVelocityThreshold(velocity_threshold);
|
||||||
button->setInvert(invert);
|
button->setInvert(invert);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
@@ -198,6 +211,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->SetAttribute("analogtype", (int) it.getAnalogType());
|
gameButtonNode->SetAttribute("analogtype", (int) it.getAnalogType());
|
||||||
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
||||||
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", it.getBatThreshold());
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
||||||
gameButtonNode->SetAttribute("invert", it.getInvert());
|
gameButtonNode->SetAttribute("invert", it.getInvert());
|
||||||
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
||||||
@@ -239,7 +253,7 @@ bool Config::addGame(Game &game) {
|
|||||||
bool smoothing = false;
|
bool smoothing = false;
|
||||||
int multiplier = 1;
|
int multiplier = 1;
|
||||||
bool relative_mode = false;
|
bool relative_mode = false;
|
||||||
int delay_buffer_depth = 0;
|
uint32_t delay = 0;
|
||||||
tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index);
|
tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index);
|
||||||
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
||||||
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
||||||
@@ -248,7 +262,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
||||||
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
||||||
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
||||||
gameAnalogNode->QueryIntAttribute("delay", &delay_buffer_depth);
|
gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
|
||||||
const char *devid = gameAnalogNode->Attribute("devid");
|
const char *devid = gameAnalogNode->Attribute("devid");
|
||||||
|
|
||||||
if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) {
|
if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) {
|
||||||
@@ -264,7 +278,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
} else {
|
} else {
|
||||||
it.setIndex(static_cast<unsigned short int>(index));
|
it.setIndex(static_cast<unsigned short int>(index));
|
||||||
@@ -276,7 +290,7 @@ bool Config::addGame(Game &game) {
|
|||||||
it.setSmoothing(smoothing);
|
it.setSmoothing(smoothing);
|
||||||
it.setMultiplier(multiplier);
|
it.setMultiplier(multiplier);
|
||||||
it.setRelativeMode(relative_mode);
|
it.setRelativeMode(relative_mode);
|
||||||
it.setDelayBufferDepth(delay_buffer_depth);
|
it.setDelayMs(delay);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
gameAnalogNode = this->configFile.NewElement("analog");
|
gameAnalogNode = this->configFile.NewElement("analog");
|
||||||
@@ -289,7 +303,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||||
gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
}
|
}
|
||||||
@@ -423,6 +437,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->SetAttribute("analogtype", it.getAnalogType());
|
gameButtonNode->SetAttribute("analogtype", it.getAnalogType());
|
||||||
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
||||||
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", it.getBatThreshold());
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
||||||
gameButtonNode->SetAttribute("invert", it.getInvert());
|
gameButtonNode->SetAttribute("invert", it.getInvert());
|
||||||
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
||||||
@@ -443,7 +458,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,8 +525,10 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tinyxml2::XMLElement *gameButtonNode = nullptr;
|
||||||
|
|
||||||
// iterate button nodes
|
// iterate button nodes
|
||||||
tinyxml2::XMLElement *gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||||
int button_count = 0;
|
int button_count = 0;
|
||||||
while (gameButtonNode != nullptr) {
|
while (gameButtonNode != nullptr) {
|
||||||
const char *buttonNodeName = gameButtonNode->Attribute("name");
|
const char *buttonNodeName = gameButtonNode->Attribute("name");
|
||||||
@@ -524,6 +541,7 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
gameButtonNode->SetAttribute("analogtype", (int) button.getAnalogType());
|
gameButtonNode->SetAttribute("analogtype", (int) button.getAnalogType());
|
||||||
gameButtonNode->SetAttribute("debounce_up", button.getDebounceUp());
|
gameButtonNode->SetAttribute("debounce_up", button.getDebounceUp());
|
||||||
gameButtonNode->SetAttribute("debounce_down", button.getDebounceDown());
|
gameButtonNode->SetAttribute("debounce_down", button.getDebounceDown());
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", button.getBatThreshold());
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", button.getVelocityThreshold());
|
gameButtonNode->SetAttribute("velocity_threshold", button.getVelocityThreshold());
|
||||||
gameButtonNode->SetAttribute("invert", button.getInvert());
|
gameButtonNode->SetAttribute("invert", button.getInvert());
|
||||||
gameButtonNode->SetAttribute("devid", button.getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", button.getDeviceIdentifier().c_str());
|
||||||
@@ -540,6 +558,7 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
gameButtonNode->SetAttribute("analogtype", 0);
|
gameButtonNode->SetAttribute("analogtype", 0);
|
||||||
gameButtonNode->SetAttribute("debounce_up", 0.0);
|
gameButtonNode->SetAttribute("debounce_up", 0.0);
|
||||||
gameButtonNode->SetAttribute("debounce_down", 0.0);
|
gameButtonNode->SetAttribute("debounce_down", 0.0);
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", 0);
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", 0);
|
gameButtonNode->SetAttribute("velocity_threshold", 0);
|
||||||
gameButtonNode->SetAttribute("invert", false);
|
gameButtonNode->SetAttribute("invert", false);
|
||||||
gameButtonNode->SetAttribute("devid", "");
|
gameButtonNode->SetAttribute("devid", "");
|
||||||
@@ -574,6 +593,39 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trim the trailing invalid (unbound) alternatives
|
||||||
|
tinyxml2::XMLElement *to_delete = nullptr;
|
||||||
|
gameButtonNode = gameButtonsNode->LastChildElement("button");
|
||||||
|
while (gameButtonNode != nullptr) {
|
||||||
|
const char *name = gameButtonNode->Attribute("name");
|
||||||
|
const char *devid = gameButtonNode->Attribute("devid");
|
||||||
|
int vkey = INVALID_VKEY;
|
||||||
|
gameButtonNode->QueryIntAttribute("vkey", &vkey);
|
||||||
|
|
||||||
|
// wrong button, ignore and keep iterating backwards
|
||||||
|
if (name == nullptr || std::string(name) != button.getName()) {
|
||||||
|
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// found a node for this button, which means to_delete is not the first one
|
||||||
|
// delete the node for to_delete
|
||||||
|
if (to_delete != nullptr) {
|
||||||
|
log_debug("cfg", "deleting excessive button binding for {}", name);
|
||||||
|
gameButtonsNode->DeleteChild(to_delete);
|
||||||
|
to_delete = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((devid == nullptr || strlen(devid) == 0) && vkey == INVALID_VKEY) {
|
||||||
|
// unbound button with no device ID, make a note of it so it can be deleted later
|
||||||
|
to_delete = gameButtonNode;
|
||||||
|
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||||
|
} else {
|
||||||
|
// valid binding found; stop iterating
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if button was not found
|
// check if button was not found
|
||||||
if (button_count == 0) {
|
if (button_count == 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -640,7 +692,7 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", analog.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
|
||||||
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
||||||
} else {
|
} else {
|
||||||
gameAnalogNode = this->configFile.NewElement("analog");
|
gameAnalogNode = this->configFile.NewElement("analog");
|
||||||
@@ -652,7 +704,7 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", analog.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
|
||||||
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
}
|
}
|
||||||
@@ -760,6 +812,37 @@ bool Config::updateBinding(const Game &game, const Light &light, int alternative
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trim the trailing invalid (unbound) alternatives
|
||||||
|
tinyxml2::XMLElement *to_delete = nullptr;
|
||||||
|
gameLightNode = gameLightsNode->LastChildElement("light");
|
||||||
|
while (gameLightNode != nullptr) {
|
||||||
|
const char *name = gameLightNode->Attribute("name");
|
||||||
|
const char *devid = gameLightNode->Attribute("devid");
|
||||||
|
|
||||||
|
// wrong light, ignore and keep iterating backwards
|
||||||
|
if (name == nullptr || std::string(name) != light.getName()) {
|
||||||
|
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// found a node for this button, which means to_delete is not the first one
|
||||||
|
// delete the node for to_delete
|
||||||
|
if (to_delete != nullptr) {
|
||||||
|
log_debug("cfg", "deleting excessive light binding for {}", name);
|
||||||
|
gameLightsNode->DeleteChild(to_delete);
|
||||||
|
to_delete = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devid == nullptr || strlen(devid) == 0) {
|
||||||
|
// unbound light, make a note of it so it can be deleted later
|
||||||
|
to_delete = gameLightNode;
|
||||||
|
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||||
|
} else {
|
||||||
|
// valid binding found; stop iterating
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if light was not found
|
// check if light was not found
|
||||||
if (light_count == 0) {
|
if (light_count == 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -871,12 +954,14 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
|
|||||||
auto analogType = (int) BAT_NONE;
|
auto analogType = (int) BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
|
int bat_threshold = 0;
|
||||||
int velocity_threshold = 0;
|
int velocity_threshold = 0;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
||||||
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
||||||
|
gameButtonNode->QueryIntAttribute("bat_threshold", &bat_threshold);
|
||||||
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
||||||
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
||||||
const char *devid = gameButtonNode->Attribute("devid");
|
const char *devid = gameButtonNode->Attribute("devid");
|
||||||
@@ -891,6 +976,7 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
|
|||||||
alt.setAnalogType((ButtonAnalogType) analogType);
|
alt.setAnalogType((ButtonAnalogType) analogType);
|
||||||
alt.setDebounceUp(debounce_up);
|
alt.setDebounceUp(debounce_up);
|
||||||
alt.setDebounceDown(debounce_down);
|
alt.setDebounceDown(debounce_down);
|
||||||
|
alt.setBatThreshold(bat_threshold);
|
||||||
alt.setVelocityThreshold(velocity_threshold);
|
alt.setVelocityThreshold(velocity_threshold);
|
||||||
alt.setInvert(invert);
|
alt.setInvert(invert);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
@@ -909,6 +995,7 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
|
|||||||
button.setAnalogType((ButtonAnalogType) analogType);
|
button.setAnalogType((ButtonAnalogType) analogType);
|
||||||
button.setDebounceUp(debounce_up);
|
button.setDebounceUp(debounce_up);
|
||||||
button.setDebounceDown(debounce_down);
|
button.setDebounceDown(debounce_down);
|
||||||
|
button.setBatThreshold(bat_threshold);
|
||||||
button.setVelocityThreshold(velocity_threshold);
|
button.setVelocityThreshold(velocity_threshold);
|
||||||
button.setInvert(invert);
|
button.setInvert(invert);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
@@ -1055,7 +1142,7 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
|||||||
bool smoothing = false;
|
bool smoothing = false;
|
||||||
int multiplier = 1;
|
int multiplier = 1;
|
||||||
bool relative_mode = false;
|
bool relative_mode = false;
|
||||||
int delay_buffer_depth = 0;
|
uint32_t delay = 0;
|
||||||
gameAnalogNode->QueryIntAttribute("index", &index);
|
gameAnalogNode->QueryIntAttribute("index", &index);
|
||||||
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
||||||
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
||||||
@@ -1064,7 +1151,7 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
|||||||
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
||||||
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
||||||
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
||||||
gameAnalogNode->QueryIntAttribute("delay", &delay_buffer_depth);
|
gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
|
||||||
const char *devid = gameAnalogNode->Attribute("devid");
|
const char *devid = gameAnalogNode->Attribute("devid");
|
||||||
|
|
||||||
// create analog and add to list
|
// create analog and add to list
|
||||||
@@ -1077,7 +1164,7 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
|||||||
analog.setSmoothing(smoothing);
|
analog.setSmoothing(smoothing);
|
||||||
analog.setMultiplier(multiplier);
|
analog.setMultiplier(multiplier);
|
||||||
analog.setRelativeMode(relative_mode);
|
analog.setRelativeMode(relative_mode);
|
||||||
analog.setDelayBufferDepth(delay_buffer_depth);
|
analog.setDelayMs(delay);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
analog.setDeviceIdentifier(devid);
|
analog.setDeviceIdentifier(devid);
|
||||||
}
|
}
|
||||||
@@ -1240,7 +1327,7 @@ void Config::saveConfigFile() {
|
|||||||
// create a .tmp file and write to it...
|
// create a .tmp file and write to it...
|
||||||
const auto xml_result = this->configFile.SaveFile(this->configLocationTemp.c_str(), false);
|
const auto xml_result = this->configFile.SaveFile(this->configLocationTemp.c_str(), false);
|
||||||
if (xml_result != tinyxml2::XMLError::XML_SUCCESS) {
|
if (xml_result != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
log_info("cfg", "failed to write file: {}", this->configLocationTemp.string());
|
log_info("cfg", "failed to write file: {}", this->configLocationTemp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// copy the .tmp file to the main file...
|
// copy the .tmp file to the main file...
|
||||||
@@ -1250,4 +1337,4 @@ void Config::saveConfigFile() {
|
|||||||
}
|
}
|
||||||
// delete the .tmp file (not critical if this fails)
|
// delete the .tmp file (not critical if this fails)
|
||||||
DeleteFileW(this->configLocationTemp.c_str());
|
DeleteFileW(this->configLocationTemp.c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,465 @@
|
|||||||
|
#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("bat_threshold", entry.bat_threshold);
|
||||||
|
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;
|
||||||
|
|
||||||
|
int bat = 0;
|
||||||
|
el->QueryIntAttribute("bat_threshold", &bat);
|
||||||
|
entry.bat_threshold = bat;
|
||||||
|
|
||||||
|
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_ms", analog.delay_ms);
|
||||||
|
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->QueryUnsignedAttribute("delay_ms", &a.delay_ms);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -85,6 +85,15 @@ std::string Light::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
|
|
||||||
return "Invalid SMX Dedicab Light (" + index_string + ")";
|
return "Invalid SMX Dedicab Light (" + index_string + ")";
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
if (index < static_cast<size_t>(xinput::XInputOutputEnum::COUNT)) {
|
||||||
|
return fmt::format("{} ({})",
|
||||||
|
xinput::get_output_string(static_cast<xinput::XInputOutputEnum>(index)),
|
||||||
|
device->desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Invalid XInput Gamepad Light (" + index_string + ")";
|
||||||
|
}
|
||||||
case rawinput::DESTROYED:
|
case rawinput::DESTROYED:
|
||||||
return "Unplugged device (" + index_string + ")";
|
return "Unplugged device (" + index_string + ")";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ class Light {
|
|||||||
private:
|
private:
|
||||||
std::vector<Light> alternatives;
|
std::vector<Light> alternatives;
|
||||||
std::string lightName;
|
std::string lightName;
|
||||||
|
std::string lightCategory;
|
||||||
std::string deviceIdentifier = "";
|
std::string deviceIdentifier = "";
|
||||||
unsigned int index = 0;
|
unsigned int index = 0;
|
||||||
|
bool is_temporary = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float last_state = 0.f;
|
float last_state = 0.f;
|
||||||
@@ -22,6 +24,8 @@ public:
|
|||||||
float override_state = 0.f;
|
float override_state = 0.f;
|
||||||
|
|
||||||
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
|
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
|
||||||
|
explicit Light(std::string lightName, std::string lightCategory) :
|
||||||
|
lightName(std::move(lightName)), lightCategory(std::move(lightCategory)) {};
|
||||||
|
|
||||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||||
|
|
||||||
@@ -50,6 +54,14 @@ public:
|
|||||||
return this->lightName;
|
return this->lightName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const std::string &getCategory() const {
|
||||||
|
return this->lightCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setCategory(std::string category) {
|
||||||
|
this->lightCategory = std::move(category);
|
||||||
|
}
|
||||||
|
|
||||||
inline const std::string &getDeviceIdentifier() const {
|
inline const std::string &getDeviceIdentifier() const {
|
||||||
return this->deviceIdentifier;
|
return this->deviceIdentifier;
|
||||||
}
|
}
|
||||||
@@ -65,4 +77,26 @@ public:
|
|||||||
inline void setIndex(unsigned int index) {
|
inline void setIndex(unsigned int index) {
|
||||||
this->index = index;
|
this->index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isTemporary() const {
|
||||||
|
return this->is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setTemporary(bool is_temporary) {
|
||||||
|
this->is_temporary = is_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isValid() {
|
||||||
|
// temporarily created by UI
|
||||||
|
if (isTemporary()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing is set
|
||||||
|
if (getDeviceIdentifier().empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</dependency>
|
</dependency>
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges>
|
<requestedPrivileges>
|
||||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||||
</requestedPrivileges>
|
</requestedPrivileges>
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
</assembly>
|
</assembly>
|
||||||
@@ -13,6 +13,16 @@ enum class OptionType {
|
|||||||
Hex,
|
Hex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class OptionPickerType {
|
||||||
|
None,
|
||||||
|
AsioDriver,
|
||||||
|
EACard,
|
||||||
|
CpuAffinity,
|
||||||
|
FilePath,
|
||||||
|
DirectoryPath,
|
||||||
|
Monitor,
|
||||||
|
};
|
||||||
|
|
||||||
struct OptionDefinition {
|
struct OptionDefinition {
|
||||||
std::string title;
|
std::string title;
|
||||||
// unique identifier used for flag matching but also stored in config files
|
// unique identifier used for flag matching but also stored in config files
|
||||||
@@ -32,6 +42,10 @@ struct OptionDefinition {
|
|||||||
bool sensitive = false;
|
bool sensitive = false;
|
||||||
std::vector<std::pair<std::string, std::string>> elements = {};
|
std::vector<std::pair<std::string, std::string>> elements = {};
|
||||||
bool disabled = false;
|
bool disabled = false;
|
||||||
|
OptionPickerType picker = OptionPickerType::None;
|
||||||
|
|
||||||
|
// for OptionPickerType::FilePath
|
||||||
|
std::string file_extension = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class Option {
|
class Option {
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
#define IDR_PATCHES 132
|
#define IDR_PATCHES 132
|
||||||
#define IDR_README 133
|
#define IDR_README 133
|
||||||
#define IDR_DSEGFONT 134
|
#define IDR_DSEGFONT 134
|
||||||
|
#define IDR_CONTROLLER_PRESETS_BUILTIN 135
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#include "screen_resize.h"
|
#include "screen_resize.h"
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
#include "external/rapidjson/pointer.h"
|
#include "external/rapidjson/pointer.h"
|
||||||
#include "external/rapidjson/prettywriter.h"
|
#include "external/rapidjson/prettywriter.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/deferlog.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
@@ -19,7 +21,7 @@ namespace cfg {
|
|||||||
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
|
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
|
||||||
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
|
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
|
||||||
if (fileutils::file_exists(this->config_path)) {
|
if (fileutils::file_exists(this->config_path)) {
|
||||||
log_info("ScreenResize", "loading config from: {}", this->config_path.string());
|
log_info("ScreenResize", "loading config from: {}", this->config_path);
|
||||||
file_exists = true;
|
file_exists = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -33,7 +35,7 @@ namespace cfg {
|
|||||||
|
|
||||||
ScreenResize::~ScreenResize() {
|
ScreenResize::~ScreenResize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenResize::config_load() {
|
void ScreenResize::config_load() {
|
||||||
std::string config = fileutils::text_read(this->config_path);
|
std::string config = fileutils::text_read(this->config_path);
|
||||||
if (config.empty()) {
|
if (config.empty()) {
|
||||||
@@ -76,8 +78,12 @@ namespace cfg {
|
|||||||
eamuse_get_game(),
|
eamuse_get_game(),
|
||||||
use_game_setting,
|
use_game_setting,
|
||||||
root);
|
root);
|
||||||
|
|
||||||
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
||||||
|
if (this->enable_screen_resize) {
|
||||||
|
log_misc("ScreenResize", "enabled by config file");
|
||||||
|
}
|
||||||
|
|
||||||
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||||
auto& scene = this->scene_settings[i];
|
auto& scene = this->scene_settings[i];
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -51,11 +52,15 @@ namespace cfg {
|
|||||||
bool enable_linear_filter = true;
|
bool enable_linear_filter = true;
|
||||||
fullscreen_setting scene_settings[4];
|
fullscreen_setting scene_settings[4];
|
||||||
|
|
||||||
|
// state
|
||||||
|
std::atomic<bool> need_surface_clean = false;
|
||||||
|
|
||||||
// windowed mode sizing
|
// windowed mode sizing
|
||||||
// Windows terminology:
|
// Windows terminology:
|
||||||
// window = rectangle including the frame
|
// window = rectangle including the frame
|
||||||
// client = just the content area without frames.
|
// client = just the content area without frames.
|
||||||
bool window_always_on_top = false;
|
bool window_always_on_top = false;
|
||||||
|
bool window_disable_round_corners = false;
|
||||||
bool client_keep_aspect_ratio = true;
|
bool client_keep_aspect_ratio = true;
|
||||||
bool enable_window_resize = false;
|
bool enable_window_resize = false;
|
||||||
int window_decoration = 0; // enum type WindowDecorationMode
|
int window_decoration = 0; // enum type WindowDecorationMode
|
||||||
@@ -69,6 +74,7 @@ namespace cfg {
|
|||||||
uint32_t init_client_height = 0;
|
uint32_t init_client_height = 0;
|
||||||
float init_client_aspect_ratio = 1.f;
|
float init_client_aspect_ratio = 1.f;
|
||||||
uint32_t init_window_style = 0;
|
uint32_t init_window_style = 0;
|
||||||
|
uint32_t init_window_style_ex = 0;
|
||||||
uint32_t window_deco_width = 0;
|
uint32_t window_deco_width = 0;
|
||||||
uint32_t window_deco_height = 0;
|
uint32_t window_deco_height = 0;
|
||||||
|
|
||||||
|
|||||||
+8
@@ -15,3 +15,11 @@ RUN pacman --noconfirm -Syu git \
|
|||||||
ccache
|
ccache
|
||||||
RUN useradd user -m && echo "ALL ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
RUN useradd user -m && echo "ALL ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||||
RUN su user -c "cd /tmp/ && git clone https://aur.archlinux.org/yay-bin.git --depth=1 && cd yay-bin && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
|
RUN su user -c "cd /tmp/ && git clone https://aur.archlinux.org/yay-bin.git --depth=1 && cd yay-bin && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/llvm-mingw-xp \
|
||||||
|
&& curl -fsSL "https://github.com/mon/llvm-mingw-xp/releases/download/llvm-mingw-xp-22.1.0/llvm-mingw-llvm-mingw-xp-22.1.0-msvcrt-ubuntu-22.04-x86_64.tar.xz" \
|
||||||
|
| tar -xJ --strip-components=1 -C /opt/llvm-mingw-xp
|
||||||
|
ENV PATH="$PATH:/opt/llvm-mingw-xp/bin"
|
||||||
|
|
||||||
|
RUN curl -fsSL "https://github.com/mon/windows-dll-compat-checker/releases/download/v1.3/windows_dll_compat_checker-linux-x86_64.tar.xz" \
|
||||||
|
| tar -xJ -C /usr/local/bin
|
||||||
|
|||||||
Vendored
+1
@@ -20,6 +20,7 @@ Usage:
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2023 Omar Cornut
|
Copyright (c) 2014-2026 Omar Cornut
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+645
-269
@@ -1,16 +1,37 @@
|
|||||||
// dear imgui: Renderer for DirectX9
|
// dear imgui: Renderer Backend for DirectX9
|
||||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||||
|
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||||
|
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||||
|
// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
|
||||||
|
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||||
|
|
||||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// https://github.com/ocornut/imgui
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
// CHANGELOG
|
// CHANGELOG
|
||||||
// (minor and older changes stripped away, please see git history for details)
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
|
||||||
|
// 2026-03-19: Fixed issue in ImGui_ImplDX9_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310)
|
||||||
|
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
|
||||||
|
// 2025-06-11: DirectX9: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
|
||||||
|
// 2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
||||||
|
// 2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575)
|
||||||
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2021-06-25: DirectX9: Explicitly disable texture state stages after >= 1.
|
||||||
|
// 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||||
|
// 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states.
|
||||||
|
// 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857).
|
||||||
|
// 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file.
|
||||||
|
// 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer.
|
||||||
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||||
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||||
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
|
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
|
||||||
@@ -22,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-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
|
||||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||||
|
|
||||||
#include "imgui_impl_dx9.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
// DirectX
|
|
||||||
#include <d3d9.h>
|
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_dx9.h"
|
||||||
|
|
||||||
// allow std::min use
|
// allow std::min use
|
||||||
#ifdef min
|
#ifdef min
|
||||||
#undef min
|
#undef min
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// DirectX data
|
// DirectX
|
||||||
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
|
#include <d3d9.h>
|
||||||
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
|
|
||||||
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
|
|
||||||
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
|
|
||||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
|
||||||
|
|
||||||
|
// Clang/GCC warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DirectX data
|
||||||
|
struct ImGui_ImplDX9_Data
|
||||||
|
{
|
||||||
|
LPDIRECT3DDEVICE9 pd3dDevice;
|
||||||
|
LPDIRECT3DVERTEXBUFFER9 pVB;
|
||||||
|
LPDIRECT3DINDEXBUFFER9 pIB;
|
||||||
|
int VertexBufferSize;
|
||||||
|
int IndexBufferSize;
|
||||||
|
bool HasRgbaSupport;
|
||||||
|
|
||||||
|
ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CUSTOMVERTEX
|
||||||
|
{
|
||||||
|
float pos[3];
|
||||||
|
D3DCOLOR col;
|
||||||
|
float uv[2];
|
||||||
|
};
|
||||||
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
|
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
|
||||||
|
|
||||||
// Render function.
|
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
#define IMGUI_COL_TO_DX9_ARGB(_COL) (_COL)
|
||||||
void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
#else
|
||||||
|
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
|
||||||
|
#endif
|
||||||
|
|
||||||
// Avoid rendering when minimized
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
return;
|
static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Create and grow buffers if needed
|
// Forward Declarations
|
||||||
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) {
|
static void ImGui_ImplDX9_InitMultiViewportSupport();
|
||||||
if (g_pVB) {
|
static void ImGui_ImplDX9_ShutdownMultiViewportSupport();
|
||||||
g_pVB->Release();
|
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
|
||||||
g_pVB = NULL;
|
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
|
||||||
}
|
|
||||||
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
|
||||||
if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(ImDrawVert),
|
|
||||||
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX,
|
|
||||||
D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) {
|
|
||||||
if (g_pIB) {
|
|
||||||
g_pIB->Release();
|
|
||||||
g_pIB = NULL;
|
|
||||||
}
|
|
||||||
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
|
||||||
if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx),
|
|
||||||
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY,
|
|
||||||
sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32,
|
|
||||||
D3DPOOL_DEFAULT, &g_pIB, NULL) < 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backup the DX9 state
|
struct render_state_t {
|
||||||
IDirect3DStateBlock9 *d3d9_state_block = NULL;
|
IDirect3DSurface9 *back_buffer;
|
||||||
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
|
IDirect3DSurface9 *depth_stencil;
|
||||||
return;
|
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)
|
// Functions
|
||||||
D3DMATRIX last_world, last_view, last_projection;
|
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data, struct render_state_t *render_state)
|
||||||
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
|
{
|
||||||
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
|
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||||
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);
|
|
||||||
|
|
||||||
// Setup viewport
|
// Setup viewport
|
||||||
D3DVIEWPORT9 vp;
|
D3DVIEWPORT9 vp;
|
||||||
vp.X = vp.Y = 0;
|
vp.X = vp.Y = 0;
|
||||||
vp.Width = (DWORD) draw_data->DisplaySize.x;
|
vp.Width = (DWORD)draw_data->DisplaySize.x;
|
||||||
vp.Height = (DWORD) draw_data->DisplaySize.y;
|
vp.Height = (DWORD)draw_data->DisplaySize.y;
|
||||||
vp.MinZ = 0.0f;
|
vp.MinZ = 0.0f;
|
||||||
vp.MaxZ = 1.0f;
|
vp.MaxZ = 1.0f;
|
||||||
g_pd3dDevice->SetViewport(&vp);
|
|
||||||
|
|
||||||
g_pd3dDevice->SetPixelShader(nullptr);
|
LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
|
||||||
g_pd3dDevice->SetVertexShader(nullptr);
|
device->SetViewport(&vp);
|
||||||
|
|
||||||
D3DCAPS9 caps {};
|
// spice ldj
|
||||||
if (FAILED(g_pd3dDevice->GetDeviceCaps(&caps))) {
|
{
|
||||||
caps.NumSimultaneousRTs = 0UL;
|
if (FAILED(device->GetDeviceCaps(&render_state->caps))) {
|
||||||
}
|
render_state->caps.NumSimultaneousRTs = 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
IDirect3DSurface9 *back_buffer = nullptr;
|
// save all previous render target state
|
||||||
IDirect3DSurface9 *depth_stencil = nullptr;
|
for (size_t target = 0; target < std::min(8UL, render_state->caps.NumSimultaneousRTs); target++) {
|
||||||
IDirect3DSurface9 *render_targets[8];
|
if (FAILED(device->GetRenderTarget(target, &render_state->render_targets[target]))) {
|
||||||
|
render_state->render_targets[target] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// save all previous render target state
|
// get the previous depth stencil
|
||||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
if (FAILED(device->GetDepthStencilSurface(&render_state->depth_stencil))) {
|
||||||
if (FAILED(g_pd3dDevice->GetRenderTarget(target, &render_targets[target]))) {
|
render_state->depth_stencil = nullptr;
|
||||||
render_targets[target] = 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
|
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling.
|
||||||
if (FAILED(g_pd3dDevice->GetDepthStencilSurface(&depth_stencil))) {
|
device->SetPixelShader(nullptr);
|
||||||
depth_stencil = nullptr;
|
device->SetVertexShader(nullptr);
|
||||||
}
|
device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
|
||||||
|
device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
||||||
// set the back buffer as the current render target
|
device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||||
if (SUCCEEDED(g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer))) {
|
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderTarget(0, back_buffer);
|
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||||
g_pd3dDevice->SetDepthStencilSurface(nullptr);
|
device->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||||
|
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||||
for (size_t target = 1; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
||||||
g_pd3dDevice->SetRenderTarget(target, nullptr);
|
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||||
}
|
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||||
} else {
|
device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
|
||||||
back_buffer = nullptr;
|
device->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
|
||||||
}
|
device->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
|
||||||
|
device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
|
||||||
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
|
device->SetRenderState(D3DRS_FOGENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
device->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
|
device->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
|
device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
|
device->SetRenderState(D3DRS_CLIPPING, TRUE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
|
device->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
|
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||||
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
|
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
|
||||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
|
||||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
|
||||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
|
||||||
|
|
||||||
// Setup orthographic projection matrix
|
// Setup orthographic projection matrix
|
||||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||||
@@ -185,176 +200,537 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
|
|||||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
|
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
|
||||||
float T = draw_data->DisplayPos.y + 0.5f;
|
float T = draw_data->DisplayPos.y + 0.5f;
|
||||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.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 =
|
D3DMATRIX mat_projection =
|
||||||
{{{
|
{ { {
|
||||||
2.0f / (R - L), 0.0f, 0.0f, 0.0f,
|
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
|
||||||
0.0f, 2.0f / (T - B), 0.0f, 0.0f,
|
0.0f, 2.0f/(T-B), 0.0f, 0.0f,
|
||||||
0.0f, 0.0f, 0.5f, 0.0f,
|
0.0f, 0.0f, 0.5f, 0.0f,
|
||||||
(L + R) / (L - R), (T + B) / (B - T), 0.5f, 1.0f
|
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
|
||||||
}}};
|
} } };
|
||||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
|
device->SetTransform(D3DTS_WORLD, &mat_identity);
|
||||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
|
device->SetTransform(D3DTS_VIEW, &mat_identity);
|
||||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
device->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render function.
|
||||||
|
void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
|
||||||
|
{
|
||||||
|
// Avoid rendering when minimized
|
||||||
|
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||||
|
LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
|
||||||
|
|
||||||
|
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||||
|
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||||
|
if (draw_data->Textures != nullptr)
|
||||||
|
for (ImTextureData* tex : *draw_data->Textures)
|
||||||
|
if (tex->Status != ImTextureStatus_OK)
|
||||||
|
ImGui_ImplDX9_UpdateTexture(tex);
|
||||||
|
|
||||||
|
// Create and grow buffers if needed
|
||||||
|
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||||
|
{
|
||||||
|
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||||
|
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||||
|
if (device->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||||
|
{
|
||||||
|
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||||
|
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||||
|
if (device->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup the DX9 state
|
||||||
|
IDirect3DStateBlock9* state_block = nullptr;
|
||||||
|
if (device->CreateStateBlock(D3DSBT_ALL, &state_block) < 0)
|
||||||
|
return;
|
||||||
|
if (state_block->Capture() < 0)
|
||||||
|
{
|
||||||
|
state_block->Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
|
||||||
|
D3DMATRIX last_world, last_view, last_projection;
|
||||||
|
device->GetTransform(D3DTS_WORLD, &last_world);
|
||||||
|
device->GetTransform(D3DTS_VIEW, &last_view);
|
||||||
|
device->GetTransform(D3DTS_PROJECTION, &last_projection);
|
||||||
|
|
||||||
|
// Allocate buffers
|
||||||
|
CUSTOMVERTEX* vtx_dst;
|
||||||
|
ImDrawIdx* idx_dst;
|
||||||
|
if (bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
|
||||||
|
{
|
||||||
|
state_block->Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
|
||||||
|
{
|
||||||
|
bd->pVB->Unlock();
|
||||||
|
state_block->Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
|
||||||
|
// FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and
|
||||||
|
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||||
|
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||||
|
{
|
||||||
|
const ImDrawVert* vtx_src = draw_list->VtxBuffer.Data;
|
||||||
|
for (int i = 0; i < draw_list->VtxBuffer.Size; i++)
|
||||||
|
{
|
||||||
|
vtx_dst->pos[0] = vtx_src->pos.x;
|
||||||
|
vtx_dst->pos[1] = vtx_src->pos.y;
|
||||||
|
vtx_dst->pos[2] = 0.0f;
|
||||||
|
vtx_dst->col = IMGUI_COL_TO_DX9_ARGB(vtx_src->col);
|
||||||
|
vtx_dst->uv[0] = vtx_src->uv.x;
|
||||||
|
vtx_dst->uv[1] = vtx_src->uv.y;
|
||||||
|
vtx_dst++;
|
||||||
|
vtx_src++;
|
||||||
|
}
|
||||||
|
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||||
|
idx_dst += draw_list->IdxBuffer.Size;
|
||||||
|
}
|
||||||
|
bd->pVB->Unlock();
|
||||||
|
bd->pIB->Unlock();
|
||||||
|
device->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX));
|
||||||
|
device->SetIndices(bd->pIB);
|
||||||
|
device->SetFVF(D3DFVF_CUSTOMVERTEX);
|
||||||
|
|
||||||
|
// Setup desired DX state
|
||||||
|
struct render_state_t render_state = {};
|
||||||
|
ImGui_ImplDX9_SetupRenderState(draw_data, &render_state);
|
||||||
|
|
||||||
// Render command lists
|
// Render command lists
|
||||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||||
int global_vtx_offset = 0;
|
int global_vtx_offset = 0;
|
||||||
int global_idx_offset = 0;
|
int global_idx_offset = 0;
|
||||||
ImVec2 clip_off = draw_data->DisplayPos;
|
ImVec2 clip_off = draw_data->DisplayPos;
|
||||||
for (int n = 0; n < draw_data->CmdListsCount; n++) {
|
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||||
const ImDrawList *cmd_list = draw_data->CmdLists[n];
|
{
|
||||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
|
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||||
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
|
{
|
||||||
if (pcmd->UserCallback != NULL) {
|
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||||
pcmd->UserCallback(cmd_list, pcmd);
|
if (pcmd->UserCallback != nullptr)
|
||||||
} else {
|
{
|
||||||
const RECT r = {(LONG) (pcmd->ClipRect.x - clip_off.x), (LONG) (pcmd->ClipRect.y - clip_off.y),
|
// User callback, registered via ImDrawList::AddCallback()
|
||||||
(LONG) (pcmd->ClipRect.z - clip_off.x), (LONG) (pcmd->ClipRect.w - clip_off.y)};
|
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||||
auto texture = reinterpret_cast<IDirect3DBaseTexture9 *>(pcmd->TextureId);
|
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||||
g_pd3dDevice->SetTexture(0, texture);
|
ImGui_ImplDX9_SetupRenderState(draw_data, &render_state);
|
||||||
g_pd3dDevice->SetScissorRect(&r);
|
else
|
||||||
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
|
pcmd->UserCallback(draw_list, pcmd);
|
||||||
pcmd->VtxOffset + global_vtx_offset, 0,
|
}
|
||||||
(UINT) cmd_list->VtxBuffer.Size,
|
else
|
||||||
pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
|
{
|
||||||
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||||
|
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||||
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Apply scissor/clipping rectangle
|
||||||
|
const RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||||
|
device->SetScissorRect(&r);
|
||||||
|
|
||||||
|
// Bind texture, Draw
|
||||||
|
const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID();
|
||||||
|
device->SetTexture(0, texture);
|
||||||
|
device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)draw_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
global_idx_offset += draw_list->IdxBuffer.Size;
|
||||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
global_vtx_offset += draw_list->VtxBuffer.Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (back_buffer) {
|
// When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows
|
||||||
back_buffer->Release();
|
// from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560)
|
||||||
back_buffer = nullptr;
|
if (global_vtx_offset == 0)
|
||||||
}
|
bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
// restore previous depth stencil
|
// spice ldj
|
||||||
if (depth_stencil) {
|
{
|
||||||
g_pd3dDevice->SetDepthStencilSurface(depth_stencil);
|
if (render_state.back_buffer) {
|
||||||
depth_stencil->Release();
|
render_state.back_buffer->Release();
|
||||||
depth_stencil = nullptr;
|
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
|
if (render_target) {
|
||||||
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
|
device->SetRenderTarget(target, render_target);
|
||||||
auto render_target = render_targets[target];
|
render_target->Release();
|
||||||
|
}
|
||||||
if (render_target) {
|
|
||||||
g_pd3dDevice->SetRenderTarget(target, render_target);
|
|
||||||
render_target->Release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore the DX9 transform
|
// Restore the DX9 transform
|
||||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
|
device->SetTransform(D3DTS_WORLD, &last_world);
|
||||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
|
device->SetTransform(D3DTS_VIEW, &last_view);
|
||||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
|
device->SetTransform(D3DTS_PROJECTION, &last_projection);
|
||||||
|
|
||||||
// restore the DX9 state
|
// Restore the DX9 state
|
||||||
d3d9_state_block->Apply();
|
state_block->Apply();
|
||||||
d3d9_state_block->Release();
|
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
|
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
|
||||||
auto &io = ImGui::GetIO();
|
{
|
||||||
|
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.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.
|
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
|
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;
|
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();
|
ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||||
|
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||||
|
|
||||||
if (g_pd3dDevice) {
|
io.BackendRendererName = nullptr;
|
||||||
g_pd3dDevice->Release();
|
io.BackendRendererUserData = nullptr;
|
||||||
g_pd3dDevice = NULL;
|
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() {
|
void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex)
|
||||||
// Build texture atlas
|
{
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
|
||||||
unsigned char *pixels;
|
|
||||||
int width, height, bytes_per_pixel;
|
|
||||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
|
|
||||||
|
|
||||||
// Upload texture to graphics system
|
if (tex->Status == ImTextureStatus_WantCreate)
|
||||||
g_FontTexture = NULL;
|
{
|
||||||
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
|
// Create and upload new texture to graphics system
|
||||||
D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
|
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||||
return false;
|
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||||
D3DLOCKED_RECT tex_locked_rect;
|
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||||
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
|
LPDIRECT3DTEXTURE9 dx_tex = nullptr;
|
||||||
return false;
|
HRESULT hr = bd->pd3dDevice->CreateTexture(tex->Width, tex->Height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &dx_tex, nullptr);
|
||||||
for (int y = 0; y < height; y++)
|
if (hr < 0)
|
||||||
memcpy((unsigned char *) tex_locked_rect.pBits + tex_locked_rect.Pitch * y,
|
{
|
||||||
pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
|
IM_ASSERT(hr >= 0 && "Backend failed to create texture!");
|
||||||
g_FontTexture->UnlockRect(0);
|
return;
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// dear imgui: Renderer Backend for DirectX9
|
||||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||||
|
|
||||||
// Implemented features:
|
// Implemented features:
|
||||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||||
|
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||||
|
// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
|
||||||
|
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||||
|
|
||||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
// https://github.com/ocornut/imgui
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
#include "imgui.h"
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
struct IDirect3DDevice9;
|
struct IDirect3DDevice9;
|
||||||
|
|
||||||
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_Shutdown();
|
||||||
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
|
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
|
||||||
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data);
|
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
// Use if you want to reset your rendering device without losing ImGui state.
|
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||||
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
|
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
|
||||||
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();
|
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||||
|
|
||||||
|
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex);
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
|
|||||||
+13
-5
@@ -15,7 +15,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
//---- Define assertion handler. Defaults to calling assert().
|
//---- Define assertion handler. Defaults to calling assert().
|
||||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
// - If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||||
|
// - Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.
|
||||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||||
#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||||
|
|
||||||
@@ -29,7 +30,6 @@
|
|||||||
|
|
||||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
|
||||||
|
|
||||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||||
#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean/ProggyForever), remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert.
|
||||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||||
|
|
||||||
//---- Enable Test Engine / Automation features.
|
//---- Enable Test Engine / Automation features.
|
||||||
@@ -59,12 +60,15 @@
|
|||||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||||
|
|
||||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||||
#define IMGUI_USE_BGRA_PACKED_COLOR
|
#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
//---- Performance optimization for DX9
|
//---- Performance optimization for DX9
|
||||||
#define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
#define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||||
|
|
||||||
|
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||||
|
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||||
|
|
||||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||||
//#define IMGUI_USE_WCHAR32
|
//#define IMGUI_USE_WCHAR32
|
||||||
|
|
||||||
@@ -83,13 +87,13 @@
|
|||||||
|
|
||||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||||
|
// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience.
|
||||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||||
//#define IMGUI_ENABLE_FREETYPE
|
//#define IMGUI_ENABLE_FREETYPE
|
||||||
|
|
||||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||||
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
|
|
||||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||||
@@ -130,6 +134,10 @@
|
|||||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||||
//#define IM_DEBUG_BREAK __debugbreak()
|
//#define IM_DEBUG_BREAK __debugbreak()
|
||||||
|
|
||||||
|
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
|
||||||
|
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
|
||||||
|
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
|
||||||
|
|
||||||
//---- Debug Tools: Enable slower asserts
|
//---- Debug Tools: Enable slower asserts
|
||||||
//#define IMGUI_DEBUG_PARANOID
|
//#define IMGUI_DEBUG_PARANOID
|
||||||
|
|
||||||
|
|||||||
Vendored
+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)
|
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
||||||
// Get latest version at http://www.github.com/ocornut/imgui_club
|
// Get latest version at http://www.github.com/ocornut/imgui_club
|
||||||
//
|
// Licensed under The MIT License (MIT)
|
||||||
|
|
||||||
// Right-click anywhere to access the Options menu!
|
// Right-click anywhere to access the Options menu!
|
||||||
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
||||||
// The code assume a mono-space font for simplicity!
|
// The code assume a mono-space font for simplicity!
|
||||||
@@ -39,10 +40,22 @@
|
|||||||
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]
|
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]
|
||||||
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
|
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
|
||||||
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
|
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
|
||||||
|
// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34)
|
||||||
|
// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87.
|
||||||
|
// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35)
|
||||||
|
// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2])
|
||||||
|
// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3]
|
||||||
|
// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3]
|
||||||
|
// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview.
|
||||||
|
// *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed]
|
||||||
|
// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54)
|
||||||
|
// - v0.57 (2025/03/26): fixed warnings. using ImGui's ImSXX/ImUXX types instead of e.g. int32_t/uint32_t. (#56)
|
||||||
|
// - v0.58 (2025/03/31): fixed extraneous footer spacing (added in 0.51) breaking vertical auto-resize. (#53)
|
||||||
|
// - v0.59 (2025/04/08): fixed GotoAddrAndHighlight() not working if OptShowOptions is disabled.
|
||||||
//
|
//
|
||||||
// Todo/Bugs:
|
// TODO:
|
||||||
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.
|
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.
|
||||||
// - PageUp/PageDown are supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
|
// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
|
||||||
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
|
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
|
||||||
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
||||||
|
|
||||||
@@ -51,15 +64,18 @@
|
|||||||
#include <stdio.h> // sprintf, scanf
|
#include <stdio.h> // sprintf, scanf
|
||||||
#include <stdint.h> // uint8_t, etc.
|
#include <stdint.h> // uint8_t, etc.
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) && !defined(snprintf)
|
||||||
#define _PRISizeT "I"
|
|
||||||
#define ImSnprintf _snprintf
|
#define ImSnprintf _snprintf
|
||||||
#else
|
#else
|
||||||
#define _PRISizeT "z"
|
|
||||||
#define ImSnprintf snprintf
|
#define ImSnprintf snprintf
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__)
|
||||||
|
#define _PRISizeT "I"
|
||||||
|
#else
|
||||||
|
#define _PRISizeT "z"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) || defined(_UCRT)
|
||||||
#pragma warning (push)
|
#pragma warning (push)
|
||||||
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||||
#endif
|
#endif
|
||||||
@@ -88,9 +104,17 @@ struct MemoryEditor
|
|||||||
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
|
||||||
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
|
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
|
||||||
ImU32 HighlightColor; // // background color of highlighted bytes.
|
ImU32 HighlightColor; // // background color of highlighted bytes.
|
||||||
ImU8 (*ReadFn)(const ImU8* data, size_t off); // = 0 // optional handler to read bytes.
|
|
||||||
void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes.
|
// Function handlers
|
||||||
bool (*HighlightFn)(const ImU8* data, size_t off);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes.
|
||||||
|
void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes.
|
||||||
|
bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||||
|
ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes.
|
||||||
|
void* UserData; // = NULL // user data forwarded to the function handlers
|
||||||
|
|
||||||
|
// Public read-only data
|
||||||
|
bool MouseHovered; // set when mouse is hovering a value.
|
||||||
|
size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set.
|
||||||
|
|
||||||
// [Internal State]
|
// [Internal State]
|
||||||
bool ContentsWidthChanged;
|
bool ContentsWidthChanged;
|
||||||
@@ -101,7 +125,7 @@ struct MemoryEditor
|
|||||||
char AddrInputBuf[32];
|
char AddrInputBuf[32];
|
||||||
size_t GotoAddr;
|
size_t GotoAddr;
|
||||||
size_t HighlightMin, HighlightMax;
|
size_t HighlightMin, HighlightMax;
|
||||||
int PreviewEndianess;
|
int PreviewEndianness;
|
||||||
ImGuiDataType PreviewDataType;
|
ImGuiDataType PreviewDataType;
|
||||||
|
|
||||||
MemoryEditor()
|
MemoryEditor()
|
||||||
@@ -120,9 +144,11 @@ struct MemoryEditor
|
|||||||
OptAddrDigitsCount = 0;
|
OptAddrDigitsCount = 0;
|
||||||
OptFooterExtraHeight = 0.0f;
|
OptFooterExtraHeight = 0.0f;
|
||||||
HighlightColor = IM_COL32(255, 255, 255, 50);
|
HighlightColor = IM_COL32(255, 255, 255, 50);
|
||||||
ReadFn = NULL;
|
ReadFn = nullptr;
|
||||||
WriteFn = NULL;
|
WriteFn = nullptr;
|
||||||
HighlightFn = NULL;
|
HighlightFn = nullptr;
|
||||||
|
BgColorFn = nullptr;
|
||||||
|
UserData = nullptr;
|
||||||
|
|
||||||
// State/Internals
|
// State/Internals
|
||||||
ContentsWidthChanged = false;
|
ContentsWidthChanged = false;
|
||||||
@@ -131,8 +157,10 @@ struct MemoryEditor
|
|||||||
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
||||||
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
||||||
GotoAddr = (size_t)-1;
|
GotoAddr = (size_t)-1;
|
||||||
|
MouseHovered = false;
|
||||||
|
MouseHoveredAddr = 0;
|
||||||
HighlightMin = HighlightMax = (size_t)-1;
|
HighlightMin = HighlightMax = (size_t)-1;
|
||||||
PreviewEndianess = 0;
|
PreviewEndianness = 0;
|
||||||
PreviewDataType = ImGuiDataType_S32;
|
PreviewDataType = ImGuiDataType_S32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,16 +173,16 @@ struct MemoryEditor
|
|||||||
|
|
||||||
struct Sizes
|
struct Sizes
|
||||||
{
|
{
|
||||||
int AddrDigitsCount;
|
int AddrDigitsCount; // Number of digits required to represent maximum address.
|
||||||
float LineHeight;
|
float LineHeight; // Height of each line (no spacing).
|
||||||
float GlyphWidth;
|
float GlyphWidth; // Glyph width (assume mono-space).
|
||||||
float HexCellWidth;
|
float HexCellWidth; // Width of a hex edit cell ~2.5f * GlypHWidth.
|
||||||
float SpacingBetweenMidCols;
|
float SpacingBetweenMidCols; // Spacing between each columns section (OptMidColsCount).
|
||||||
float PosHexStart;
|
float OffsetHexMinX;
|
||||||
float PosHexEnd;
|
float OffsetHexMaxX;
|
||||||
float PosAsciiStart;
|
float OffsetAsciiMinX;
|
||||||
float PosAsciiEnd;
|
float OffsetAsciiMaxX;
|
||||||
float WindowWidth;
|
float WindowWidth; // Ideal window width.
|
||||||
|
|
||||||
Sizes() { memset(this, 0, sizeof(*this)); }
|
Sizes() { memset(this, 0, sizeof(*this)); }
|
||||||
};
|
};
|
||||||
@@ -170,17 +198,17 @@ struct MemoryEditor
|
|||||||
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
|
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
|
||||||
s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere
|
s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere
|
||||||
s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
|
s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
|
||||||
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
s.OffsetHexMinX = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||||
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
s.OffsetHexMaxX = s.OffsetHexMinX + (s.HexCellWidth * Cols);
|
||||||
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
s.OffsetAsciiMinX = s.OffsetAsciiMaxX = s.OffsetHexMaxX;
|
||||||
if (OptShowAscii)
|
if (OptShowAscii)
|
||||||
{
|
{
|
||||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
s.OffsetAsciiMinX = s.OffsetHexMaxX + s.GlyphWidth * 1;
|
||||||
if (OptMidColsCount > 0)
|
if (OptMidColsCount > 0)
|
||||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
s.OffsetAsciiMinX += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
s.OffsetAsciiMaxX = s.OffsetAsciiMinX + Cols * s.GlyphWidth;
|
||||||
}
|
}
|
||||||
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
s.WindowWidth = s.OffsetAsciiMaxX + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standalone Memory Editor window
|
// Standalone Memory Editor window
|
||||||
@@ -194,8 +222,6 @@ struct MemoryEditor
|
|||||||
Open = true;
|
Open = true;
|
||||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
||||||
{
|
{
|
||||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
|
||||||
ImGui::OpenPopup("context");
|
|
||||||
DrawContents(mem_data, mem_size, base_display_addr);
|
DrawContents(mem_data, mem_size, base_display_addr);
|
||||||
if (ContentsWidthChanged)
|
if (ContentsWidthChanged)
|
||||||
{
|
{
|
||||||
@@ -217,6 +243,8 @@ struct MemoryEditor
|
|||||||
CalcSizes(s, mem_size, base_display_addr);
|
CalcSizes(s, mem_size, base_display_addr);
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
|
||||||
|
const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos();
|
||||||
|
|
||||||
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
||||||
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
||||||
const float height_separator = style.ItemSpacing.y;
|
const float height_separator = style.ItemSpacing.y;
|
||||||
@@ -225,20 +253,21 @@ struct MemoryEditor
|
|||||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||||
if (OptShowDataPreview)
|
if (OptShowDataPreview)
|
||||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
||||||
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||||
|
|
||||||
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
|
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
|
||||||
|
const ImVec2 avail_size = ImGui::GetContentRegionAvail();
|
||||||
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||||
ImGuiListClipper clipper;
|
ImGuiListClipper clipper;
|
||||||
clipper.Begin(line_total_count, s.LineHeight);
|
clipper.Begin(line_total_count, s.LineHeight);
|
||||||
|
|
||||||
bool data_next = false;
|
bool data_next = false;
|
||||||
|
|
||||||
if (ReadOnly || DataEditingAddr >= mem_size)
|
if (DataEditingAddr >= mem_size)
|
||||||
DataEditingAddr = (size_t)-1;
|
DataEditingAddr = (size_t)-1;
|
||||||
if (DataPreviewAddr >= mem_size)
|
if (DataPreviewAddr >= mem_size)
|
||||||
DataPreviewAddr = (size_t)-1;
|
DataPreviewAddr = (size_t)-1;
|
||||||
@@ -249,16 +278,16 @@ struct MemoryEditor
|
|||||||
if (DataEditingAddr != (size_t)-1)
|
if (DataEditingAddr != (size_t)-1)
|
||||||
{
|
{
|
||||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
||||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
|
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
|
||||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; }
|
else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; }
|
||||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
|
else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
|
||||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw vertical separator
|
// Draw vertical separator
|
||||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||||
if (OptShowAscii)
|
if (OptShowAscii)
|
||||||
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
draw_list->AddLine(ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||||
|
|
||||||
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
||||||
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
||||||
@@ -268,36 +297,51 @@ struct MemoryEditor
|
|||||||
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
||||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||||
|
|
||||||
|
MouseHovered = false;
|
||||||
|
MouseHoveredAddr = 0;
|
||||||
|
|
||||||
while (clipper.Step())
|
while (clipper.Step())
|
||||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
||||||
{
|
{
|
||||||
size_t addr = (size_t)(line_i * Cols);
|
size_t addr = (size_t)line_i * Cols;
|
||||||
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||||
|
|
||||||
// Draw Hexadecimal
|
// Draw Hexadecimal
|
||||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||||
{
|
{
|
||||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
|
float byte_pos_x = s.OffsetHexMinX + s.HexCellWidth * n;
|
||||||
if (OptMidColsCount > 0)
|
if (OptMidColsCount > 0)
|
||||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
ImGui::SameLine(byte_pos_x);
|
ImGui::SameLine(byte_pos_x);
|
||||||
|
|
||||||
// Draw highlight
|
// Draw highlight or custom background color
|
||||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr));
|
const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData));
|
||||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
||||||
|
|
||||||
|
ImU32 bg_color = 0;
|
||||||
|
bool is_next_byte_highlighted = false;
|
||||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||||
{
|
{
|
||||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size));
|
||||||
float highlight_width = s.GlyphWidth * 2;
|
bg_color = HighlightColor;
|
||||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1)));
|
}
|
||||||
|
else if (BgColorFn != nullptr)
|
||||||
|
{
|
||||||
|
is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0);
|
||||||
|
bg_color = BgColorFn(mem_data, addr, UserData);
|
||||||
|
}
|
||||||
|
if (bg_color != 0)
|
||||||
|
{
|
||||||
|
float bg_width = s.GlyphWidth * 2;
|
||||||
if (is_next_byte_highlighted || (n + 1 == Cols))
|
if (is_next_byte_highlighted || (n + 1 == Cols))
|
||||||
{
|
{
|
||||||
highlight_width = s.HexCellWidth;
|
bg_width = s.HexCellWidth;
|
||||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||||
highlight_width += s.SpacingBetweenMidCols;
|
bg_width += s.SpacingBetweenMidCols;
|
||||||
}
|
}
|
||||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor);
|
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||||
|
draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DataEditingAddr == addr)
|
if (DataEditingAddr == addr)
|
||||||
@@ -308,17 +352,21 @@ struct MemoryEditor
|
|||||||
if (DataEditingTakeFocus)
|
if (DataEditingTakeFocus)
|
||||||
{
|
{
|
||||||
ImGui::SetKeyboardFocusHere(0);
|
ImGui::SetKeyboardFocusHere(0);
|
||||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||||
}
|
}
|
||||||
struct UserData
|
struct InputTextUserData
|
||||||
{
|
{
|
||||||
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
||||||
static int Callback(ImGuiInputTextCallbackData* data)
|
static int Callback(ImGuiInputTextCallbackData* data)
|
||||||
{
|
{
|
||||||
UserData* user_data = (UserData*)data->UserData;
|
InputTextUserData* user_data = (InputTextUserData*)data->UserData;
|
||||||
if (!data->HasSelection())
|
if (!data->HasSelection())
|
||||||
user_data->CursorPos = data->CursorPos;
|
user_data->CursorPos = data->CursorPos;
|
||||||
|
#if IMGUI_VERSION_NUM < 19102
|
||||||
|
if (data->Flags & ImGuiInputTextFlags_ReadOnly)
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||||
{
|
{
|
||||||
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
|
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
|
||||||
@@ -334,39 +382,42 @@ struct MemoryEditor
|
|||||||
char CurrentBufOverwrite[3]; // Input
|
char CurrentBufOverwrite[3]; // Input
|
||||||
int CursorPos; // Output
|
int CursorPos; // Output
|
||||||
};
|
};
|
||||||
UserData user_data;
|
InputTextUserData input_text_user_data;
|
||||||
user_data.CursorPos = -1;
|
input_text_user_data.CursorPos = -1;
|
||||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
|
||||||
#if IMGUI_VERSION_NUM >= 18104
|
if (ReadOnly)
|
||||||
flags |= ImGuiInputTextFlags_AlwaysOverwrite;
|
flags |= ImGuiInputTextFlags_ReadOnly;
|
||||||
#else
|
flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode
|
||||||
flags |= ImGuiInputTextFlags_AlwaysInsertMode;
|
|
||||||
#endif
|
|
||||||
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
|
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
|
||||||
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, UserData::Callback, &user_data))
|
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data))
|
||||||
data_write = data_next = true;
|
data_write = data_next = true;
|
||||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||||
DataEditingTakeFocus = false;
|
DataEditingTakeFocus = false;
|
||||||
if (user_data.CursorPos >= 2)
|
if (input_text_user_data.CursorPos >= 2)
|
||||||
data_write = data_next = true;
|
data_write = data_next = true;
|
||||||
if (data_editing_addr_next != (size_t)-1)
|
if (data_editing_addr_next != (size_t)-1)
|
||||||
data_write = data_next = false;
|
data_write = data_next = false;
|
||||||
unsigned int data_input_value = 0;
|
unsigned int data_input_value = 0;
|
||||||
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||||
{
|
{
|
||||||
if (WriteFn)
|
if (WriteFn)
|
||||||
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);
|
||||||
else
|
else
|
||||||
mem_data[addr] = (ImU8)data_input_value;
|
mem_data[addr] = (ImU8)data_input_value;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
{
|
||||||
|
MouseHovered = true;
|
||||||
|
MouseHoveredAddr = addr;
|
||||||
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
||||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||||
|
|
||||||
if (OptShowHexII)
|
if (OptShowHexII)
|
||||||
{
|
{
|
||||||
@@ -386,10 +437,15 @@ struct MemoryEditor
|
|||||||
else
|
else
|
||||||
ImGui::Text(format_byte_space, b);
|
ImGui::Text(format_byte_space, b);
|
||||||
}
|
}
|
||||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
|
if (ImGui::IsItemHovered())
|
||||||
{
|
{
|
||||||
DataEditingTakeFocus = true;
|
MouseHovered = true;
|
||||||
data_editing_addr_next = addr;
|
MouseHoveredAddr = addr;
|
||||||
|
if (ImGui::IsMouseClicked(0))
|
||||||
|
{
|
||||||
|
DataEditingTakeFocus = true;
|
||||||
|
data_editing_addr_next = addr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,15 +453,24 @@ struct MemoryEditor
|
|||||||
if (OptShowAscii)
|
if (OptShowAscii)
|
||||||
{
|
{
|
||||||
// Draw ASCII values
|
// Draw ASCII values
|
||||||
ImGui::SameLine(s.PosAsciiStart);
|
ImGui::SameLine(s.OffsetAsciiMinX);
|
||||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||||
addr = line_i * Cols;
|
addr = (size_t)line_i * Cols;
|
||||||
|
|
||||||
|
const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x;
|
||||||
|
const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.OffsetAsciiMaxX - s.OffsetAsciiMinX) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1;
|
||||||
|
|
||||||
ImGui::PushID(line_i);
|
ImGui::PushID(line_i);
|
||||||
if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))
|
if (ImGui::InvisibleButton("ascii", ImVec2(s.OffsetAsciiMaxX - s.OffsetAsciiMinX, s.LineHeight)))
|
||||||
{
|
{
|
||||||
DataEditingAddr = DataPreviewAddr = addr + (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth);
|
DataEditingAddr = DataPreviewAddr = mouse_addr;
|
||||||
DataEditingTakeFocus = true;
|
DataEditingTakeFocus = true;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
{
|
||||||
|
MouseHovered = true;
|
||||||
|
MouseHoveredAddr = mouse_addr;
|
||||||
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||||
{
|
{
|
||||||
@@ -414,7 +479,11 @@ struct MemoryEditor
|
|||||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||||
}
|
}
|
||||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
else if (BgColorFn)
|
||||||
|
{
|
||||||
|
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData));
|
||||||
|
}
|
||||||
|
unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||||
pos.x += s.GlyphWidth;
|
pos.x += s.GlyphWidth;
|
||||||
@@ -422,10 +491,14 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
|
const float child_width = ImGui::GetWindowSize().x;
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
||||||
|
ImVec2 backup_pos = ImGui::GetCursorScreenPos();
|
||||||
ImGui::SetCursorPosX(s.WindowWidth);
|
ImGui::SetCursorPosX(s.WindowWidth);
|
||||||
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||||
|
ImGui::SetCursorScreenPos(backup_pos);
|
||||||
|
|
||||||
if (data_next && DataEditingAddr + 1 < mem_size)
|
if (data_next && DataEditingAddr + 1 < mem_size)
|
||||||
{
|
{
|
||||||
@@ -450,18 +523,28 @@ struct MemoryEditor
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
if (GotoAddr != (size_t)-1)
|
||||||
{
|
{
|
||||||
IM_UNUSED(mem_data);
|
if (GotoAddr < mem_size)
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
{
|
||||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
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
|
const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y);
|
||||||
if (ImGui::Button("Options"))
|
//ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255));
|
||||||
ImGui::OpenPopup("context");
|
if (OptShowOptions)
|
||||||
if (ImGui::BeginPopup("context"))
|
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);
|
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
|
||||||
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
|
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
|
||||||
@@ -473,6 +556,17 @@ struct MemoryEditor
|
|||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
|
||||||
|
{
|
||||||
|
IM_UNUSED(mem_data);
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
||||||
|
|
||||||
|
// Options menu
|
||||||
|
if (ImGui::Button("Options"))
|
||||||
|
ImGui::OpenPopup("OptionsPopup");
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
||||||
@@ -488,18 +582,11 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GotoAddr != (size_t)-1)
|
//if (MouseHovered)
|
||||||
{
|
//{
|
||||||
if (GotoAddr < mem_size)
|
// ImGui::SameLine();
|
||||||
{
|
// ImGui::Text("Hovered: %p", MouseHoveredAddr);
|
||||||
ImGui::BeginChild("##scrolling");
|
//}
|
||||||
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());
|
|
||||||
ImGui::EndChild();
|
|
||||||
DataEditingAddr = DataPreviewAddr = GotoAddr;
|
|
||||||
DataEditingTakeFocus = true;
|
|
||||||
}
|
|
||||||
GotoAddr = (size_t)-1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
||||||
@@ -511,16 +598,21 @@ struct MemoryEditor
|
|||||||
ImGui::Text("Preview as:");
|
ImGui::Text("Preview as:");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||||
|
|
||||||
|
static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double };
|
||||||
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
||||||
{
|
{
|
||||||
for (int n = 0; n < ImGuiDataType_COUNT; n++)
|
for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++)
|
||||||
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
|
{
|
||||||
PreviewDataType = (ImGuiDataType)n;
|
ImGuiDataType data_type = supported_data_types[n];
|
||||||
|
if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))
|
||||||
|
PreviewDataType = data_type;
|
||||||
|
}
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||||
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
|
ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0");
|
||||||
|
|
||||||
char buf[128] = "";
|
char buf[128] = "";
|
||||||
float x = s.GlyphWidth * 6.0f;
|
float x = s.GlyphWidth * 6.0f;
|
||||||
@@ -537,18 +629,19 @@ struct MemoryEditor
|
|||||||
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utilities for Data Preview
|
// Utilities for Data Preview (since we don't access imgui_internal.h)
|
||||||
|
// FIXME: This technically depends on ImGuiDataType order.
|
||||||
const char* DataTypeGetDesc(ImGuiDataType data_type) const
|
const char* DataTypeGetDesc(ImGuiDataType data_type) const
|
||||||
{
|
{
|
||||||
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
||||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs));
|
||||||
return descs[data_type];
|
return descs[data_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
||||||
{
|
{
|
||||||
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
||||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes));
|
||||||
return sizes[data_type];
|
return sizes[data_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,18 +654,18 @@ struct MemoryEditor
|
|||||||
|
|
||||||
bool IsBigEndian() const
|
bool IsBigEndian() const
|
||||||
{
|
{
|
||||||
uint16_t x = 1;
|
ImU16 x = 1;
|
||||||
char c[2];
|
char c[2];
|
||||||
memcpy(c, &x, 2);
|
memcpy(c, &x, 2);
|
||||||
return c[0] != 0;
|
return c[0] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* EndianessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||||
{
|
{
|
||||||
if (is_little_endian)
|
if (is_little_endian)
|
||||||
{
|
{
|
||||||
uint8_t* dst = (uint8_t*)_dst;
|
ImU8* dst = (ImU8*)_dst;
|
||||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
ImU8* src = (ImU8*)_src + s - 1;
|
||||||
for (int i = 0, n = (int)s; i < n; ++i)
|
for (int i = 0, n = (int)s; i < n; ++i)
|
||||||
memcpy(dst++, src--, 1);
|
memcpy(dst++, src--, 1);
|
||||||
return _dst;
|
return _dst;
|
||||||
@@ -583,7 +676,7 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* EndianessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
|
||||||
{
|
{
|
||||||
if (is_little_endian)
|
if (is_little_endian)
|
||||||
{
|
{
|
||||||
@@ -591,23 +684,23 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t* dst = (uint8_t*)_dst;
|
ImU8* dst = (ImU8*)_dst;
|
||||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
ImU8* src = (ImU8*)_src + s - 1;
|
||||||
for (int i = 0, n = (int)s; i < n; ++i)
|
for (int i = 0, n = (int)s; i < n; ++i)
|
||||||
memcpy(dst++, src--, 1);
|
memcpy(dst++, src--, 1);
|
||||||
return _dst;
|
return _dst;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* EndianessCopy(void* dst, void* src, size_t size) const
|
void* EndiannessCopy(void* dst, void* src, size_t size) const
|
||||||
{
|
{
|
||||||
static void* (*fp)(void*, void*, size_t, int) = NULL;
|
static void* (*fp)(void*, void*, size_t, int) = nullptr;
|
||||||
if (fp == NULL)
|
if (fp == nullptr)
|
||||||
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
|
fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;
|
||||||
return fp(dst, src, size, PreviewEndianess);
|
return fp(dst, src, size, PreviewEndianness);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* FormatBinary(const uint8_t* buf, int width) const
|
const char* FormatBinary(const ImU8* buf, int width) const
|
||||||
{
|
{
|
||||||
IM_ASSERT(width <= 64);
|
IM_ASSERT(width <= 64);
|
||||||
size_t out_n = 0;
|
size_t out_n = 0;
|
||||||
@@ -627,19 +720,19 @@ struct MemoryEditor
|
|||||||
// [Internal]
|
// [Internal]
|
||||||
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
||||||
{
|
{
|
||||||
uint8_t buf[8];
|
ImU8 buf[8];
|
||||||
size_t elem_size = DataTypeGetSize(data_type);
|
size_t elem_size = DataTypeGetSize(data_type);
|
||||||
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
||||||
if (ReadFn)
|
if (ReadFn)
|
||||||
for (int i = 0, n = (int)size; i < n; ++i)
|
for (int i = 0, n = (int)size; i < n; ++i)
|
||||||
buf[i] = ReadFn(mem_data, addr + i);
|
buf[i] = ReadFn(mem_data, addr + i, UserData);
|
||||||
else
|
else
|
||||||
memcpy(buf, mem_data + addr, size);
|
memcpy(buf, mem_data + addr, size);
|
||||||
|
|
||||||
if (data_format == DataFormat_Bin)
|
if (data_format == DataFormat_Bin)
|
||||||
{
|
{
|
||||||
uint8_t binbuf[8];
|
ImU8 binbuf[8];
|
||||||
EndianessCopy(binbuf, buf, size);
|
EndiannessCopy(binbuf, buf, size);
|
||||||
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -649,84 +742,85 @@ struct MemoryEditor
|
|||||||
{
|
{
|
||||||
case ImGuiDataType_S8:
|
case ImGuiDataType_S8:
|
||||||
{
|
{
|
||||||
int8_t int8 = 0;
|
ImS8 data = 0;
|
||||||
EndianessCopy(&int8, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", int8); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U8:
|
case ImGuiDataType_U8:
|
||||||
{
|
{
|
||||||
uint8_t uint8 = 0;
|
ImU8 data = 0;
|
||||||
EndianessCopy(&uint8, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", uint8); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_S16:
|
case ImGuiDataType_S16:
|
||||||
{
|
{
|
||||||
int16_t int16 = 0;
|
ImS16 data = 0;
|
||||||
EndianessCopy(&int16, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", int16); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U16:
|
case ImGuiDataType_U16:
|
||||||
{
|
{
|
||||||
uint16_t uint16 = 0;
|
ImU16 data = 0;
|
||||||
EndianessCopy(&uint16, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", uint16); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_S32:
|
case ImGuiDataType_S32:
|
||||||
{
|
{
|
||||||
int32_t int32 = 0;
|
ImS32 data = 0;
|
||||||
EndianessCopy(&int32, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", int32); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U32:
|
case ImGuiDataType_U32:
|
||||||
{
|
{
|
||||||
uint32_t uint32 = 0;
|
ImU32 data = 0;
|
||||||
EndianessCopy(&uint32, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", uint32); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_S64:
|
case ImGuiDataType_S64:
|
||||||
{
|
{
|
||||||
int64_t int64 = 0;
|
ImS64 data = 0;
|
||||||
EndianessCopy(&int64, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_U64:
|
case ImGuiDataType_U64:
|
||||||
{
|
{
|
||||||
uint64_t uint64 = 0;
|
ImU64 data = 0;
|
||||||
EndianessCopy(&uint64, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)uint64); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_Float:
|
case ImGuiDataType_Float:
|
||||||
{
|
{
|
||||||
float float32 = 0.0f;
|
float data = 0.0f;
|
||||||
EndianessCopy(&float32, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float32); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ImGuiDataType_Double:
|
case ImGuiDataType_Double:
|
||||||
{
|
{
|
||||||
double float64 = 0.0;
|
double data = 0.0;
|
||||||
EndianessCopy(&float64, buf, size);
|
EndiannessCopy(&data, buf, size);
|
||||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float64); return; }
|
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
|
||||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
|
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
case ImGuiDataType_COUNT:
|
case ImGuiDataType_COUNT:
|
||||||
break;
|
break;
|
||||||
} // Switch
|
} // Switch
|
||||||
|
|||||||
+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) {
|
if (node->y > min_y) {
|
||||||
// raise min_y higher.
|
// raise min_y higher.
|
||||||
// we've accounted for all waste up to min_y,
|
// we've accounted for all waste up to min_y,
|
||||||
// but we'll now add more waste for everything we've visted
|
// but we'll now add more waste for everything we've visited
|
||||||
waste_area += visited_width * (node->y - min_y);
|
waste_area += visited_width * (node->y - min_y);
|
||||||
min_y = node->y;
|
min_y = node->y;
|
||||||
// the first time through, visited_width might be reduced
|
// the first time through, visited_width might be reduced
|
||||||
@@ -470,7 +470,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
|
|||||||
|
|
||||||
// insert the new node into the right starting point, and
|
// insert the new node into the right starting point, and
|
||||||
// let 'cur' point to the remaining nodes needing to be
|
// let 'cur' point to the remaining nodes needing to be
|
||||||
// stiched back in
|
// stitched back in
|
||||||
|
|
||||||
cur = *res.prev_link;
|
cur = *res.prev_link;
|
||||||
if (cur->x < res.x) {
|
if (cur->x < res.x) {
|
||||||
|
|||||||
+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)
|
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||||
// - Added name to struct or it may be forward declared in our code.
|
// - Added name to struct or it may be forward declared in our code.
|
||||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||||
// Grep for [DEAR IMGUI] to find the changes.
|
// - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||||
|
// Grep for [DEAR IMGUI] to find some changes.
|
||||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||||
|
|
||||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
//
|
//
|
||||||
// VERSION HISTORY
|
// VERSION HISTORY
|
||||||
//
|
//
|
||||||
|
// !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||||
// 1.14 (2021-07-11) page up/down, various fixes
|
// 1.14 (2021-07-11) page up/down, various fixes
|
||||||
// 1.13 (2019-02-07) fix bug in undo size management
|
// 1.13 (2019-02-07) fix bug in undo size management
|
||||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||||
@@ -141,12 +143,14 @@
|
|||||||
// with previous char)
|
// with previous char)
|
||||||
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
||||||
// (return type is int, -1 means not valid to insert)
|
// (return type is int, -1 means not valid to insert)
|
||||||
|
// (not supported if you want to use UTF-8, see below)
|
||||||
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
|
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
|
||||||
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
||||||
// as manually wordwrapping for end-of-line positioning
|
// as manually wordwrapping for end-of-line positioning
|
||||||
//
|
//
|
||||||
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
|
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
|
||||||
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
||||||
|
// returns number of characters actually inserted. [DEAR IMGUI]
|
||||||
//
|
//
|
||||||
// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
|
// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
|
||||||
//
|
//
|
||||||
@@ -178,6 +182,13 @@
|
|||||||
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
|
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
|
||||||
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
|
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
|
||||||
//
|
//
|
||||||
|
// To support UTF-8:
|
||||||
|
//
|
||||||
|
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
|
||||||
|
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
|
||||||
|
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
|
||||||
|
// Instead, call stb_textedit_text() directly for text contents.
|
||||||
|
//
|
||||||
// Keyboard input must be encoded as a single integer value; e.g. a character code
|
// Keyboard input must be encoded as a single integer value; e.g. a character code
|
||||||
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
||||||
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
||||||
@@ -250,8 +261,10 @@
|
|||||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||||
// transformed into text and stb_textedit_text() is automatically called.
|
// transformed into text and stb_textedit_text() is automatically called.
|
||||||
//
|
//
|
||||||
// text: [DEAR IMGUI] added 2024-09
|
// text: (added 2025)
|
||||||
// call this to text inputs sent to the textfield.
|
// call this to directly send text input the textfield, which is required
|
||||||
|
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
|
||||||
|
// cannot infer text length.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// When rendering, you can read the cursor position and selection state from
|
// When rendering, you can read the cursor position and selection state from
|
||||||
@@ -400,6 +413,16 @@ typedef struct
|
|||||||
#define IMSTB_TEXTEDIT_memmove memmove
|
#define IMSTB_TEXTEDIT_memmove memmove
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// [DEAR IMGUI]
|
||||||
|
// Functions must be implemented for UTF8 support
|
||||||
|
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||||
|
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||||
|
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||||
|
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
|
||||||
|
#endif
|
||||||
|
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||||
|
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
|
||||||
|
#endif
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -407,7 +430,7 @@ typedef struct
|
|||||||
//
|
//
|
||||||
|
|
||||||
// traverse the layout to locate the nearest character to a display position
|
// traverse the layout to locate the nearest character to a display position
|
||||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
|
||||||
{
|
{
|
||||||
StbTexteditRow r;
|
StbTexteditRow r;
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||||
@@ -417,6 +440,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
|||||||
r.x0 = r.x1 = 0;
|
r.x0 = r.x1 = 0;
|
||||||
r.ymin = r.ymax = 0;
|
r.ymin = r.ymax = 0;
|
||||||
r.num_chars = 0;
|
r.num_chars = 0;
|
||||||
|
*out_side_on_line = 0;
|
||||||
|
|
||||||
// search rows to find one that straddles 'y'
|
// search rows to find one that straddles 'y'
|
||||||
while (i < n) {
|
while (i < n) {
|
||||||
@@ -436,7 +460,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
|||||||
|
|
||||||
// below all text, return 'after' last character
|
// below all text, return 'after' last character
|
||||||
if (i >= n)
|
if (i >= n)
|
||||||
|
{
|
||||||
|
*out_side_on_line = 1;
|
||||||
return n;
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// check if it's before the beginning of the line
|
// check if it's before the beginning of the line
|
||||||
if (x < r.x0)
|
if (x < r.x0)
|
||||||
@@ -449,6 +476,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
|||||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||||
if (x < prev_x+w) {
|
if (x < prev_x+w) {
|
||||||
|
*out_side_on_line = (k == 0) ? 0 : 1;
|
||||||
if (x < prev_x+w/2)
|
if (x < prev_x+w/2)
|
||||||
return k+i;
|
return k+i;
|
||||||
else
|
else
|
||||||
@@ -460,6 +488,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if the last character is a newline, return that. otherwise return 'after' the last character
|
// if the last character is a newline, return that. otherwise return 'after' the last character
|
||||||
|
*out_side_on_line = 1;
|
||||||
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
|
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
|
||||||
return i+r.num_chars-1;
|
return i+r.num_chars-1;
|
||||||
else
|
else
|
||||||
@@ -471,6 +500,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
|||||||
{
|
{
|
||||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||||
// goes off the top or bottom of the text
|
// goes off the top or bottom of the text
|
||||||
|
int side_on_line;
|
||||||
if( state->single_line )
|
if( state->single_line )
|
||||||
{
|
{
|
||||||
StbTexteditRow r;
|
StbTexteditRow r;
|
||||||
@@ -478,16 +508,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
|||||||
y = r.ymin;
|
y = r.ymin;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->cursor = stb_text_locate_coord(str, x, y);
|
state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||||
state->select_start = state->cursor;
|
state->select_start = state->cursor;
|
||||||
state->select_end = state->cursor;
|
state->select_end = state->cursor;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
|
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||||
}
|
}
|
||||||
|
|
||||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||||
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||||
{
|
{
|
||||||
int p = 0;
|
int p = 0;
|
||||||
|
int side_on_line;
|
||||||
|
|
||||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||||
// goes off the top or bottom of the text
|
// goes off the top or bottom of the text
|
||||||
@@ -501,8 +533,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
|||||||
if (state->select_start == state->select_end)
|
if (state->select_start == state->select_end)
|
||||||
state->select_start = state->cursor;
|
state->select_start = state->cursor;
|
||||||
|
|
||||||
p = stb_text_locate_coord(str, x, y);
|
p = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||||
state->cursor = state->select_end = p;
|
state->cursor = state->select_end = p;
|
||||||
|
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -552,6 +585,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
|
|||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||||
if (n < i + r.num_chars)
|
if (n < i + r.num_chars)
|
||||||
break;
|
break;
|
||||||
|
if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
|
||||||
|
break;
|
||||||
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
|
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
|
||||||
break; // [DEAR IMGUI]
|
break; // [DEAR IMGUI]
|
||||||
prev_start = i;
|
prev_start = i;
|
||||||
@@ -648,15 +683,33 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [DEAR IMGUI]
|
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
|
||||||
// Functions must be implemented for UTF8 support
|
#ifndef STB_TEXTEDIT_MOVELINESTART
|
||||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
{
|
||||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
if (state->single_line)
|
||||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
|
return 0;
|
||||||
|
while (cursor > 0) {
|
||||||
|
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
|
||||||
|
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||||
|
break;
|
||||||
|
cursor = prev;
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
|
||||||
#endif
|
#endif
|
||||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
#ifndef STB_TEXTEDIT_MOVELINEEND
|
||||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
|
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||||
|
{
|
||||||
|
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||||
|
if (state->single_line)
|
||||||
|
return n;
|
||||||
|
while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
|
||||||
|
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||||
@@ -668,9 +721,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
|||||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||||
{
|
{
|
||||||
--c; // always move at least one character
|
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
|
||||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
while (c >= 0 && !is_word_boundary(str, c))
|
||||||
--c;
|
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
|
||||||
|
|
||||||
if( c < 0 )
|
if( c < 0 )
|
||||||
c = 0;
|
c = 0;
|
||||||
@@ -684,9 +737,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
|
|||||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||||
{
|
{
|
||||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||||
++c; // always move at least one character
|
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
|
||||||
while( c < len && !is_word_boundary( str, c ) )
|
while( c < len && !is_word_boundary( str, c ) )
|
||||||
++c;
|
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
|
||||||
|
|
||||||
if( c > len )
|
if( c > len )
|
||||||
c = len;
|
c = len;
|
||||||
@@ -725,7 +778,8 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
|||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_delete_selection(str,state);
|
stb_textedit_delete_selection(str,state);
|
||||||
// try to insert the characters
|
// try to insert the characters
|
||||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {
|
len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len);
|
||||||
|
if (len) {
|
||||||
stb_text_makeundo_insert(state, state->cursor, len);
|
stb_text_makeundo_insert(state, state->cursor, len);
|
||||||
state->cursor += len;
|
state->cursor += len;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
@@ -739,6 +793,7 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
|||||||
#define STB_TEXTEDIT_KEYTYPE int
|
#define STB_TEXTEDIT_KEYTYPE int
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// API key: process text input
|
||||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||||
{
|
{
|
||||||
@@ -749,14 +804,15 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
|
|||||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||||
|
if (text_len) {
|
||||||
state->cursor += text_len;
|
state->cursor += text_len;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||||
|
if (text_len) {
|
||||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||||
state->cursor += text_len;
|
state->cursor += text_len;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
@@ -771,6 +827,7 @@ retry:
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
default: {
|
default: {
|
||||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||||
|
// This is not suitable for UTF-8 support.
|
||||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||||
if (c > 0) {
|
if (c > 0) {
|
||||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||||
@@ -911,15 +968,16 @@ retry:
|
|||||||
|
|
||||||
// [DEAR IMGUI]
|
// [DEAR IMGUI]
|
||||||
// going down while being on the last line shouldn't bring us to that line end
|
// going down while being on the last line shouldn't bring us to that line end
|
||||||
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
//if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||||
break;
|
// break;
|
||||||
|
|
||||||
// now find character position down a row
|
// now find character position down a row
|
||||||
state->cursor = start;
|
state->cursor = start;
|
||||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||||
x = row.x0;
|
x = row.x0;
|
||||||
for (i=0; i < row.num_chars; ++i) {
|
for (i=0; i < row.num_chars; ) {
|
||||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||||
|
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||||
break;
|
break;
|
||||||
@@ -927,10 +985,13 @@ retry:
|
|||||||
x += dx;
|
x += dx;
|
||||||
if (x > goal_x)
|
if (x > goal_x)
|
||||||
break;
|
break;
|
||||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
i += next - state->cursor;
|
||||||
|
state->cursor = next;
|
||||||
}
|
}
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
|
|
||||||
|
if (state->cursor == find.first_char + find.length)
|
||||||
|
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||||
state->has_preferred_x = 1;
|
state->has_preferred_x = 1;
|
||||||
state->preferred_x = goal_x;
|
state->preferred_x = goal_x;
|
||||||
|
|
||||||
@@ -980,8 +1041,9 @@ retry:
|
|||||||
state->cursor = find.prev_first;
|
state->cursor = find.prev_first;
|
||||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||||
x = row.x0;
|
x = row.x0;
|
||||||
for (i=0; i < row.num_chars; ++i) {
|
for (i=0; i < row.num_chars; ) {
|
||||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||||
|
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||||
break;
|
break;
|
||||||
@@ -989,10 +1051,15 @@ retry:
|
|||||||
x += dx;
|
x += dx;
|
||||||
if (x > goal_x)
|
if (x > goal_x)
|
||||||
break;
|
break;
|
||||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
i += next - state->cursor;
|
||||||
|
state->cursor = next;
|
||||||
}
|
}
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
|
|
||||||
|
if (state->cursor == find.first_char)
|
||||||
|
str->LastMoveDirectionLR = ImGuiDir_Right;
|
||||||
|
else if (state->cursor == find.prev_first)
|
||||||
|
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||||
state->has_preferred_x = 1;
|
state->has_preferred_x = 1;
|
||||||
state->preferred_x = goal_x;
|
state->preferred_x = goal_x;
|
||||||
|
|
||||||
@@ -1002,10 +1069,15 @@ retry:
|
|||||||
// go to previous line
|
// go to previous line
|
||||||
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
||||||
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
|
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
|
||||||
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
|
while (prev_scan > 0)
|
||||||
--prev_scan;
|
{
|
||||||
|
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
|
||||||
|
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||||
|
break;
|
||||||
|
prev_scan = prev;
|
||||||
|
}
|
||||||
find.first_char = find.prev_first;
|
find.first_char = find.prev_first;
|
||||||
find.prev_first = prev_scan;
|
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1079,10 +1151,7 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINESTART:
|
case STB_TEXTEDIT_K_LINESTART:
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_move_to_first(state);
|
stb_textedit_move_to_first(state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||||
state->cursor = 0;
|
|
||||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
--state->cursor;
|
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1090,13 +1159,9 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINEEND2:
|
case STB_TEXTEDIT_K_LINEEND2:
|
||||||
#endif
|
#endif
|
||||||
case STB_TEXTEDIT_K_LINEEND: {
|
case STB_TEXTEDIT_K_LINEEND: {
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_move_to_first(state);
|
stb_textedit_move_to_last(str, state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||||
state->cursor = n;
|
|
||||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
++state->cursor;
|
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1107,10 +1172,7 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
|
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_prep_selection_at_cursor(state);
|
stb_textedit_prep_selection_at_cursor(state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||||
state->cursor = 0;
|
|
||||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
--state->cursor;
|
|
||||||
state->select_end = state->cursor;
|
state->select_end = state->cursor;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
@@ -1119,13 +1181,9 @@ retry:
|
|||||||
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
|
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
|
||||||
#endif
|
#endif
|
||||||
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
|
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
|
||||||
stb_textedit_clamp(str, state);
|
stb_textedit_clamp(str, state);
|
||||||
stb_textedit_prep_selection_at_cursor(state);
|
stb_textedit_prep_selection_at_cursor(state);
|
||||||
if (state->single_line)
|
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||||
state->cursor = n;
|
|
||||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
|
||||||
++state->cursor;
|
|
||||||
state->select_end = state->cursor;
|
state->select_end = state->cursor;
|
||||||
state->has_preferred_x = 0;
|
state->has_preferred_x = 0;
|
||||||
break;
|
break;
|
||||||
@@ -1300,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||||||
// check type of recorded action:
|
// check type of recorded action:
|
||||||
if (u.insert_length) {
|
if (u.insert_length) {
|
||||||
// easy case: was a deletion, so we need to insert n characters
|
// easy case: was a deletion, so we need to insert n characters
|
||||||
STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
||||||
s->undo_char_point -= u.insert_length;
|
s->undo_char_point -= u.insert_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1351,7 +1409,7 @@ static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||||||
|
|
||||||
if (r.insert_length) {
|
if (r.insert_length) {
|
||||||
// easy case: need to insert n characters
|
// easy case: need to insert n characters
|
||||||
STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
||||||
s->redo_char_point += r.insert_length;
|
s->redo_char_point += r.insert_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -886,7 +886,7 @@ STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, fl
|
|||||||
// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
|
// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
|
||||||
|
|
||||||
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
|
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
|
||||||
// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
|
// the same as stbtt_GetCodepointBitmap, but you can specify a subpixel
|
||||||
// shift for the character
|
// shift for the character
|
||||||
|
|
||||||
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
|
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
|
||||||
@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
|
|||||||
q2[0] = (float)x2;
|
q2[0] = (float)x2;
|
||||||
q2[1] = (float)y2;
|
q2[1] = (float)y2;
|
||||||
if (equal(q0,q1) || equal(q1,q2)) {
|
if (equal(q0,q1) || equal(q1,q2)) {
|
||||||
x0 = (int)verts[i-1].x;
|
x0 = (int)verts[i-1].x; //-V1048
|
||||||
y0 = (int)verts[i-1].y;
|
y0 = (int)verts[i-1].y; //-V1048
|
||||||
x1 = (int)verts[i ].x;
|
x1 = (int)verts[i ].x;
|
||||||
y1 = (int)verts[i ].y;
|
y1 = (int)verts[i ].y;
|
||||||
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
||||||
|
|||||||
Vendored
+1
@@ -39,6 +39,7 @@
|
|||||||
#define ROBIN_HOOD_VERSION_PATCH 2 // for backwards-compatible bug fixes
|
#define ROBIN_HOOD_VERSION_PATCH 2 // for backwards-compatible bug fixes
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|||||||
@@ -38,11 +38,12 @@ std::vector<Analog> &games::bc::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Busou Shinki: Armored Princess Battle Conductor");
|
analogs = GameAPI::Analogs::getAnalogs("Busou Shinki: Armored Princess Battle Conductor");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Stick X",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Stick Y"
|
{ "Stick X", AnalogType::LinearCentered },
|
||||||
);
|
{ "Stick Y", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -16,15 +18,19 @@ namespace games::ccj {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct AIO_SCI_COMM {
|
struct AIO_SCI_COMM {
|
||||||
|
uint8_t data[0xf8];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB2 {
|
struct AIO_NMGR_IOB2 {
|
||||||
|
uint8_t data[0xe30];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_TBS {
|
struct AIO_IOB2_BI2X_TBS {
|
||||||
|
uint8_t data[0x4680];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_WRFIRM {
|
struct AIO_IOB2_BI2X_WRFIRM {
|
||||||
|
uint8_t data[0x20f48];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB__NODEINFO {
|
struct AIO_NMGR_IOB__NODEINFO {
|
||||||
@@ -72,6 +78,12 @@ namespace games::ccj {
|
|||||||
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// checked with UJK-001-2024060400
|
||||||
|
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_TBS) == 0x4680);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||||
|
|
||||||
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value);
|
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -621,4 +633,6 @@ namespace games::ccj {
|
|||||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
namespace games::ccj {
|
namespace games::ccj {
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -65,12 +65,20 @@ namespace games::ccj {
|
|||||||
libutils::load_library("libaio-iob2_video.dll");
|
libutils::load_library("libaio-iob2_video.dll");
|
||||||
libutils::load_library("win10actlog.dll");
|
libutils::load_library("win10actlog.dll");
|
||||||
|
|
||||||
|
// we do this to prevent this unexpectedly being unloaded
|
||||||
|
// which leads to D3D10Warp.dll_unloaded crash
|
||||||
|
libutils::load_library("d3d10warp.dll");
|
||||||
|
|
||||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||||
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
// insert BI2X hooks
|
// insert BI2X hooks
|
||||||
bi2x_hook_init();
|
bi2x_hook_init();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// insert trackball hooks
|
// insert trackball hooks
|
||||||
trackball_hook_init();
|
trackball_hook_init();
|
||||||
|
|
||||||
|
|||||||
@@ -45,13 +45,14 @@ std::vector<Analog> &games::ccj::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Chase Chase Jokers");
|
analogs = GameAPI::Analogs::getAnalogs("Chase Chase Jokers");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Joystick X",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Joystick Y",
|
{ "Joystick X", AnalogType::LinearCentered },
|
||||||
"Trackball DX",
|
{ "Joystick Y", AnalogType::LinearCentered },
|
||||||
"Trackball DY"
|
{ "Trackball DX", AnalogType::LinearCentered },
|
||||||
);
|
{ "Trackball DY", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ namespace games::ccj {
|
|||||||
static std::chrono::time_point<std::chrono::steady_clock> lastModified = std::chrono::steady_clock::now();
|
static std::chrono::time_point<std::chrono::steady_clock> lastModified = std::chrono::steady_clock::now();
|
||||||
static std::chrono::milliseconds debounceDuration(100);
|
static std::chrono::milliseconds debounceDuration(100);
|
||||||
auto currentTime = std::chrono::steady_clock::now();
|
auto currentTime = std::chrono::steady_clock::now();
|
||||||
bool pressed = get_async_secondary_mouse() & 0x8000;
|
bool pressed = get_async_secondary_mouse();
|
||||||
bool focused = GetForegroundWindow() == hWnd;
|
bool focused = GetForegroundWindow() == hWnd;
|
||||||
|
|
||||||
if (focused && MOUSE_TRACKBALL_USE_TOGGLE && pressed && (currentTime - lastModified > debounceDuration)) {
|
if (focused && MOUSE_TRACKBALL_USE_TOGGLE && pressed && (currentTime - lastModified > debounceDuration)) {
|
||||||
@@ -243,6 +244,7 @@ namespace games::ccj {
|
|||||||
log_info("trackball", "thread start, use mouse: {}, toggle: {}", MOUSE_TRACKBALL, MOUSE_TRACKBALL_USE_TOGGLE);
|
log_info("trackball", "thread start, use mouse: {}, toggle: {}", MOUSE_TRACKBALL, MOUSE_TRACKBALL_USE_TOGGLE);
|
||||||
|
|
||||||
tbThread = new std::thread([&] {
|
tbThread = new std::thread([&] {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
while (tbThreadRunning) {
|
while (tbThreadRunning) {
|
||||||
if (hWnd && wndProc) {
|
if (hWnd && wndProc) {
|
||||||
wndProc(hWnd, WM_INPUT, RIM_INPUT, (LPARAM)fakeHandle);
|
wndProc(hWnd, WM_INPUT, RIM_INPUT, (LPARAM)fakeHandle);
|
||||||
@@ -251,7 +253,7 @@ namespace games::ccj {
|
|||||||
if (!tbThreadRunning)
|
if (!tbThreadRunning)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
std::this_thread::sleep_for(10ms);
|
timer.sleep(10);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "hooks/setupapihook.h"
|
#include "hooks/setupapihook.h"
|
||||||
#include "hooks/sleephook.h"
|
#include "hooks/sleephook.h"
|
||||||
#include "hooks/input/dinput8/hook.h"
|
#include "hooks/input/dinput8/hook.h"
|
||||||
|
#include "util/deferlog.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
#include "p3io/usbmem.h"
|
#include "p3io/usbmem.h"
|
||||||
|
|
||||||
#include "p4io/p4io.h"
|
#include "p4io/p4io.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
using namespace acioemu;
|
using namespace acioemu;
|
||||||
|
|
||||||
@@ -52,9 +54,9 @@ namespace games::ddr {
|
|||||||
return SendMessage_real(hWnd, Msg, wParam, lParam);
|
return SendMessage_real(hWnd, Msg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains_only_ascii(const std::string& str) {
|
bool contains_only_ascii(const std::wstring& str) {
|
||||||
for (auto c: str) {
|
for (auto c: str) {
|
||||||
if (static_cast<unsigned char>(c) > 127) {
|
if (c > 127) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,24 +81,24 @@ namespace games::ddr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fileutils::dir_exists(dir)) {
|
if (fileutils::dir_exists(dir)) {
|
||||||
log_info("ddr", "looking for codecs in this directory: {}", dir.string());
|
log_info("ddr", "looking for codecs in this directory: {}", dir);
|
||||||
} else {
|
} else {
|
||||||
log_info("ddr", "codecs directory not found: {}", dir.string());
|
log_info("ddr", "codecs directory not found: {}", dir);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &file : std::filesystem::directory_iterator(dir)) {
|
for (const auto &file : std::filesystem::directory_iterator(dir)) {
|
||||||
const auto &filename = file.path().filename();
|
const auto &filename = file.path().filename();
|
||||||
const auto extension = strtolower(filename.extension().string());
|
const auto extension = filename.extension().wstring();
|
||||||
|
|
||||||
if (extension != ".dll") {
|
if (wcsicmp(extension.c_str(), L".dll") != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("ddr", "found DLL: {}, size: {} bytes", filename.string(), file.file_size());
|
log_info("ddr", "found DLL: {}, size: {} bytes", filename, file.file_size());
|
||||||
if (filename == "k-clvsd.dll" || filename.string().find("xactengine") == 0) {
|
if (filename == "k-clvsd.dll" || filename.wstring().find(L"xactengine") == 0) {
|
||||||
const std::wstring wcmd = L"regsvr32.exe /s \"" + file.path().wstring() + L"\"";
|
const std::wstring wcmd = L"regsvr32.exe /s \"" + file.path().wstring() + L"\"";
|
||||||
const std::string cmd = "regsvr32.exe /s \"" + file.path().string() + "\"";
|
const std::string cmd = fmt::format("regsvr32.exe /s \"{}\"", file.path());
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
std::thread t([wcmd, &result]() {
|
std::thread t([wcmd, &result]() {
|
||||||
@@ -108,18 +110,32 @@ namespace games::ddr {
|
|||||||
log_info("ddr", "`{}` returned {}", cmd, result);
|
log_info("ddr", "`{}` returned {}", cmd, result);
|
||||||
} else {
|
} else {
|
||||||
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"DDR codec registration failure",
|
||||||
|
fmt::format(" {} failed to register with error {}", filename, result)});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contains_only_ascii(file.path().string())) {
|
static std::once_flag printed;
|
||||||
log_warning(
|
std::call_once(printed, [file]() {
|
||||||
"ddr",
|
if (!contains_only_ascii(file.path().wstring())) {
|
||||||
"BAD PATH ERROR\n\n\n"
|
log_warning(
|
||||||
"!!! !!!\n"
|
"ddr",
|
||||||
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
|
"BAD PATH ERROR\n\n"
|
||||||
"!!! this may cause the game to crash! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
|
||||||
);
|
"!!! this may cause failures when loading codecs, and !!!\n"
|
||||||
}
|
"!!! potentially leading the game to crash! !!!\n"
|
||||||
|
"!!! !!!\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"path to DDR codecs contain non-Latin characters",
|
||||||
|
" this may lead to movies not playing, white screen issue,",
|
||||||
|
" game hanging, or potentially a crash",
|
||||||
|
" move the game to a directory that does not contain non-Latin characters",
|
||||||
|
" if you encountered any issues"});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,4 +291,50 @@ namespace games::ddr {
|
|||||||
// dispose device hook
|
// dispose device hook
|
||||||
devicehook_dispose();
|
devicehook_dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_analog_xy_axis(Analog &analog, bool &less, bool &more) {
|
||||||
|
if (!analog.isSet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto value = GameAPI::Analogs::getState(RI_MGR, analog);
|
||||||
|
|
||||||
|
// stepmania linux source:
|
||||||
|
// https://github.com/stepmania/stepmania/blob/d55acb1ba26f1c5b5e3048d6d6c0bd116625216f/src/arch/InputHandler/InputHandler_Linux_Event.cpp#L410
|
||||||
|
|
||||||
|
// for example, value cap with range [0, 255]:
|
||||||
|
// 127 = 0.498, neutral
|
||||||
|
// 128 = 0.502, both
|
||||||
|
// apparently some adapters report values like above; this obviously makes a lot of
|
||||||
|
// assumptions about bit width of the HID value cap and how the adapter reports
|
||||||
|
// neutral/both values, but this is good enough for parity with stepmania and its many forks
|
||||||
|
if (0.5001f < value && value < 0.75f) {
|
||||||
|
less |= true;
|
||||||
|
more |= true;
|
||||||
|
|
||||||
|
} else if (value <= 0.25f) {
|
||||||
|
less |= true;
|
||||||
|
} else if (value >= 0.75f) {
|
||||||
|
more |= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_analog_x_axis(int player, bool &left, bool &right) {
|
||||||
|
if (player != 1 && player != 2) {
|
||||||
|
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_LEFT_RIGHT : Analogs::P2_LEFT_RIGHT);
|
||||||
|
get_analog_xy_axis(analog, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_analog_y_axis(int player, bool &up, bool &down) {
|
||||||
|
if (player != 1 && player != 2) {
|
||||||
|
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_UP_DOWN : Analogs::P2_UP_DOWN);
|
||||||
|
get_analog_xy_axis(analog, up, down);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,7 @@ namespace games::ddr {
|
|||||||
private:
|
private:
|
||||||
void register_codecs();
|
void register_codecs();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void get_analog_x_axis(int player, bool &left, bool &right);
|
||||||
|
void get_analog_y_axis(int player, bool &up, bool &down);
|
||||||
}
|
}
|
||||||
|
|||||||
+158
-110
@@ -35,142 +35,190 @@ std::vector<Button> &games::ddr::get_buttons() {
|
|||||||
return analogs;
|
return analogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string games::ddr::get_buttons_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return
|
||||||
|
"For DDR pad arrows, double check that simultaneous Left+Right and Up+Down can be detected.\n"
|
||||||
|
"You should boot the game, enter test menu, and use Foot Panel Check.\n\n"
|
||||||
|
"When mapping arrows, try the following in order:\n\n"
|
||||||
|
" 1. If your controller uses face buttons (A/B/X/Y), bind them here.\n"
|
||||||
|
" 2. If your controller supports XInput, use that.\n"
|
||||||
|
" 3. If your controller uses analog axis for arrows, try Analogs tab.\n"
|
||||||
|
" 4. Otherwise, you will need to use remapping software or get a better adapter."
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Analog> &games::ddr::get_analogs() {
|
||||||
|
static std::vector<Analog> analogs;
|
||||||
|
|
||||||
|
if (analogs.empty()) {
|
||||||
|
analogs = GameAPI::Analogs::getAnalogs("Dance Dance Revolution");
|
||||||
|
|
||||||
|
using namespace GameAPI::Analogs;
|
||||||
|
|
||||||
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
|
{ "P1 Left-Right (Axis Fix)", AnalogType::LinearCentered },
|
||||||
|
{ "P1 Up-Down (Axis Fix)", AnalogType::LinearCentered },
|
||||||
|
{ "P2 Left-Right (Axis Fix)", AnalogType::LinearCentered },
|
||||||
|
{ "P2 Up-Down (Axis Fix)", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return analogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string games::ddr::get_analogs_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return
|
||||||
|
"Only use this if your DDR pad outputs analog axis for arrows.\n\n"
|
||||||
|
"If the pad uses face buttons (A/B/X/Y), use Buttons tab instead.\n\n"
|
||||||
|
"Spice will treat values <=25% as Left, >=75% as Right, ~=50% as neutral,\n"
|
||||||
|
"and value between 50% and 75% as both arrows.\n\n"
|
||||||
|
"This is the classic Stepmania \"Axis Fix\" which may or may not work with\n"
|
||||||
|
"your dance pad or your adapter."
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Light> &games::ddr::get_lights() {
|
std::vector<Light> &games::ddr::get_lights() {
|
||||||
static std::vector<Light> lights;
|
static std::vector<Light> lights;
|
||||||
|
|
||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"P1 Foot Left",
|
{
|
||||||
"P1 Foot Up",
|
{"General", "P1 Foot Left"},
|
||||||
"P1 Foot Right",
|
{"General", "P1 Foot Up"},
|
||||||
"P1 Foot Down",
|
{"General", "P1 Foot Right"},
|
||||||
"P2 Foot Left",
|
{"General", "P1 Foot Down"},
|
||||||
"P2 Foot Up",
|
{"General", "P2 Foot Left"},
|
||||||
"P2 Foot Right",
|
{"General", "P2 Foot Up"},
|
||||||
"P2 Foot Down",
|
{"General", "P2 Foot Right"},
|
||||||
"Spot Red",
|
{"General", "P2 Foot Down"},
|
||||||
"Spot Blue",
|
|
||||||
"Top Spot Red",
|
|
||||||
"Top Spot Blue",
|
|
||||||
"P1 Halogen Upper",
|
|
||||||
"P1 Halogen Lower",
|
|
||||||
"P2 Halogen Upper",
|
|
||||||
"P2 Halogen Lower",
|
|
||||||
"P1 Button",
|
|
||||||
"P2 Button",
|
|
||||||
"Neon",
|
|
||||||
"HD P1 Start",
|
|
||||||
"HD P1 Menu Left-Right",
|
|
||||||
"HD P1 Menu Up-Down",
|
|
||||||
"HD P2 Start",
|
|
||||||
"HD P2 Menu Left-Right",
|
|
||||||
"HD P2 Menu Up-Down",
|
|
||||||
"HD P1 Speaker F R",
|
|
||||||
"HD P1 Speaker F G",
|
|
||||||
"HD P1 Speaker F B",
|
|
||||||
"HD P1 Speaker W R",
|
|
||||||
"HD P1 Speaker W G",
|
|
||||||
"HD P1 Speaker W B",
|
|
||||||
"HD P2 Speaker F R",
|
|
||||||
"HD P2 Speaker F G",
|
|
||||||
"HD P2 Speaker F B",
|
|
||||||
"HD P2 Speaker W R",
|
|
||||||
"HD P2 Speaker W G",
|
|
||||||
"HD P2 Speaker W B",
|
|
||||||
|
|
||||||
"WHITE Speaker Top R",
|
{"SD", "Spot Red"},
|
||||||
"WHITE Speaker Top G",
|
{"SD", "Spot Blue"},
|
||||||
"WHITE Speaker Top B",
|
{"SD", "Top Spot Red"},
|
||||||
"WHITE Speaker Bottom R",
|
{"SD", "Top Spot Blue"},
|
||||||
"WHITE Speaker Bottom G",
|
{"SD", "P1 Halogen Upper"},
|
||||||
"WHITE Speaker Bottom B",
|
{"SD", "P1 Halogen Lower"},
|
||||||
"WHITE Woofer R",
|
{"SD", "P2 Halogen Upper"},
|
||||||
"WHITE Woofer G",
|
{"SD", "P2 Halogen Lower"},
|
||||||
"WHITE Woofer B",
|
{"SD", "P1 Button"},
|
||||||
|
{"SD", "P2 Button"},
|
||||||
|
{"SD", "Neon"},
|
||||||
|
|
||||||
"GOLD P1 Menu Start",
|
{"HD", "HD P1 Start"},
|
||||||
"GOLD P1 Menu Up",
|
{"HD", "HD P1 Menu Left-Right"},
|
||||||
"GOLD P1 Menu Down",
|
{"HD", "HD P1 Menu Up-Down"},
|
||||||
"GOLD P1 Menu Left",
|
{"HD", "HD P2 Start"},
|
||||||
"GOLD P1 Menu Right",
|
{"HD", "HD P2 Menu Left-Right"},
|
||||||
|
{"HD", "HD P2 Menu Up-Down"},
|
||||||
|
{"HD", "HD P1 Speaker F R"},
|
||||||
|
{"HD", "HD P1 Speaker F G"},
|
||||||
|
{"HD", "HD P1 Speaker F B"},
|
||||||
|
{"HD", "HD P1 Speaker W R"},
|
||||||
|
{"HD", "HD P1 Speaker W G"},
|
||||||
|
{"HD", "HD P1 Speaker W B"},
|
||||||
|
{"HD", "HD P2 Speaker F R"},
|
||||||
|
{"HD", "HD P2 Speaker F G"},
|
||||||
|
{"HD", "HD P2 Speaker F B"},
|
||||||
|
{"HD", "HD P2 Speaker W R"},
|
||||||
|
{"HD", "HD P2 Speaker W G"},
|
||||||
|
{"HD", "HD P2 Speaker W B"},
|
||||||
|
|
||||||
"GOLD P2 Menu Start",
|
{"WHITE", "WHITE Speaker Top R"},
|
||||||
"GOLD P2 Menu Up",
|
{"WHITE", "WHITE Speaker Top G"},
|
||||||
"GOLD P2 Menu Down",
|
{"WHITE", "WHITE Speaker Top B"},
|
||||||
"GOLD P2 Menu Left",
|
{"WHITE", "WHITE Speaker Bottom R"},
|
||||||
"GOLD P2 Menu Right",
|
{"WHITE", "WHITE Speaker Bottom G"},
|
||||||
|
{"WHITE", "WHITE Speaker Bottom B"},
|
||||||
|
{"WHITE", "WHITE Woofer R"},
|
||||||
|
{"WHITE", "WHITE Woofer G"},
|
||||||
|
{"WHITE", "WHITE Woofer B"},
|
||||||
|
|
||||||
"GOLD Title Panel Left",
|
{"GOLD", "GOLD P1 Menu Start"},
|
||||||
"GOLD Title Panel Center",
|
{"GOLD", "GOLD P1 Menu Up"},
|
||||||
"GOLD Title Panel Right",
|
{"GOLD", "GOLD P1 Menu Down"},
|
||||||
|
{"GOLD", "GOLD P1 Menu Left"},
|
||||||
|
{"GOLD", "GOLD P1 Menu Right"},
|
||||||
|
|
||||||
"GOLD P1 Woofer Corner",
|
{"GOLD", "GOLD P2 Menu Start"},
|
||||||
"GOLD P2 Woofer Corner",
|
{"GOLD", "GOLD P2 Menu Up"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Down"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Left"},
|
||||||
|
{"GOLD", "GOLD P2 Menu Right"},
|
||||||
|
|
||||||
"GOLD P1 Card Unit R",
|
{"GOLD", "GOLD Title Panel Left"},
|
||||||
"GOLD P1 Card Unit G",
|
{"GOLD", "GOLD Title Panel Center"},
|
||||||
"GOLD P1 Card Unit B",
|
{"GOLD", "GOLD Title Panel Right"},
|
||||||
|
|
||||||
"GOLD P2 Card Unit R",
|
{"GOLD", "GOLD P1 Woofer Corner"},
|
||||||
"GOLD P2 Card Unit G",
|
{"GOLD", "GOLD P2 Woofer Corner"},
|
||||||
"GOLD P2 Card Unit B",
|
|
||||||
|
|
||||||
"GOLD Top Panel Avg R",
|
{"GOLD", "GOLD P1 Card Unit R"},
|
||||||
"GOLD Top Panel Avg G",
|
{"GOLD", "GOLD P1 Card Unit G"},
|
||||||
"GOLD Top Panel Avg B",
|
{"GOLD", "GOLD P1 Card Unit B"},
|
||||||
|
|
||||||
"GOLD Monitor Side Left Avg R",
|
{"GOLD", "GOLD P2 Card Unit R"},
|
||||||
"GOLD Monitor Side Left Avg G",
|
{"GOLD", "GOLD P2 Card Unit G"},
|
||||||
"GOLD Monitor Side Left Avg B",
|
{"GOLD", "GOLD P2 Card Unit B"},
|
||||||
|
|
||||||
"GOLD Monitor Side Right Avg R",
|
{"GOLD", "GOLD Top Panel Avg R"},
|
||||||
"GOLD Monitor Side Right Avg G",
|
{"GOLD", "GOLD Top Panel Avg G"},
|
||||||
"GOLD Monitor Side Right Avg B",
|
{"GOLD", "GOLD Top Panel Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Up Avg R",
|
{"GOLD", "GOLD Monitor Side Left Avg R"},
|
||||||
"GOLD P1 Foot Up Avg G",
|
{"GOLD", "GOLD Monitor Side Left Avg G"},
|
||||||
"GOLD P1 Foot Up Avg B",
|
{"GOLD", "GOLD Monitor Side Left Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Down Avg R",
|
{"GOLD", "GOLD Monitor Side Right Avg R"},
|
||||||
"GOLD P1 Foot Down Avg G",
|
{"GOLD", "GOLD Monitor Side Right Avg G"},
|
||||||
"GOLD P1 Foot Down Avg B",
|
{"GOLD", "GOLD Monitor Side Right Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Left Avg R",
|
{"GOLD", "GOLD P1 Foot Up Avg R"},
|
||||||
"GOLD P1 Foot Left Avg G",
|
{"GOLD", "GOLD P1 Foot Up Avg G"},
|
||||||
"GOLD P1 Foot Left Avg B",
|
{"GOLD", "GOLD P1 Foot Up Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Foot Right Avg R",
|
{"GOLD", "GOLD P1 Foot Down Avg R"},
|
||||||
"GOLD P1 Foot Right Avg G",
|
{"GOLD", "GOLD P1 Foot Down Avg G"},
|
||||||
"GOLD P1 Foot Right Avg B",
|
{"GOLD", "GOLD P1 Foot Down Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Up Avg R",
|
{"GOLD", "GOLD P1 Foot Left Avg R"},
|
||||||
"GOLD P2 Foot Up Avg G",
|
{"GOLD", "GOLD P1 Foot Left Avg G"},
|
||||||
"GOLD P2 Foot Up Avg B",
|
{"GOLD", "GOLD P1 Foot Left Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Down Avg R",
|
{"GOLD", "GOLD P1 Foot Right Avg R"},
|
||||||
"GOLD P2 Foot Down Avg G",
|
{"GOLD", "GOLD P1 Foot Right Avg G"},
|
||||||
"GOLD P2 Foot Down Avg B",
|
{"GOLD", "GOLD P1 Foot Right Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Left Avg R",
|
{"GOLD", "GOLD P2 Foot Up Avg R"},
|
||||||
"GOLD P2 Foot Left Avg G",
|
{"GOLD", "GOLD P2 Foot Up Avg G"},
|
||||||
"GOLD P2 Foot Left Avg B",
|
{"GOLD", "GOLD P2 Foot Up Avg B"},
|
||||||
|
|
||||||
"GOLD P2 Foot Right Avg R",
|
{"GOLD", "GOLD P2 Foot Down Avg R"},
|
||||||
"GOLD P2 Foot Right Avg G",
|
{"GOLD", "GOLD P2 Foot Down Avg G"},
|
||||||
"GOLD P2 Foot Right Avg B",
|
{"GOLD", "GOLD P2 Foot Down Avg B"},
|
||||||
|
|
||||||
"GOLD P1 Stage Corner Up-Left",
|
{"GOLD", "GOLD P2 Foot Left Avg R"},
|
||||||
"GOLD P1 Stage Corner Up-Right",
|
{"GOLD", "GOLD P2 Foot Left Avg G"},
|
||||||
"GOLD P1 Stage Corner Down-Left",
|
{"GOLD", "GOLD P2 Foot Left Avg B"},
|
||||||
"GOLD P1 Stage Corner Down-Right",
|
|
||||||
|
|
||||||
"GOLD P2 Stage Corner Up-Left",
|
{"GOLD", "GOLD P2 Foot Right Avg R"},
|
||||||
"GOLD P2 Stage Corner Up-Right",
|
{"GOLD", "GOLD P2 Foot Right Avg G"},
|
||||||
"GOLD P2 Stage Corner Down-Left",
|
{"GOLD", "GOLD P2 Foot Right Avg B"},
|
||||||
"GOLD P2 Stage Corner Down-Right"
|
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Up-Left"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Up-Right"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Down-Left"},
|
||||||
|
{"GOLD", "GOLD P1 Stage Corner Down-Right"},
|
||||||
|
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Up-Left"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Up-Right"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Down-Left"},
|
||||||
|
{"GOLD", "GOLD P2 Stage Corner Down-Right"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ namespace games::ddr {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all analogs in correct order
|
||||||
|
namespace Analogs {
|
||||||
|
enum {
|
||||||
|
P1_LEFT_RIGHT,
|
||||||
|
P1_UP_DOWN,
|
||||||
|
P2_LEFT_RIGHT,
|
||||||
|
P2_UP_DOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// all lights in correct order
|
// all lights in correct order
|
||||||
namespace Lights {
|
namespace Lights {
|
||||||
enum {
|
enum {
|
||||||
@@ -177,5 +187,8 @@ namespace games::ddr {
|
|||||||
|
|
||||||
// getters
|
// getters
|
||||||
std::vector<Button> &get_buttons();
|
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();
|
std::vector<Light> &get_lights();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "cfg/api.h"
|
#include "cfg/api.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "../ddr.h"
|
#include "../ddr.h"
|
||||||
@@ -282,7 +283,7 @@ int games::ddr::DDRP3IOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use both sat spots for a neon pulse
|
// use both sat spots for a neon pulse
|
||||||
float value_neon = (light_bits & hd_mapping_bits[0] && hd_mapping_bits[1]) ? 1.f : 0.f;
|
float value_neon = (light_bits & hd_mapping_bits[0] && light_bits & hd_mapping_bits[1]) ? 1.f : 0.f;
|
||||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +460,8 @@ int games::ddr::DDRP3IOHandle::device_io(
|
|||||||
if (nOutBufferSize >= 4) {
|
if (nOutBufferSize >= 4) {
|
||||||
|
|
||||||
// cool down
|
// cool down
|
||||||
Sleep(1);
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(1);
|
||||||
|
|
||||||
// get controls as single variable (4 bytes)
|
// get controls as single variable (4 bytes)
|
||||||
auto &controls = *(uint32_t*) lpOutBuffer;
|
auto &controls = *(uint32_t*) lpOutBuffer;
|
||||||
@@ -498,32 +500,80 @@ int games::ddr::DDRP3IOHandle::device_io(
|
|||||||
|
|
||||||
// shift table
|
// shift table
|
||||||
static size_t shift_table[] = {
|
static size_t shift_table[] = {
|
||||||
30, 28, 29, 8, 9, 10, 11, 12, 24, 25, 14, 15, 16, 17, 18, 19, 20, 26, 27, 22, 23
|
30, 28, 29, // service, test, coin
|
||||||
|
8, // p1 start
|
||||||
|
9, 10, 11, 12, // p1 panel
|
||||||
|
24, 25, 14, 15, // p1 menu
|
||||||
|
16, // p2 start
|
||||||
|
17, 18, 19, 20, // p2 panel
|
||||||
|
26, 27, 22, 23 // p2 menu
|
||||||
};
|
};
|
||||||
static size_t button_table[] = {
|
static size_t button_table[] = {
|
||||||
Buttons::SERVICE,
|
Buttons::SERVICE,
|
||||||
Buttons::TEST,
|
Buttons::TEST,
|
||||||
Buttons::COIN_MECH,
|
Buttons::COIN_MECH,
|
||||||
|
|
||||||
Buttons::P1_START,
|
Buttons::P1_START,
|
||||||
|
|
||||||
Buttons::P1_PANEL_UP,
|
Buttons::P1_PANEL_UP,
|
||||||
Buttons::P1_PANEL_DOWN,
|
Buttons::P1_PANEL_DOWN,
|
||||||
Buttons::P1_PANEL_LEFT,
|
Buttons::P1_PANEL_LEFT,
|
||||||
Buttons::P1_PANEL_RIGHT,
|
Buttons::P1_PANEL_RIGHT,
|
||||||
|
|
||||||
Buttons::P1_MENU_UP,
|
Buttons::P1_MENU_UP,
|
||||||
Buttons::P1_MENU_DOWN,
|
Buttons::P1_MENU_DOWN,
|
||||||
Buttons::P1_MENU_LEFT,
|
Buttons::P1_MENU_LEFT,
|
||||||
Buttons::P1_MENU_RIGHT,
|
Buttons::P1_MENU_RIGHT,
|
||||||
|
|
||||||
Buttons::P2_START,
|
Buttons::P2_START,
|
||||||
|
|
||||||
Buttons::P2_PANEL_UP,
|
Buttons::P2_PANEL_UP,
|
||||||
Buttons::P2_PANEL_DOWN,
|
Buttons::P2_PANEL_DOWN,
|
||||||
Buttons::P2_PANEL_LEFT,
|
Buttons::P2_PANEL_LEFT,
|
||||||
Buttons::P2_PANEL_RIGHT,
|
Buttons::P2_PANEL_RIGHT,
|
||||||
|
|
||||||
Buttons::P2_MENU_UP,
|
Buttons::P2_MENU_UP,
|
||||||
Buttons::P2_MENU_DOWN,
|
Buttons::P2_MENU_DOWN,
|
||||||
Buttons::P2_MENU_LEFT,
|
Buttons::P2_MENU_LEFT,
|
||||||
Buttons::P2_MENU_RIGHT,
|
Buttons::P2_MENU_RIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// get analogs
|
||||||
|
struct {
|
||||||
|
bool up;
|
||||||
|
bool down;
|
||||||
|
bool left;
|
||||||
|
bool right;
|
||||||
|
} analog_values[2] = {};
|
||||||
|
games::ddr::get_analog_x_axis(1, analog_values[0].left, analog_values[0].right);
|
||||||
|
games::ddr::get_analog_y_axis(1, analog_values[0].up, analog_values[0].down);
|
||||||
|
games::ddr::get_analog_x_axis(2, analog_values[1].left, analog_values[1].right);
|
||||||
|
games::ddr::get_analog_y_axis(2, analog_values[1].up, analog_values[1].down);
|
||||||
|
if (analog_values[0].up) {
|
||||||
|
controls |= 1 << 9;
|
||||||
|
}
|
||||||
|
if (analog_values[0].down) {
|
||||||
|
controls |= 1 << 10;
|
||||||
|
}
|
||||||
|
if (analog_values[0].left) {
|
||||||
|
controls |= 1 << 11;
|
||||||
|
}
|
||||||
|
if (analog_values[0].right) {
|
||||||
|
controls |= 1 << 12;
|
||||||
|
}
|
||||||
|
if (analog_values[1].up) {
|
||||||
|
controls |= 1 << 17;
|
||||||
|
}
|
||||||
|
if (analog_values[1].down) {
|
||||||
|
controls |= 1 << 18;
|
||||||
|
}
|
||||||
|
if (analog_values[1].left) {
|
||||||
|
controls |= 1 << 19;
|
||||||
|
}
|
||||||
|
if (analog_values[1].right) {
|
||||||
|
controls |= 1 << 20;
|
||||||
|
}
|
||||||
|
|
||||||
// update states
|
// update states
|
||||||
auto &buttons = get_buttons();
|
auto &buttons = get_buttons();
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "../io.h"
|
#include "../io.h"
|
||||||
@@ -253,28 +254,26 @@ namespace games::ddr {
|
|||||||
if(((dwIoControlCode>>16)&0xFFFF) == FILE_DEVICE_UNKNOWN) {
|
if(((dwIoControlCode>>16)&0xFFFF) == FILE_DEVICE_UNKNOWN) {
|
||||||
switch((dwIoControlCode>>2)&0x3FFF) {
|
switch((dwIoControlCode>>2)&0x3FFF) {
|
||||||
case P4IO_IOCTL_GET_INPUTS: {
|
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
|
||||||
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(4);
|
||||||
|
|
||||||
memset(lpOutBuffer, 0, 16);
|
memset(lpOutBuffer, 0, 16);
|
||||||
auto controls = (uint32_t*) lpOutBuffer;
|
auto controls = (uint32_t*) lpOutBuffer;
|
||||||
|
|
||||||
const std::pair<size_t, uint8_t> buttonMapping[] =
|
const std::pair<size_t, uint8_t> buttonMapping[] =
|
||||||
{
|
{
|
||||||
|
// Panel input comes from MDXF Control Status Buffer functions
|
||||||
{Buttons::SERVICE, 25}, //Good
|
{Buttons::SERVICE, 25}, //Good
|
||||||
{Buttons::TEST, 28}, //Good
|
{Buttons::TEST, 28}, //Good
|
||||||
{Buttons::COIN_MECH, 24}, //Good
|
{Buttons::COIN_MECH, 24}, //Good
|
||||||
{Buttons::P1_START, 0}, //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_UP, 1}, //Good
|
||||||
{Buttons::P1_MENU_DOWN, 2}, //Good
|
{Buttons::P1_MENU_DOWN, 2}, //Good
|
||||||
{Buttons::P1_MENU_LEFT, 3}, //Good
|
{Buttons::P1_MENU_LEFT, 3}, //Good
|
||||||
{Buttons::P1_MENU_RIGHT, 4}, //Good
|
{Buttons::P1_MENU_RIGHT, 4}, //Good
|
||||||
{Buttons::P2_START, 8}, //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_UP, 9}, //Good
|
||||||
{Buttons::P2_MENU_DOWN, 10}, //Good
|
{Buttons::P2_MENU_DOWN, 10}, //Good
|
||||||
{Buttons::P2_MENU_LEFT, 11}, //Good
|
{Buttons::P2_MENU_LEFT, 11}, //Good
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/memutils.h"
|
#include "util/memutils.h"
|
||||||
|
#include "rgb_cam.h"
|
||||||
|
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
|
|
||||||
@@ -150,6 +152,7 @@ namespace games::drs {
|
|||||||
char DRS_TAPELED[DRS_TAPELED_COLS_TOTAL][3] {};
|
char DRS_TAPELED[DRS_TAPELED_COLS_TOTAL][3] {};
|
||||||
bool DISABLE_TOUCH = false;
|
bool DISABLE_TOUCH = false;
|
||||||
bool TRANSPOSE_TOUCH = false;
|
bool TRANSPOSE_TOUCH = false;
|
||||||
|
bool RGB_CAMERA_HOOK = false;
|
||||||
|
|
||||||
std::vector<TouchEvent> TOUCH_EVENTS;
|
std::vector<TouchEvent> TOUCH_EVENTS;
|
||||||
|
|
||||||
@@ -219,10 +222,11 @@ namespace games::drs {
|
|||||||
void start_touch() {
|
void start_touch() {
|
||||||
std::thread t([] {
|
std::thread t([] {
|
||||||
log_info("drs", "starting touch input thread");
|
log_info("drs", "starting touch input thread");
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
timer.sleep(1);
|
||||||
|
|
||||||
TOUCH_EVENTS.clear();
|
TOUCH_EVENTS.clear();
|
||||||
touch_get_events(TOUCH_EVENTS);
|
touch_get_events(TOUCH_EVENTS);
|
||||||
@@ -313,6 +317,10 @@ namespace games::drs {
|
|||||||
} else {
|
} else {
|
||||||
log_info("drs", "no native input method detected");
|
log_info("drs", "no native input method detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RGB_CAMERA_HOOK) {
|
||||||
|
init_rgb_camera_hook();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRSGame::detach() {
|
void DRSGame::detach() {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace games::drs {
|
|||||||
extern std::vector<TouchEvent> TOUCH_EVENTS;
|
extern std::vector<TouchEvent> TOUCH_EVENTS;
|
||||||
extern bool DISABLE_TOUCH;
|
extern bool DISABLE_TOUCH;
|
||||||
extern bool TRANSPOSE_TOUCH;
|
extern bool TRANSPOSE_TOUCH;
|
||||||
|
extern bool RGB_CAMERA_HOOK;
|
||||||
|
|
||||||
void fire_touches(drs_touch_t *events, size_t event_count);
|
void fire_touches(drs_touch_t *events, size_t event_count);
|
||||||
void start_touch();
|
void start_touch();
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
#include "games/drs/rgb_cam.h"
|
||||||
|
|
||||||
|
// set version to Windows 7 to enable Media Foundation functions
|
||||||
|
#ifdef _WIN32_WINNT
|
||||||
|
#undef _WIN32_WINNT
|
||||||
|
#endif
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
|
// turn on C-style COM objects
|
||||||
|
#define CINTERFACE
|
||||||
|
|
||||||
|
#include <mfobjects.h>
|
||||||
|
#include <mfidl.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
|
#include "cfg/configurator.h"
|
||||||
|
#include "hooks/cfgmgr32hook.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/memutils.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
|
||||||
|
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
|
||||||
|
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
|
||||||
|
|
||||||
|
namespace games::drs {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Camera related stuff
|
||||||
|
*/
|
||||||
|
static std::wstring CAMERA0_ID;
|
||||||
|
|
||||||
|
static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue,
|
||||||
|
UINT32 *pcchLength)
|
||||||
|
{
|
||||||
|
// call the original
|
||||||
|
HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength);
|
||||||
|
|
||||||
|
// log the cam name
|
||||||
|
log_misc("drs::rgbcam", "obtained {}", ws2s(*ppwszValue));
|
||||||
|
|
||||||
|
// try first camera
|
||||||
|
wchar_t *pwc = nullptr;
|
||||||
|
if (CAMERA0_ID.length() == 23) {
|
||||||
|
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if camera could be identified
|
||||||
|
if (pwc) {
|
||||||
|
|
||||||
|
// fake the USB IDs
|
||||||
|
pwc[4] = L'2';
|
||||||
|
pwc[5] = L'8';
|
||||||
|
pwc[6] = L'8';
|
||||||
|
pwc[7] = L'c';
|
||||||
|
|
||||||
|
pwc[13] = L'0';
|
||||||
|
pwc[14] = L'0';
|
||||||
|
pwc[15] = L'0';
|
||||||
|
pwc[16] = L'2';
|
||||||
|
|
||||||
|
pwc[21] = L'0';
|
||||||
|
pwc[22] = L'0';
|
||||||
|
|
||||||
|
log_misc("drs::rgbcam", "replaced {}", ws2s(*ppwszValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return original result
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hook_camera(std::wstring camera_id, std::string camera_instance) {
|
||||||
|
// don't hook if camera 0 is already hooked
|
||||||
|
if (CAMERA0_ID.length() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the camera ID
|
||||||
|
CAMERA0_ID = camera_id;
|
||||||
|
|
||||||
|
// cfgmgr hook
|
||||||
|
CFGMGR32_HOOK_SETTING camera_setting;
|
||||||
|
camera_setting.device_instance = 0xDEADBEEF;
|
||||||
|
camera_setting.parent_instance = ~camera_setting.device_instance;
|
||||||
|
camera_setting.device_id = "USB\\VEN_8086&DEV_8C2D";
|
||||||
|
camera_setting.device_node_id = "USB\\VID_288C&PID_0002&MI_00\\?&????????&?&????";
|
||||||
|
if (camera_instance.length() == 17) {
|
||||||
|
for (int i = 0; i < 17; i++) {
|
||||||
|
camera_setting.device_node_id[28 + i] = camera_instance[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfgmgr32hook_add(camera_setting);
|
||||||
|
|
||||||
|
log_info("drs::rgbcam", "hooked camera @ {}", ws2s(camera_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hook_camera_vtable(IMFActivate *camera) {
|
||||||
|
|
||||||
|
// hook allocated string method for camera identification
|
||||||
|
memutils::VProtectGuard camera_guard(camera->lpVtbl);
|
||||||
|
camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI MFEnumDeviceSources_hook(IMFAttributes *pAttributes, IMFActivate ***pppSourceActivate,
|
||||||
|
UINT32 *pcSourceActivate) {
|
||||||
|
|
||||||
|
// call original function
|
||||||
|
HRESULT result_orig = MFEnumDeviceSources_orig(pAttributes, pppSourceActivate, pcSourceActivate);
|
||||||
|
|
||||||
|
// check for capture devices
|
||||||
|
if (FAILED(result_orig) || !*pcSourceActivate) {
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get camera
|
||||||
|
IMFActivate *camera = **pppSourceActivate;
|
||||||
|
|
||||||
|
// save original method for later use
|
||||||
|
if (GetAllocatedString_orig == nullptr) {
|
||||||
|
GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hook allocated string method for camera identification
|
||||||
|
hook_camera_vtable(camera);
|
||||||
|
|
||||||
|
// get camera link
|
||||||
|
LPWSTR camera_link_lpwstr;
|
||||||
|
UINT32 camera_link_length;
|
||||||
|
if (SUCCEEDED(GetAllocatedString_orig(
|
||||||
|
camera,
|
||||||
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
||||||
|
&camera_link_lpwstr,
|
||||||
|
&camera_link_length))) {
|
||||||
|
|
||||||
|
// cut name to make ID
|
||||||
|
std::wstring camera_link_ws = std::wstring(camera_link_lpwstr);
|
||||||
|
std::wstring camera_id = camera_link_ws.substr(8, 23);
|
||||||
|
|
||||||
|
log_info("drs::rgbcam", "found video capture device: {}", ws2s(camera_link_ws));
|
||||||
|
|
||||||
|
// only support cameras that start with \\?\usb
|
||||||
|
if (!string_begins_with(camera_link_ws, L"\\\\?\\usb")) {
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get camera instance
|
||||||
|
std::string camera_link = ws2s(camera_link_ws);
|
||||||
|
std::string camera_instance = camera_link.substr(32, 17);
|
||||||
|
|
||||||
|
// hook the camera
|
||||||
|
hook_camera(camera_id, camera_instance);
|
||||||
|
} else {
|
||||||
|
log_warning("drs::rgbcam", "failed to open camera");
|
||||||
|
}
|
||||||
|
|
||||||
|
// return result
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_rgb_camera_hook() {
|
||||||
|
log_info("drs::rgbcam", "initializing camera hook");
|
||||||
|
|
||||||
|
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// camera media framework hook
|
||||||
|
MFEnumDeviceSources_orig = detour::iat_try(
|
||||||
|
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace games::drs {
|
||||||
|
void init_rgb_camera_hook();
|
||||||
|
}
|
||||||
@@ -35,13 +35,14 @@ std::vector<Analog> &games::ftt::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("FutureTomTom");
|
analogs = GameAPI::Analogs::getAnalogs("FutureTomTom");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Pad 1",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Pad 2",
|
{ "Pad 1", AnalogType::LinearPositive },
|
||||||
"Pad 3",
|
{ "Pad 2", AnalogType::LinearPositive },
|
||||||
"Pad 4"
|
{ "Pad 3", AnalogType::LinearPositive },
|
||||||
);
|
{ "Pad 4", AnalogType::LinearPositive }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -0,0 +1,723 @@
|
|||||||
|
#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);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_AC1__DEVSTATUS) == 0x2C8);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,58 @@
|
|||||||
#include "gitadora.h"
|
#include "gitadora.h"
|
||||||
|
#include "handle.h"
|
||||||
|
#include "bi2x_hook.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
|
#include "hooks/audio/mme.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
|
#include "overlay/overlay.h"
|
||||||
#include "util/cpuutils.h"
|
#include "util/cpuutils.h"
|
||||||
|
#include "util/deferlog.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/sysutils.h"
|
||||||
|
#include "util/utils.h"
|
||||||
|
#include "hooks/setupapihook.h"
|
||||||
|
|
||||||
namespace games::gitadora {
|
namespace games::gitadora {
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
bool TWOCHANNEL = false;
|
bool TWOCHANNEL = false;
|
||||||
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
||||||
|
bool 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.
|
* GitaDora checks if the IP address has changed, and if it has it throws 5-1506-0000 like jubeat.
|
||||||
@@ -184,9 +222,308 @@ namespace games::gitadora {
|
|||||||
log_fatal("gitadora", "Cabinet type 3 (SD2) not supported on XG series");
|
log_fatal("gitadora", "Cabinet type 3 (SD2) not supported on XG series");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// arena model launches a tiny window yet backbuffer at 4k, resulting in unusable overlay
|
||||||
|
// force scaling to make things usable
|
||||||
|
if (!overlay::UI_SCALE_PERCENT.has_value() && is_arena_model() && !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");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
if (is_arena_model() && !GRAPHICS_WINDOWED && !GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||||
|
const auto &monitors = sysutils::enumerate_monitors();
|
||||||
|
const size_t active_count = monitors.size();
|
||||||
|
log_info("gitadora", "arena model: detected {} active monitor(s)", active_count);
|
||||||
|
if (active_count < 4) {
|
||||||
|
log_info("gitadora", "arena model: enable single monitor mode due to insufficient monitors");
|
||||||
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
|
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
static decltype(GetDisplayConfigBufferSizes) *GetDisplayConfigBufferSizes_orig = nullptr;
|
||||||
|
static decltype(QueryDisplayConfig) *QueryDisplayConfig_orig = nullptr;
|
||||||
|
static decltype(DisplayConfigGetDeviceInfo) *DisplayConfigGetDeviceInfo_orig = nullptr;
|
||||||
|
|
||||||
|
// cached primary real monitor: its path + source/target mode entries.
|
||||||
|
// modeInfoIdx values on the path are renumbered to 0 / 1 so the cache is self-contained.
|
||||||
|
static DISPLAYCONFIG_PATH_INFO real_primary_path = {};
|
||||||
|
static DISPLAYCONFIG_MODE_INFO real_primary_modes[2] = {}; // [0]=source, [1]=target
|
||||||
|
|
||||||
|
// fake monitors appended after the real ones. the game classifies monitors
|
||||||
|
// by outputTechnology + connectorInstance:
|
||||||
|
// HDMI -> main 4k monitor (real primary)
|
||||||
|
// DP connInstance 0 -> left
|
||||||
|
// DP connInstance 1 -> right
|
||||||
|
// DP connInstance 2 -> small (sub/touch)
|
||||||
|
// ids are negated on the fake monitor headers so they can be distinguished
|
||||||
|
// from real ones in DisplayConfigGetDeviceInfo.
|
||||||
|
struct FakeMonitor {
|
||||||
|
LONG id;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int offset_x;
|
||||||
|
int offset_y;
|
||||||
|
UINT32 connector_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ORDERING MATTERS: the d3d9 wrapper (FAKE_SUBSCREEN_ADAPTER) hands out
|
||||||
|
// adapters as "\\.\DISPLAY_SPICE_FAKE_{N}" for N=1,2,3. The game maps each
|
||||||
|
// adapter to a swap chain role via its DisplayConfig connector instance,
|
||||||
|
// so the entries here must be listed in the same order the wrapper enumerates
|
||||||
|
// them: id=1 -> adapter 1 -> left, id=2 -> adapter 2 -> right, id=3 -> adapter 3 -> small.
|
||||||
|
static constexpr FakeMonitor FAKE_MONITORS[] = {
|
||||||
|
{ 1, 1080, 1920, -100000, -100000, 0 }, // left (DP connector instance 0)
|
||||||
|
{ 2, 1080, 1920, -200000, -200000, 1 }, // right (DP connector instance 1)
|
||||||
|
{ 3, 800, 1280, -300000, -300000, 2 }, // small (DP connector instance 2, touch)
|
||||||
|
};
|
||||||
|
static constexpr UINT32 FAKE_MONITOR_COUNT = static_cast<UINT32>(std::size(FAKE_MONITORS));
|
||||||
|
|
||||||
|
// call QueryDisplayConfig once, keep only the primary monitor's path and its
|
||||||
|
// two referenced modes (source + target). modeInfoIdx values are rewritten to
|
||||||
|
// 0 and 1 so the cache is self-consistent.
|
||||||
|
static void cache_primary_monitor_info() {
|
||||||
|
UINT32 path_count = 0;
|
||||||
|
UINT32 mode_count = 0;
|
||||||
|
if (GetDisplayConfigBufferSizes_orig(
|
||||||
|
QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) != ERROR_SUCCESS) {
|
||||||
|
log_fatal("gitadora", "cache_primary_monitor_info: GetDisplayConfigBufferSizes failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DISPLAYCONFIG_PATH_INFO> all_paths(path_count);
|
||||||
|
std::vector<DISPLAYCONFIG_MODE_INFO> all_modes(mode_count);
|
||||||
|
if (QueryDisplayConfig_orig(
|
||||||
|
QDC_ONLY_ACTIVE_PATHS,
|
||||||
|
&path_count, all_paths.data(),
|
||||||
|
&mode_count, all_modes.data(),
|
||||||
|
nullptr) != ERROR_SUCCESS) {
|
||||||
|
log_fatal("gitadora", "cache_primary_monitor_info: QueryDisplayConfig failed");
|
||||||
|
}
|
||||||
|
all_paths.resize(path_count);
|
||||||
|
all_modes.resize(mode_count);
|
||||||
|
|
||||||
|
// pick the primary monitor: source mode at (0, 0)
|
||||||
|
auto primary = std::find_if(all_paths.begin(), all_paths.end(),
|
||||||
|
[&](const auto &p) {
|
||||||
|
const auto idx = p.sourceInfo.modeInfoIdx;
|
||||||
|
return idx < all_modes.size() &&
|
||||||
|
all_modes[idx].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE &&
|
||||||
|
all_modes[idx].sourceMode.position.x == 0 &&
|
||||||
|
all_modes[idx].sourceMode.position.y == 0;
|
||||||
|
});
|
||||||
|
if (primary == all_paths.end()) {
|
||||||
|
log_fatal("gitadora", "cache_primary_monitor_info: no primary monitor found");
|
||||||
|
}
|
||||||
|
|
||||||
|
real_primary_modes[0] = all_modes[primary->sourceInfo.modeInfoIdx];
|
||||||
|
real_primary_modes[1] = all_modes[primary->targetInfo.modeInfoIdx];
|
||||||
|
real_primary_path = *primary;
|
||||||
|
real_primary_path.sourceInfo.modeInfoIdx = 0;
|
||||||
|
real_primary_path.targetInfo.modeInfoIdx = 1;
|
||||||
|
|
||||||
|
log_info("gitadora", "cache_primary_monitor_info: cached primary monitor");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
GetDisplayConfigBufferSizes_hook(
|
||||||
|
UINT32 Flags,
|
||||||
|
UINT32 *pNumPathArrayElements,
|
||||||
|
UINT32 *pNumModeInfoArrayElements)
|
||||||
|
{
|
||||||
|
// populate cached primary real monitor on the first call
|
||||||
|
static std::once_flag populate_once;
|
||||||
|
std::call_once(populate_once, cache_primary_monitor_info);
|
||||||
|
|
||||||
|
// always report exactly 1 real + N fake monitors
|
||||||
|
*pNumPathArrayElements = 1 + FAKE_MONITOR_COUNT;
|
||||||
|
*pNumModeInfoArrayElements = 2 + FAKE_MONITOR_COUNT * 2;
|
||||||
|
|
||||||
|
log_info(
|
||||||
|
"gitadora",
|
||||||
|
"GetDisplayConfigBufferSizes: 1 real path + {} fake monitor(s)",
|
||||||
|
FAKE_MONITOR_COUNT);
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write fake monitor i into the caller's path/mode arrays. layout (single-monitor
|
||||||
|
// assumption): index 0 in both arrays holds the cached primary real monitor, so
|
||||||
|
// fake i occupies path slot (1 + i) and mode slots (2 + i*2) / (2 + i*2 + 1).
|
||||||
|
static void insert_fake_monitor(
|
||||||
|
DISPLAYCONFIG_PATH_INFO *paths,
|
||||||
|
DISPLAYCONFIG_MODE_INFO *modes,
|
||||||
|
UINT32 i)
|
||||||
|
{
|
||||||
|
const FakeMonitor &m = FAKE_MONITORS[i];
|
||||||
|
const UINT32 src_idx = 2 + i * 2;
|
||||||
|
const UINT32 tgt_idx = src_idx + 1;
|
||||||
|
const LUID adapter_id { .LowPart = static_cast<DWORD>(-m.id), .HighPart = -m.id };
|
||||||
|
const UINT32 uid = static_cast<UINT32>(-m.id);
|
||||||
|
|
||||||
|
paths[1 + i] = {
|
||||||
|
.sourceInfo = {
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.id = uid,
|
||||||
|
.modeInfoIdx = src_idx,
|
||||||
|
.statusFlags = DISPLAYCONFIG_SOURCE_IN_USE,
|
||||||
|
},
|
||||||
|
.targetInfo = {
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.id = uid,
|
||||||
|
.modeInfoIdx = tgt_idx,
|
||||||
|
.outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL,
|
||||||
|
.rotation = DISPLAYCONFIG_ROTATION_IDENTITY,
|
||||||
|
.scaling = DISPLAYCONFIG_SCALING_IDENTITY,
|
||||||
|
.refreshRate = { .Numerator = 60000, .Denominator = 1000 },
|
||||||
|
.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE,
|
||||||
|
.targetAvailable = TRUE,
|
||||||
|
.statusFlags = DISPLAYCONFIG_TARGET_IN_USE,
|
||||||
|
},
|
||||||
|
.flags = DISPLAYCONFIG_PATH_ACTIVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
modes[src_idx] = {
|
||||||
|
.infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE,
|
||||||
|
.id = uid,
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.sourceMode = {
|
||||||
|
.width = static_cast<UINT32>(m.width),
|
||||||
|
.height = static_cast<UINT32>(m.height),
|
||||||
|
.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP,
|
||||||
|
.position = { .x = m.offset_x, .y = m.offset_y },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
modes[tgt_idx] = {
|
||||||
|
.infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET,
|
||||||
|
.id = uid,
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.targetMode = {},
|
||||||
|
};
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"gitadora",
|
||||||
|
"inserted fake monitor: id={}, width={}, height={}, offset_x={}, offset_y={}",
|
||||||
|
m.id, m.width, m.height, m.offset_x, m.offset_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
QueryDisplayConfig_hook(
|
||||||
|
UINT32 flags,
|
||||||
|
UINT32* numPathArrayElements,
|
||||||
|
DISPLAYCONFIG_PATH_INFO* pathArray,
|
||||||
|
UINT32* numModeInfoArrayElements,
|
||||||
|
DISPLAYCONFIG_MODE_INFO* modeInfoArray,
|
||||||
|
DISPLAYCONFIG_TOPOLOGY_ID* currentTopologyId)
|
||||||
|
{
|
||||||
|
// copy cached primary real monitor into caller buffers at index 0
|
||||||
|
pathArray[0] = real_primary_path;
|
||||||
|
modeInfoArray[0] = real_primary_modes[0];
|
||||||
|
modeInfoArray[1] = real_primary_modes[1];
|
||||||
|
*numPathArrayElements = 1 + FAKE_MONITOR_COUNT;
|
||||||
|
*numModeInfoArrayElements = 2 + FAKE_MONITOR_COUNT * 2;
|
||||||
|
if (currentTopologyId != nullptr) {
|
||||||
|
*currentTopologyId = DISPLAYCONFIG_TOPOLOGY_EXTEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_misc("gitadora", "QueryDisplayConfig returning fake monitor paths and modes");
|
||||||
|
|
||||||
|
// append fake monitors after the real one
|
||||||
|
for (UINT32 i = 0; i < FAKE_MONITOR_COUNT; i++) {
|
||||||
|
insert_fake_monitor(pathArray, modeInfoArray, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
DisplayConfigGetDeviceInfo_hook(DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket)
|
||||||
|
{
|
||||||
|
if (requestPacket == nullptr) {
|
||||||
|
return DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle fake monitors (negative id) directly without calling orig
|
||||||
|
const auto id = static_cast<int>(requestPacket->id);
|
||||||
|
if (id < 0) {
|
||||||
|
if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME) {
|
||||||
|
const auto sourceName = reinterpret_cast<DISPLAYCONFIG_SOURCE_DEVICE_NAME*>(requestPacket);
|
||||||
|
// name must match WrappedIDirect3D9::GetAdapterIdentifier
|
||||||
|
const std::string adapter_name = fmt::format("\\\\.\\DISPLAY_SPICE_FAKE_{}", -id);
|
||||||
|
wcscpy(sourceName->viewGdiDeviceName, s2ws(adapter_name).c_str());
|
||||||
|
log_misc("gitadora",
|
||||||
|
"DisplayConfigGetDeviceInfo: fake source id={} name={}", id, adapter_name);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME) {
|
||||||
|
const auto targetName = reinterpret_cast<DISPLAYCONFIG_TARGET_DEVICE_NAME*>(requestPacket);
|
||||||
|
const LONG fake_id = -id;
|
||||||
|
UINT32 conn_inst = 0xff;
|
||||||
|
for (const auto& f : FAKE_MONITORS) {
|
||||||
|
if (f.id == fake_id) {
|
||||||
|
conn_inst = f.connector_instance;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
|
||||||
|
targetName->connectorInstance = conn_inst;
|
||||||
|
wcscpy(targetName->monitorFriendlyDeviceName, L"Spice Fake Monitor");
|
||||||
|
wcscpy(targetName->monitorDevicePath, L"\\\\?\\SpiceFakeMonitor");
|
||||||
|
log_misc("gitadora",
|
||||||
|
"DisplayConfigGetDeviceInfo: fake target id={} -> DP connInst {}",
|
||||||
|
id, targetName->connectorInstance);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ret = DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
if (ret != ERROR_SUCCESS ||
|
||||||
|
requestPacket->type != DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// override the cached primary real monitor target info to look like HDMI/0
|
||||||
|
const auto targetName = reinterpret_cast<DISPLAYCONFIG_TARGET_DEVICE_NAME*>(requestPacket);
|
||||||
|
const auto& target = real_primary_path.targetInfo;
|
||||||
|
if (target.id == targetName->header.id &&
|
||||||
|
target.adapterId.HighPart == targetName->header.adapterId.HighPart &&
|
||||||
|
target.adapterId.LowPart == targetName->header.adapterId.LowPart)
|
||||||
|
{
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
|
||||||
|
targetName->connectorInstance = 0;
|
||||||
|
log_info("gitadora",
|
||||||
|
"overriding primary monitor (id={}) to pretend to be HDMI",
|
||||||
|
targetName->header.id);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void GitaDoraGame::attach() {
|
void GitaDoraGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
@@ -214,29 +551,91 @@ namespace games::gitadora {
|
|||||||
system_module, "sys_debug_dip_set_param"));
|
system_module, "sys_debug_dip_set_param"));
|
||||||
|
|
||||||
#ifdef SPICE64
|
#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);
|
||||||
|
|
||||||
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
// Gitadora IO(J32D/J33I) board emulation
|
||||||
|
devicehook_init(avs::game::DLL_INSTANCE);
|
||||||
|
devicehook_add(new GitaDoraSerialHandle());
|
||||||
|
|
||||||
// window patch
|
// test/service/coin buttons
|
||||||
if (GRAPHICS_WINDOWED && !replace_pattern(
|
bi2x_hook_init();
|
||||||
gdme_module,
|
|
||||||
"754185ED753D8B4118BF0000CB02",
|
// f: drive hook
|
||||||
"9090????9090??????????????12", 0, 0))
|
detour::iat_try("GetLogicalDrives", GetLogicalDrives_hook, avs::game::DLL_INSTANCE);
|
||||||
{
|
detour::iat_try("GetDriveTypeA", GetDriveTypeA_hook, avs::game::DLL_INSTANCE);
|
||||||
log_warning("gitadora", "windowed mode failed");
|
detour::iat_try("CreateDirectoryA", CreateDirectoryA_hook, avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// volume change prevention
|
||||||
|
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// monitor/touch hooks (windowed or full screen)
|
||||||
|
if (GRAPHICS_FORCE_SINGLE_ADAPTER || GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
|
// enable touch hook for subscreen overlay
|
||||||
|
wintouchemu::FORCE = true;
|
||||||
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
|
wintouchemu::hook("GITADORA", avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
#if !SPICE_XP
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
// monitor hook: always pretend to have 1 primary real monitor + 3 fake monitors
|
||||||
|
// (LEFT / RIGHT / SMALL) so the game accepts the arena cab topology
|
||||||
|
GetDisplayConfigBufferSizes_orig =
|
||||||
|
detour::iat_try("GetDisplayConfigBufferSizes",
|
||||||
|
GetDisplayConfigBufferSizes_hook, avs::game::DLL_INSTANCE);
|
||||||
|
QueryDisplayConfig_orig =
|
||||||
|
detour::iat_try("QueryDisplayConfig",
|
||||||
|
QueryDisplayConfig_hook, avs::game::DLL_INSTANCE);
|
||||||
|
DisplayConfigGetDeviceInfo_orig =
|
||||||
|
detour::iat_try("DisplayConfigGetDeviceInfo",
|
||||||
|
DisplayConfigGetDeviceInfo_hook, avs::game::DLL_INSTANCE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HMODULE bmsd_engine_module = libutils::try_module("libbmsd-engine.dll");
|
// window patch
|
||||||
HMODULE bmsd_module = libutils::try_module("libbmsd.dll");
|
if (!is_arena_model()) {
|
||||||
|
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
||||||
|
if (GRAPHICS_WINDOWED && !replace_pattern(
|
||||||
|
gdme_module,
|
||||||
|
"754185ED753D8B4118BF0000CB02",
|
||||||
|
"9090????9090??????????????12", 0, 0)) {
|
||||||
|
log_warning("gitadora", "windowed mode failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// two channel mod
|
// two channel mod
|
||||||
if (TWOCHANNEL) {
|
if (TWOCHANNEL) {
|
||||||
bmsd2_boot_orig = detour::iat_try("bmsd2_boot", bmsd2_boot_hook, bmsd_module);
|
if (is_arena_model()) {
|
||||||
|
log_warning("gitadora", "two channel audio (-2ch) is not supported on Arena Model - use a patch instead");
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"two channel audio (-2ch) is not supported on Arena Model - use a patch instead",
|
||||||
|
});
|
||||||
|
|
||||||
if (!(replace_pattern(bmsd_engine_module, "33000000488D", "03??????????", 0, 0) ||
|
} else {
|
||||||
replace_pattern(bmsd_engine_module, "330000000F10", "03??????????", 0, 0)))
|
HMODULE bmsd_engine_module = libutils::try_module("libbmsd-engine.dll");
|
||||||
{
|
HMODULE bmsd_module = libutils::try_module("libbmsd.dll");
|
||||||
log_warning("gitadora", "two channel mode failed");
|
|
||||||
|
bmsd2_boot_orig = detour::iat_try("bmsd2_boot", bmsd2_boot_hook, bmsd_module);
|
||||||
|
if (!(replace_pattern(bmsd_engine_module, "33000000488D", "03??????????", 0, 0) ||
|
||||||
|
replace_pattern(bmsd_engine_module, "330000000F10", "03??????????", 0, 0))) {
|
||||||
|
log_warning("gitadora", "two channel mode failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,17 @@
|
|||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
|
||||||
namespace games::gitadora {
|
namespace games::gitadora {
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern bool TWOCHANNEL;
|
extern bool TWOCHANNEL;
|
||||||
extern std::optional<unsigned int> CAB_TYPE;
|
extern std::optional<unsigned int> CAB_TYPE;
|
||||||
|
extern bool P1_LEFTY;
|
||||||
|
extern bool P2_LEFTY;
|
||||||
|
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||||
|
extern std::optional<socd::SocdAlgorithm> PICK_ALGO;
|
||||||
|
|
||||||
class GitaDoraGame : public games::Game {
|
class GitaDoraGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
@@ -21,14 +26,29 @@ namespace games::gitadora {
|
|||||||
static inline bool is_drum() {
|
static inline bool is_drum() {
|
||||||
return (
|
return (
|
||||||
avs::game::is_model({ "J32", "K32", "L32" }) ||
|
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() {
|
static inline bool is_guitar() {
|
||||||
return (
|
return (
|
||||||
avs::game::is_model({ "J33", "K33", "L33" }) ||
|
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",
|
"Service",
|
||||||
"Test",
|
"Test",
|
||||||
"Coin",
|
"Coin",
|
||||||
|
"Headphone",
|
||||||
"Guitar P1 Start",
|
"Guitar P1 Start",
|
||||||
"Guitar P1 Up",
|
"Guitar P1 Up",
|
||||||
"Guitar P1 Down",
|
"Guitar P1 Down",
|
||||||
@@ -64,17 +65,18 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
|||||||
"Drum Help",
|
"Drum Help",
|
||||||
"Drum Button Extra 1",
|
"Drum Button Extra 1",
|
||||||
"Drum Button Extra 2",
|
"Drum Button Extra 2",
|
||||||
|
|
||||||
|
"Drum Left Cymbal",
|
||||||
"Drum Hi-Hat",
|
"Drum Hi-Hat",
|
||||||
"Drum Hi-Hat Closed",
|
"Drum Left Pedal",
|
||||||
"Drum Hi-Hat Half-Open",
|
|
||||||
"Drum Snare",
|
"Drum Snare",
|
||||||
"Drum Hi-Tom",
|
"Drum Hi-Tom",
|
||||||
"Drum Low-Tom",
|
|
||||||
"Drum Right Cymbal",
|
|
||||||
"Drum Bass Pedal",
|
"Drum Bass Pedal",
|
||||||
"Drum Left Cymbal",
|
"Drum Low-Tom",
|
||||||
"Drum Left Pedal",
|
"Drum Floor Tom",
|
||||||
"Drum Floor Tom"
|
"Drum Right Cymbal",
|
||||||
|
"Drum Hi-Hat Closed",
|
||||||
|
"Drum Hi-Hat Half-Open"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,19 +86,21 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
|||||||
std::vector<Analog> &games::gitadora::get_analogs() {
|
std::vector<Analog> &games::gitadora::get_analogs() {
|
||||||
static std::vector<Analog> analogs;
|
static std::vector<Analog> analogs;
|
||||||
|
|
||||||
|
using namespace GameAPI::Analogs;
|
||||||
|
|
||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("GitaDora");
|
analogs = GameAPI::Analogs::getAnalogs("GitaDora");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(&analogs,
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Guitar P1 Wail X",
|
{"Guitar P1 Wail X", AnalogType::LinearCentered},
|
||||||
"Guitar P1 Wail Y",
|
{"Guitar P1 Wail Y", AnalogType::LinearCentered},
|
||||||
"Guitar P1 Wail Z",
|
{"Guitar P1 Wail Z", AnalogType::LinearCentered},
|
||||||
"Guitar P1 Knob",
|
{"Guitar P1 Knob", AnalogType::Circular},
|
||||||
"Guitar P2 Wail X",
|
{"Guitar P2 Wail X", AnalogType::LinearCentered},
|
||||||
"Guitar P2 Wail Y",
|
{"Guitar P2 Wail Y", AnalogType::LinearCentered},
|
||||||
"Guitar P2 Wail Z",
|
{"Guitar P2 Wail Z", AnalogType::LinearCentered},
|
||||||
"Guitar P2 Knob"
|
{"Guitar P2 Knob", AnalogType::Circular}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
@@ -106,9 +110,18 @@ std::string games::gitadora::get_buttons_help() {
|
|||||||
// keep to max 100 characters wide
|
// keep to max 100 characters wide
|
||||||
return
|
return
|
||||||
"guitar:\n"
|
"guitar:\n"
|
||||||
|
"\n"
|
||||||
" < R G B Y P --- Pick ] \n"
|
" < R G B Y P --- Pick ] \n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" If you normally hold your guitar left-handed (frets on right hand):\n"
|
||||||
|
" * here, hold the guitar as right-handed (frets on left hand),\n"
|
||||||
|
" and bind the buttons, pick, and wail controls\n"
|
||||||
|
" * in Options tab, turn on GitaDora Lefty Guitar\n"
|
||||||
|
" * finally, use the in-game option to turn on LEFT mode\n"
|
||||||
|
"\n"
|
||||||
|
"\n"
|
||||||
"drums:\n"
|
"drums:\n"
|
||||||
|
"\n"
|
||||||
" LeftCymbal RightCymbal\n"
|
" LeftCymbal RightCymbal\n"
|
||||||
" HiHat HiTom LowTom \n"
|
" HiHat HiTom LowTom \n"
|
||||||
" Snare FloorTom \n"
|
" Snare FloorTom \n"
|
||||||
@@ -116,10 +129,28 @@ std::string games::gitadora::get_buttons_help() {
|
|||||||
" Left Bass\n"
|
" Left Bass\n"
|
||||||
" Pedal Pedal\n"
|
" Pedal Pedal\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Drums are NOT velocity-sensitive!\n"
|
" For MIDI drums with Open/Closed HiHat configurations or pads with\n"
|
||||||
|
" multiple hit zones, ensure you bind all variation using the Pages\n"
|
||||||
|
" button at the bottom."
|
||||||
|
;
|
||||||
|
}
|
||||||
|
std::string games::gitadora::get_analogs_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return
|
||||||
|
"guitar:\n"
|
||||||
"\n"
|
"\n"
|
||||||
"For MIDI drums with Open/Closed HiHat configurations, bind variations below. "
|
"X axis: 0% when body is held facing monitor, 50% when flat on a table\n"
|
||||||
"v2_drum algorithm might work better for those drums."
|
"Y axis: 50% when held horizontal, 0% when neck is raised, 100% pointing down\n"
|
||||||
|
"Z axis: 50% at rest, 100% when swinging neck toward monitor (only used in XG series)\n"
|
||||||
|
"\n"
|
||||||
|
"You need both X and Y axis for up/down wail to work correctly in-game.\n"
|
||||||
|
"If you only have Y axis, consider using digital wailing instead.\n"
|
||||||
|
"\n"
|
||||||
|
"Ensure you clear all analog bindings if you want to use digital wailing.\n"
|
||||||
|
"\n"
|
||||||
|
"If you hold your guitar left-handed (frets on the right hand),\n"
|
||||||
|
"analog bindings will likely not work as intended.\n"
|
||||||
|
"Consider using digital wailing in Buttons tab."
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,88 +160,102 @@ std::vector<Light> &games::gitadora::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("GitaDora");
|
lights = GameAPI::Lights::getLights("GitaDora");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(&lights,
|
GameAPI::Lights::sortLightsWithCategory(&lights,
|
||||||
"Guitar P1 Motor",
|
{
|
||||||
"Guitar P2 Motor",
|
{"Guitar", "Guitar P1 Motor"},
|
||||||
|
{"Guitar", "Guitar P2 Motor"},
|
||||||
|
|
||||||
"P1 Start",
|
{"Menu", "P1 Start"},
|
||||||
"P1 Menu Up Down (DX)",
|
{"Menu", "P1 Menu Up Down (DX)"},
|
||||||
"P1 Menu Left Right (DX)",
|
{"Menu", "P1 Menu Left Right (DX)"},
|
||||||
"P1 Help (DX)",
|
{"Menu", "P1 Help (DX)"},
|
||||||
|
|
||||||
"P2 Start",
|
{"Menu", "P2 Start"},
|
||||||
"P2 Menu Up Down (DX)",
|
{"Menu", "P2 Menu Up Down (DX)"},
|
||||||
"P2 Menu Left Right (DX)",
|
{"Menu", "P2 Menu Left Right (DX)"},
|
||||||
"P2 Help (DX)",
|
{"Menu", "P2 Help (DX)"},
|
||||||
|
|
||||||
"Drum Left Cymbal",
|
{"Drums", "Drum Left Cymbal"},
|
||||||
"Drum Hi-Hat",
|
{"Drums", "Drum Hi-Hat"},
|
||||||
"Drum Snare",
|
{"Drums", "Drum Snare"},
|
||||||
"Drum High Tom",
|
{"Drums", "Drum High Tom"},
|
||||||
"Drum Low Tom",
|
{"Drums", "Drum Low Tom"},
|
||||||
"Drum Floor Tom",
|
{"Drums", "Drum Floor Tom"},
|
||||||
"Drum Right Cymbal",
|
{"Drums", "Drum Right Cymbal"},
|
||||||
|
|
||||||
"Drum Woofer R",
|
{"DX/SD", "Drum Woofer R"},
|
||||||
"Drum Woofer G",
|
{"DX/SD", "Drum Woofer G"},
|
||||||
"Drum Woofer B",
|
{"DX/SD", "Drum Woofer B"},
|
||||||
|
|
||||||
"Drum Stage R (DX)",
|
{"DX/SD", "Drum Stage R (DX)"},
|
||||||
"Drum Stage G (DX)",
|
{"DX/SD", "Drum Stage G (DX)"},
|
||||||
"Drum Stage B (DX)",
|
{"DX/SD", "Drum Stage B (DX)"},
|
||||||
|
|
||||||
"Spot Left (DX)",
|
{"DX/SD", "Spot Left (DX)"},
|
||||||
"Spot Right (DX)",
|
{"DX/SD", "Spot Right (DX)"},
|
||||||
"Spot Center Left (DX)",
|
{"DX/SD", "Spot Center Left (DX)"},
|
||||||
"Spot Center Right (DX)",
|
{"DX/SD", "Spot Center Right (DX)"},
|
||||||
|
|
||||||
"Drum Spot Rear Left (DX)",
|
{"DX/SD", "Drum Spot Rear Left (DX)"},
|
||||||
"Drum Spot Rear Right (DX)",
|
{"DX/SD", "Drum Spot Rear Right (DX)"},
|
||||||
|
|
||||||
"Guitar Lower Left R (DX)",
|
{"DX/SD", "Guitar Lower Left R (DX)"},
|
||||||
"Guitar Lower Left G (DX)",
|
{"DX/SD", "Guitar Lower Left G (DX)"},
|
||||||
"Guitar Lower Left B (DX)",
|
{"DX/SD", "Guitar Lower Left B (DX)"},
|
||||||
"Guitar Lower Right R (DX)",
|
{"DX/SD", "Guitar Lower Right R (DX)"},
|
||||||
"Guitar Lower Right G (DX)",
|
{"DX/SD", "Guitar Lower Right G (DX)"},
|
||||||
"Guitar Lower Right B (DX)",
|
{"DX/SD", "Guitar Lower Right B (DX)"},
|
||||||
|
|
||||||
"Guitar Left Speaker Upper R (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper R (DX)"},
|
||||||
"Guitar Left Speaker Upper G (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper G (DX)"},
|
||||||
"Guitar Left Speaker Upper B (DX)",
|
{"DX/SD", "Guitar Left Speaker Upper B (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left R (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left G (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Left B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Left B (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right R (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right G (DX)"},
|
||||||
"Guitar Left Speaker Mid Up Right B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Up Right B (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left R (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left G (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Left B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Left B (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right R (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right R (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right G (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right G (DX)"},
|
||||||
"Guitar Left Speaker Mid Low Right B (DX)",
|
{"DX/SD", "Guitar Left Speaker Mid Low Right B (DX)"},
|
||||||
"Guitar Left Speaker Lower R (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower R (DX)"},
|
||||||
"Guitar Left Speaker Lower G (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower G (DX)"},
|
||||||
"Guitar Left Speaker Lower B (DX)",
|
{"DX/SD", "Guitar Left Speaker Lower B (DX)"},
|
||||||
|
|
||||||
"Guitar Right Speaker Upper R (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper R (DX)"},
|
||||||
"Guitar Right Speaker Upper G (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper G (DX)"},
|
||||||
"Guitar Right Speaker Upper B (DX)",
|
{"DX/SD", "Guitar Right Speaker Upper B (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left R (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left G (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Left B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Left B (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right R (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right G (DX)"},
|
||||||
"Guitar Right Speaker Mid Up Right B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Up Right B (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left R (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left G (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Left B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Left B (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right R (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right R (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right G (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right G (DX)"},
|
||||||
"Guitar Right Speaker Mid Low Right B (DX)",
|
{"DX/SD", "Guitar Right Speaker Mid Low Right B (DX)"},
|
||||||
"Guitar Right Speaker Lower R (DX)",
|
{"DX/SD", "Guitar Right Speaker Lower R (DX)"},
|
||||||
"Guitar Right Speaker Lower G (DX)",
|
{"DX/SD", "Guitar Right Speaker Lower G (DX)"},
|
||||||
"Guitar Right Speaker Lower B (DX)"
|
{"DX/SD", "Guitar Right Speaker Lower B (DX)"},
|
||||||
|
|
||||||
|
{"Arena Model", "Title Average R (Arena)"},
|
||||||
|
{"Arena Model", "Title Average G (Arena)"},
|
||||||
|
{"Arena Model", "Title Average B (Arena)"},
|
||||||
|
|
||||||
|
{"Arena Model", "Woofer R (Arena)"},
|
||||||
|
{"Arena Model", "Woofer G (Arena)"},
|
||||||
|
{"Arena Model", "Woofer B (Arena)"},
|
||||||
|
|
||||||
|
{"Arena Model", "Card Reader R (Arena)"},
|
||||||
|
{"Arena Model", "Card Reader G (Arena)"},
|
||||||
|
{"Arena Model", "Card Reader B (Arena)"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace games::gitadora {
|
|||||||
Service,
|
Service,
|
||||||
Test,
|
Test,
|
||||||
Coin,
|
Coin,
|
||||||
|
Headphone,
|
||||||
GuitarP1Start,
|
GuitarP1Start,
|
||||||
GuitarP1Up,
|
GuitarP1Up,
|
||||||
GuitarP1Down,
|
GuitarP1Down,
|
||||||
@@ -65,18 +66,21 @@ namespace games::gitadora {
|
|||||||
DrumHelp,
|
DrumHelp,
|
||||||
DrumButtonExtra1,
|
DrumButtonExtra1,
|
||||||
DrumButtonExtra2,
|
DrumButtonExtra2,
|
||||||
// note: this is the order they appear in the test menu
|
// note: this is the order of drums in the default layout
|
||||||
|
DrumLeftCymbal,
|
||||||
DrumHiHat,
|
DrumHiHat,
|
||||||
DrumHiHatClosed,
|
DrumLeftPedal,
|
||||||
DrumHiHatHalfOpen,
|
|
||||||
DrumSnare,
|
DrumSnare,
|
||||||
DrumHiTom,
|
DrumHiTom,
|
||||||
DrumLowTom,
|
|
||||||
DrumRightCymbal,
|
|
||||||
DrumBassPedal,
|
DrumBassPedal,
|
||||||
DrumLeftCymbal,
|
DrumLowTom,
|
||||||
DrumLeftPedal,
|
DrumFloorTom,
|
||||||
DrumFloorTom
|
DrumRightCymbal,
|
||||||
|
|
||||||
|
// these only exist for back compat
|
||||||
|
// (created before it was possible to map multiple input to a button)
|
||||||
|
DrumHiHatClosed,
|
||||||
|
DrumHiHatHalfOpen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,13 +194,25 @@ namespace games::gitadora {
|
|||||||
GuitarRightSpeakerMidLowRightB,
|
GuitarRightSpeakerMidLowRightB,
|
||||||
GuitarRightSpeakerLowerR,
|
GuitarRightSpeakerLowerR,
|
||||||
GuitarRightSpeakerLowerG,
|
GuitarRightSpeakerLowerG,
|
||||||
GuitarRightSpeakerLowerB
|
GuitarRightSpeakerLowerB,
|
||||||
|
|
||||||
|
// arena model (common for both guitar and drum)
|
||||||
|
ArenaTitleAvgR,
|
||||||
|
ArenaTitleAvgG,
|
||||||
|
ArenaTitleAvgB,
|
||||||
|
ArenaWooferR,
|
||||||
|
ArenaWooferG,
|
||||||
|
ArenaWooferB,
|
||||||
|
ArenaCardR,
|
||||||
|
ArenaCardG,
|
||||||
|
ArenaCardB,
|
||||||
} gitadora_lights_t;
|
} gitadora_lights_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
std::vector<Button> &get_buttons();
|
std::vector<Button> &get_buttons();
|
||||||
std::string get_buttons_help();
|
std::string get_buttons_help();
|
||||||
|
std::string get_analogs_help();
|
||||||
std::vector<Analog> &get_analogs();
|
std::vector<Analog> &get_analogs();
|
||||||
std::vector<Light> &get_lights();
|
std::vector<Light> &get_lights();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "iidx.h"
|
#include "iidx.h"
|
||||||
@@ -82,7 +83,8 @@ bool games::iidx::IIDXFMSerialHandle::FMSerialDevice::parse_msg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sleep - otherwise the IO thread will go too hard on the CPU
|
// sleep - otherwise the IO thread will go too hard on the CPU
|
||||||
Sleep(1);
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(1);
|
||||||
|
|
||||||
// generate message
|
// generate message
|
||||||
auto msg = this->create_msg(msg_in, 0x2E);
|
auto msg = this->create_msg(msg_in, 0x2E);
|
||||||
@@ -195,11 +197,11 @@ bool games::iidx::IIDXFMSerialHandle::open(LPCWSTR lpFileName) {
|
|||||||
return false;
|
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 device
|
||||||
acio_emu.add_device(new FMSerialDevice());
|
acio_emu.add_device(new FMSerialDevice());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +242,7 @@ int games::iidx::IIDXFMSerialHandle::device_io(DWORD dwIoControlCode, LPVOID lpI
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool games::iidx::IIDXFMSerialHandle::close() {
|
bool games::iidx::IIDXFMSerialHandle::close() {
|
||||||
log_info("iidx", "Closed COM2 (FM DEVICE)");
|
log_info("iidx", "Closed COM2 (FM DEVICE / bi2a)");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "iidx.h"
|
#include "iidx.h"
|
||||||
@@ -82,7 +83,8 @@ bool games::iidx::BI2XSerialHandle::BI2XDevice::parse_msg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sleep - otherwise the IO thread will go too hard on the CPU
|
// sleep - otherwise the IO thread will go too hard on the CPU
|
||||||
Sleep(1);
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(1);
|
||||||
|
|
||||||
// generate message
|
// generate message
|
||||||
auto msg = this->create_msg(msg_in, 0x2E);
|
auto msg = this->create_msg(msg_in, 0x2E);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "bi2x_hook.h"
|
#include "bi2x_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
@@ -24,17 +26,28 @@ namespace games::iidx {
|
|||||||
uint8_t data[0x10];
|
uint8_t data[0x10];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIO_NMGR_IOB2 {
|
struct AIO_NMGR_IOB2_VTABLE {
|
||||||
uint8_t dummy0[0x50];
|
// other functions here, but they never get called
|
||||||
|
uint8_t dummy[0x50];
|
||||||
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
void (__fastcall *pAIO_NMGR_IOB_BeginManage)(int64_t a1);
|
||||||
uint8_t dummy1[0x9A0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR_IOB2 {
|
||||||
|
AIO_NMGR_IOB2_VTABLE *vptr;
|
||||||
|
uint8_t dummy[0x9F0];
|
||||||
|
};
|
||||||
|
|
||||||
|
// confirmed 0x9F8 in iidx27, iidx32 aioNMgrIob2_Create
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0x9F8);
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_TDJ {
|
struct AIO_IOB2_BI2X_TDJ {
|
||||||
// who knows
|
// who knows
|
||||||
uint8_t data[0x13F8];
|
uint8_t data[0x13F8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// confirmed 0x13F8 in iidx27, iidx32 in aioIob2Bi2xTDJ_Create
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_TDJ) == 0x13F8);
|
||||||
|
|
||||||
struct AIO_IOB2_BI2X_TDJ__DEVSTATUS {
|
struct AIO_IOB2_BI2X_TDJ__DEVSTATUS {
|
||||||
// of course you could work with variables here
|
// of course you could work with variables here
|
||||||
uint8_t buffer[0xCA];
|
uint8_t buffer[0xCA];
|
||||||
@@ -78,7 +91,7 @@ namespace games::iidx {
|
|||||||
// libaio-iob.dll
|
// libaio-iob.dll
|
||||||
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
typedef AC_HNDLIF* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint8_t device_num);
|
||||||
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
typedef int64_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(int64_t a1);
|
||||||
typedef AIO_NMGR_IOB2** (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
typedef AIO_NMGR_IOB2* (__fastcall *aioNMgrIob2_Create_t)(AC_HNDLIF *a1, unsigned int a2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* function pointers
|
* function pointers
|
||||||
@@ -120,6 +133,11 @@ namespace games::iidx {
|
|||||||
|
|
||||||
static AIO_IOB2_BI2X_TDJ* __fastcall aioIob2Bi2xTDJ_Create(AIO_NMGR_IOB2 *nmgr, int a2) {
|
static AIO_IOB2_BI2X_TDJ* __fastcall aioIob2Bi2xTDJ_Create(AIO_NMGR_IOB2 *nmgr, int a2) {
|
||||||
if (!BI2X_PASSTHROUGH) {
|
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;
|
custom_node = new AIO_IOB2_BI2X_TDJ;
|
||||||
memset(&custom_node->data, 0, sizeof(custom_node->data));
|
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) {
|
if (acHndlif == nullptr) {
|
||||||
acHndlif = new AC_HNDLIF;
|
acHndlif = new AC_HNDLIF;
|
||||||
memset(acHndlif->data, 0x0, sizeof(acHndlif->data));
|
memset(acHndlif->data, 0x0, sizeof(acHndlif->data));
|
||||||
@@ -451,14 +469,16 @@ namespace games::iidx {
|
|||||||
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
log_info("bi2x_hook", "AIO_NMGR_IOB::BeginManage");
|
||||||
};
|
};
|
||||||
|
|
||||||
static AIO_NMGR_IOB2** __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
static AIO_NMGR_IOB2* __fastcall aioNMgrIob2_Create(AC_HNDLIF *a1, unsigned int a2) {
|
||||||
if (aioNmgrIob2 == nullptr) {
|
if (aioNmgrIob2 == nullptr) {
|
||||||
aioNmgrIob2 = new AIO_NMGR_IOB2;
|
aioNmgrIob2 = new AIO_NMGR_IOB2{};
|
||||||
memset(aioNmgrIob2, 0x0, sizeof(AIO_NMGR_IOB2));
|
aioNmgrIob2->vptr = new AIO_NMGR_IOB2_VTABLE{};
|
||||||
aioNmgrIob2->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
aioNmgrIob2->vptr->pAIO_NMGR_IOB_BeginManage = AIO_NMGR_IOB_BeginManageStub;
|
||||||
}
|
}
|
||||||
log_info("bi2x_hook", "aioNMgrIob2_Create");
|
log_info("bi2x_hook", "aioNMgrIob2_Create returned {}, size=0x{:x}, vptr @ {}",
|
||||||
return &aioNmgrIob2;
|
fmt::ptr(aioNmgrIob2), sizeof(*aioNmgrIob2), fmt::ptr(aioNmgrIob2->vptr));
|
||||||
|
|
||||||
|
return aioNmgrIob2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
static int64_t __fastcall aioIob2Bi2x_WriteFirmGetState(int64_t a1) {
|
||||||
@@ -515,10 +535,12 @@ namespace games::iidx {
|
|||||||
// hook IOB
|
// hook IOB
|
||||||
const auto libaioIobDll = "libaio-iob.dll";
|
const auto libaioIobDll = "libaio-iob.dll";
|
||||||
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_OpenSciUsbCdc",
|
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_OpenSciUsbCdc",
|
||||||
aioIob2Bi2X_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
|
aioIob2Bi2x_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
|
||||||
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_WriteFirmGetState",
|
detour::trampoline_try(libaioIobDll, "aioIob2Bi2x_WriteFirmGetState",
|
||||||
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
||||||
detour::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
|
detour::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
|
||||||
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
namespace games::iidx {
|
namespace games::iidx {
|
||||||
|
|
||||||
void bi2x_hook_init();
|
void bi2x_hook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
#include "mf_wrappers.h"
|
#include "mf_wrappers.h"
|
||||||
@@ -84,7 +84,7 @@ namespace games::iidx {
|
|||||||
|
|
||||||
bool find_camera_hooks() {
|
bool find_camera_hooks() {
|
||||||
std::string dll_name = avs::game::DLL_NAME;
|
std::string dll_name = avs::game::DLL_NAME;
|
||||||
dll_path = MODULE_PATH / dll_name;
|
dll_path = MODULE_PATH / dll_name;
|
||||||
|
|
||||||
iidx_module = libutils::try_module(dll_path);
|
iidx_module = libutils::try_module(dll_path);
|
||||||
if (!iidx_module) {
|
if (!iidx_module) {
|
||||||
@@ -149,7 +149,7 @@ namespace games::iidx {
|
|||||||
"E800000000488BC8488BD34883C420",
|
"E800000000488BC8488BD34883C420",
|
||||||
"X????XXXXXXXXXX",
|
"X????XXXXXXXXXX",
|
||||||
0, 0));
|
0, 0));
|
||||||
|
|
||||||
if (addr_textures_ptr == nullptr) {
|
if (addr_textures_ptr == nullptr) {
|
||||||
log_warning("iidx:camhook", "failed to find hook: addr_textures (part 1)");
|
log_warning("iidx:camhook", "failed to find hook: addr_textures (part 1)");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -232,7 +232,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void **__fastcall camera_hook_a(PBYTE a1) {
|
static void **__fastcall camera_hook_a(PBYTE a1) {
|
||||||
std::call_once(hook_a_init, [&]{
|
std::call_once(hook_a_init, [&]{
|
||||||
device = *reinterpret_cast<LPDIRECT3DDEVICE9EX*>(a1 + addr_device_offset);
|
device = *reinterpret_cast<LPDIRECT3DDEVICE9EX*>(a1 + addr_device_offset);
|
||||||
auto const preview = *reinterpret_cast<LPDIRECT3DTEXTURE9**>((uint8_t*)iidx_module + addr_textures);
|
auto const preview = *reinterpret_cast<LPDIRECT3DTEXTURE9**>((uint8_t*)iidx_module + addr_textures);
|
||||||
auto const manager = reinterpret_cast<Camera::CCameraManager2*>((uint8_t*)iidx_module + addr_camera_manager);
|
auto const manager = reinterpret_cast<Camera::CCameraManager2*>((uint8_t*)iidx_module + addr_camera_manager);
|
||||||
@@ -298,11 +298,11 @@ namespace games::iidx {
|
|||||||
// Ask for source type = video capture devices.
|
// Ask for source type = video capture devices.
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
hr = pAttributes->SetGUID(
|
hr = pAttributes->SetGUID(
|
||||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
||||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enumerate devices.
|
// Enumerate devices.
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
hr = WrappedMFEnumDeviceSources(pAttributes, &ppDevices, &numDevices);
|
hr = WrappedMFEnumDeviceSources(pAttributes, &ppDevices, &numDevices);
|
||||||
@@ -478,7 +478,7 @@ namespace games::iidx {
|
|||||||
init_mf_library();
|
init_mf_library();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "games/iidx/local_camera.h"
|
#include "games/iidx/local_camera.h"
|
||||||
|
|||||||
+110
-26
@@ -29,6 +29,9 @@
|
|||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/memutils.h"
|
#include "util/memutils.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/sysutils.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "launcher/signal.h"
|
#include "launcher/signal.h"
|
||||||
|
|
||||||
@@ -64,6 +67,8 @@ namespace games::iidx {
|
|||||||
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
||||||
std::optional<std::string> SOUND_OUTPUT_DEVICE_IN_EFFECT = std::nullopt;
|
std::optional<std::string> SOUND_OUTPUT_DEVICE_IN_EFFECT = std::nullopt;
|
||||||
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
||||||
|
uint32_t TT_DELAY_P1 = 0;
|
||||||
|
uint32_t TT_DELAY_P2 = 0;
|
||||||
uint8_t DIGITAL_TT_SENS = 4;
|
uint8_t DIGITAL_TT_SENS = 4;
|
||||||
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE = std::nullopt;
|
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE = std::nullopt;
|
||||||
std::optional<std::string> SCREEN_MODE = std::nullopt;
|
std::optional<std::string> SCREEN_MODE = std::nullopt;
|
||||||
@@ -79,6 +84,11 @@ namespace games::iidx {
|
|||||||
char IIDXIO_LED_TICKER[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\x00'};
|
char IIDXIO_LED_TICKER[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\x00'};
|
||||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||||
std::mutex IIDX_LED_TICKER_LOCK;
|
std::mutex IIDX_LED_TICKER_LOCK;
|
||||||
|
bool IIDX_TDJ_MONITOR_WARNING = false;
|
||||||
|
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] = {
|
tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL] = {
|
||||||
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" },
|
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" },
|
||||||
@@ -247,6 +257,8 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
typedef void* (*aioIob2Bi2x_CreateWriteFirmContext_t)(unsigned int, int);
|
typedef void* (*aioIob2Bi2x_CreateWriteFirmContext_t)(unsigned int, int);
|
||||||
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
|
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
|
||||||
|
|
||||||
@@ -265,6 +277,8 @@ namespace games::iidx {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
IIDXGame::IIDXGame() : Game("Beatmania IIDX") {
|
IIDXGame::IIDXGame() : Game("Beatmania IIDX") {
|
||||||
logger::hook_add(log_hook, this);
|
logger::hook_add(log_hook, this);
|
||||||
}
|
}
|
||||||
@@ -311,13 +325,16 @@ namespace games::iidx {
|
|||||||
memcpy(settings2.interface_detail, interface_detail2, sizeof(interface_detail2));
|
memcpy(settings2.interface_detail, interface_detail2, sizeof(interface_detail2));
|
||||||
setupapihook_add(settings2);
|
setupapihook_add(settings2);
|
||||||
|
|
||||||
// IIDX 25-27 BIO2 BI2A input device
|
// IIDX 25+ BIO2 BI2A input device
|
||||||
devicehook_add(new IIDXFMSerialHandle());
|
devicehook_add(new IIDXFMSerialHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
// check for new I/O DLL
|
// check for new I/O DLL
|
||||||
auto aio = libutils::try_library("libaio.dll");
|
auto aio = libutils::try_library("libaio.dll");
|
||||||
if (aio != nullptr) {
|
if (aio != nullptr) {
|
||||||
|
HAS_LIBAIO = true;
|
||||||
|
|
||||||
// check TDJ mode
|
// check TDJ mode
|
||||||
TDJ_MODE |= fileutils::text_read("C:\\000rom.txt") == "TDJ-JA";
|
TDJ_MODE |= fileutils::text_read("C:\\000rom.txt") == "TDJ-JA";
|
||||||
@@ -379,6 +396,8 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
if (hooks::audio::ENABLED) {
|
if (hooks::audio::ENABLED) {
|
||||||
apply_audio_hacks();
|
apply_audio_hacks();
|
||||||
} else {
|
} else {
|
||||||
@@ -407,6 +426,14 @@ namespace games::iidx {
|
|||||||
init_legacy_camera_hook(FLIP_CAMS);
|
init_legacy_camera_hook(FLIP_CAMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
|
if (TDJ_MODE) {
|
||||||
|
sysutils::hook_EnumDisplayDevicesA();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// init cfgmgr32 hooks
|
// init cfgmgr32 hooks
|
||||||
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
||||||
}
|
}
|
||||||
@@ -438,7 +465,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// windowed subscreen, enabled by default, unless turned off by user
|
// windowed subscreen, enabled by default, unless turned off by user
|
||||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
|
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool() && TDJ_MODE) {
|
||||||
GRAPHICS_IIDX_WSUB = true;
|
GRAPHICS_IIDX_WSUB = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +484,7 @@ namespace games::iidx {
|
|||||||
"!!! please do the following instead: !!!\n"
|
"!!! please do the following instead: !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! Revert your changes to XML file so it says !!!\n"
|
"!!! Revert your changes to XML file so it says !!!\n"
|
||||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
||||||
"!!! in command line !!!\n"
|
"!!! in command line !!!\n"
|
||||||
@@ -470,31 +497,40 @@ namespace games::iidx {
|
|||||||
log_fatal("iidx", "BAD MODEL NAME ERROR - TDJ specified, must be LDJ instead");
|
log_fatal("iidx", "BAD MODEL NAME ERROR - TDJ specified, must be LDJ instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
// check -monitor + TDJ mode
|
// check -dxmainadapter + TDJ mode
|
||||||
if (!GRAPHICS_WINDOWED &&
|
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && TDJ_MODE) {
|
||||||
options->at(launcher::Options::DisplayAdapter).is_active() &&
|
IIDX_TDJ_MONITOR_WARNING = true;
|
||||||
TDJ_MODE) {
|
|
||||||
log_warning(
|
log_warning(
|
||||||
"iidx",
|
"iidx",
|
||||||
"\n\n!!! using -monitor option with TDJ is NOT recommended due to known !!!\n"
|
"\n\n"
|
||||||
"!!! compatibility issues with the game !!!\n"
|
"!!! using -dxmainadapter option with TDJ is NOT recommended !!!\n"
|
||||||
|
"!!! due to known compatibility issues with the game !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! * game may launch in wrong resolution or refresh rate !!!\n"
|
"!!! * game may launch in wrong resolution or refresh rate !!!\n"
|
||||||
"!!! * touch / mouse input may stop working in subscreen / overlay !!!\n"
|
"!!! * touch / mouse input may stop working in subscreen / overlay !!!\n"
|
||||||
"!!! !!!\n"
|
"!!! !!!\n"
|
||||||
"!!! recommendation is to NOT use -monitor and instead set the !!!\n"
|
"!!! recommendation is to use the Change Main Monitor (-mainmonitor) !!!\n"
|
||||||
"!!! primary monitor in Windows settings before launching the game !!!\n\n"
|
"!!! option instead of -dxmainadapter !!!\n\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
"-monitor option is NOT recommended when running with TDJ mode",
|
"-dxmainadapter option is NOT recommended when running with TDJ mode",
|
||||||
" due to known compatibility issues with the game:",
|
" due to known compatibility issues with the game:",
|
||||||
" * game may launch in wrong resolution or refresh rate",
|
" * game may launch in wrong resolution or refresh rate",
|
||||||
" * touch / mouse input may stop working in subscreen / overlay",
|
" * touch / mouse input may stop working in subscreen / overlay",
|
||||||
" recommended fix is to NOT use -monitor and instead set the primary",
|
" use Change Main Monitor (-mainmonitor) option instead"
|
||||||
" monitor in Windows settings before launching the game"
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// socd
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::Neutral;
|
||||||
|
if (options->at(launcher::Options::IIDXDigitalTTSocd).is_active()) {
|
||||||
|
if (options->at(launcher::Options::IIDXDigitalTTSocd).value_text() == "last") {
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||||
|
} else if (options->at(launcher::Options::IIDXDigitalTTSocd).value_text() == "first") {
|
||||||
|
socd::ALGORITHM = socd::SocdAlgorithm::PreferFirst;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IIDXGame::detach() {
|
void IIDXGame::detach() {
|
||||||
@@ -718,13 +754,12 @@ namespace games::iidx {
|
|||||||
bool ttpm_alt = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
bool ttpm_alt = GameAPI::Buttons::getState(RI_MGR, buttons.at(
|
||||||
player != 0 ? Buttons::P2_TTPlusMinusAlt : Buttons::P1_TTPlusMinusAlt));
|
player != 0 ? Buttons::P2_TTPlusMinusAlt : Buttons::P1_TTPlusMinusAlt));
|
||||||
|
|
||||||
// TT+
|
const auto tt_socd = socd::socd_clean(player, ttm, ttp, get_performance_milliseconds());
|
||||||
if (ttp)
|
if (tt_socd == socd::SocdCW) {
|
||||||
IIDXIO_TT_STATE[player] += change;
|
IIDXIO_TT_STATE[player] += change;
|
||||||
|
} else if (tt_socd == socd::SocdCCW) {
|
||||||
// TT-
|
|
||||||
if (ttm)
|
|
||||||
IIDXIO_TT_STATE[player] -= change;
|
IIDXIO_TT_STATE[player] -= change;
|
||||||
|
}
|
||||||
|
|
||||||
// TT+/-
|
// TT+/-
|
||||||
bool ttpm_rising_edge = !IIDXIO_TT_PRESSED[player] && ttpm;
|
bool ttpm_rising_edge = !IIDXIO_TT_PRESSED[player] && ttpm;
|
||||||
@@ -748,7 +783,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return higher 8 bit
|
// return higher 8 bit
|
||||||
return (uint8_t) (ret_value >> 2);
|
return (uint8_t)(ret_value >> 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char get_slider(uint8_t slider) {
|
unsigned char get_slider(uint8_t slider) {
|
||||||
@@ -794,7 +829,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_tdj_fhd() {
|
bool is_tdj_fhd() {
|
||||||
return TDJ_MODE && avs::game::is_ext(2022101900, MAXINT);
|
return TDJ_MODE && avs::game::is_ext(2022101900, INT_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_audio_hacks() {
|
void apply_audio_hacks() {
|
||||||
@@ -860,12 +895,12 @@ namespace games::iidx {
|
|||||||
|
|
||||||
// patch iidx32+ for asio compatibility
|
// patch iidx32+ for asio compatibility
|
||||||
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
||||||
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
||||||
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
||||||
if (avs::game::is_ext(2024090100, MAXINT) &&
|
if (avs::game::is_ext(2024090100, INT_MAX) &&
|
||||||
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
||||||
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
||||||
|
|
||||||
// in iidx32 final:
|
// in iidx32 final:
|
||||||
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
||||||
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
|
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
|
||||||
@@ -882,8 +917,7 @@ namespace games::iidx {
|
|||||||
log_warning(
|
log_warning(
|
||||||
"iidx",
|
"iidx",
|
||||||
"Failed to apply ASIO compatibility fix for iidx32+. "
|
"Failed to apply ASIO compatibility fix for iidx32+. "
|
||||||
"Unless patches are applied, your ASIO device ({}) may hang or fail to work",
|
"Unless patches are applied, your ASIO device may hang or fail to work");
|
||||||
ASIO_DRIVER->c_str());
|
|
||||||
} else {
|
} else {
|
||||||
log_info(
|
log_info(
|
||||||
"iidx",
|
"iidx",
|
||||||
@@ -895,4 +929,54 @@ namespace games::iidx {
|
|||||||
#endif
|
#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 {
|
namespace games::iidx {
|
||||||
|
|
||||||
|
enum class iidx_aio_emulation_state {
|
||||||
|
unknown,
|
||||||
|
bi2a_com2,
|
||||||
|
bi2x_hook
|
||||||
|
};
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
|
|
||||||
extern bool FLIP_CAMS;
|
extern bool FLIP_CAMS;
|
||||||
@@ -33,10 +39,12 @@ namespace games::iidx {
|
|||||||
extern char IIDXIO_LED_TICKER[10];
|
extern char IIDXIO_LED_TICKER[10];
|
||||||
extern bool IIDXIO_LED_TICKER_READONLY;
|
extern bool IIDXIO_LED_TICKER_READONLY;
|
||||||
extern std::mutex IIDX_LED_TICKER_LOCK;
|
extern std::mutex IIDX_LED_TICKER_LOCK;
|
||||||
|
extern bool IIDX_TDJ_MONITOR_WARNING;
|
||||||
|
|
||||||
constexpr int IIDX_TAPELED_TOTAL = 17;
|
constexpr int IIDX_TAPELED_TOTAL = 17;
|
||||||
// data mapping
|
// data mapping
|
||||||
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
||||||
|
extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE;
|
||||||
|
|
||||||
class IIDXGame : public games::Game {
|
class IIDXGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
@@ -62,4 +70,6 @@ namespace games::iidx {
|
|||||||
const char* get_16seg();
|
const char* get_16seg();
|
||||||
bool is_tdj_fhd();
|
bool is_tdj_fhd();
|
||||||
void apply_audio_hacks();
|
void apply_audio_hacks();
|
||||||
|
|
||||||
|
void update_io_emulation_state(iidx_aio_emulation_state state);
|
||||||
}
|
}
|
||||||
|
|||||||
+109
-106
@@ -63,16 +63,17 @@ std::vector<Analog> &games::iidx::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Beatmania IIDX");
|
analogs = GameAPI::Analogs::getAnalogs("Beatmania IIDX");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using namespace GameAPI::Analogs;
|
||||||
&analogs,
|
|
||||||
"Turntable P1",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Turntable P2",
|
{ "Turntable P1", AnalogType::Circular },
|
||||||
"VEFX",
|
{ "Turntable P2", AnalogType::Circular },
|
||||||
"Low-EQ",
|
{ "VEFX", AnalogType::LinearPositive },
|
||||||
"Hi-EQ",
|
{ "Low-EQ", AnalogType::LinearPositive },
|
||||||
"Filter",
|
{ "Hi-EQ", AnalogType::LinearPositive },
|
||||||
"Play Volume"
|
{ "Filter", AnalogType::LinearPositive },
|
||||||
);
|
{ "Play Volume", AnalogType::LinearPositive }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return analogs;
|
return analogs;
|
||||||
}
|
}
|
||||||
@@ -83,103 +84,105 @@ std::vector<Light> &games::iidx::get_lights() {
|
|||||||
if (lights.empty()) {
|
if (lights.empty()) {
|
||||||
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
lights = GameAPI::Lights::getLights("Beatmania IIDX");
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
GameAPI::Lights::sortLightsWithCategory(
|
||||||
&lights,
|
&lights,
|
||||||
"P1 1",
|
{
|
||||||
"P1 2",
|
{"Common", "P1 1"},
|
||||||
"P1 3",
|
{"Common", "P1 2"},
|
||||||
"P1 4",
|
{"Common", "P1 3"},
|
||||||
"P1 5",
|
{"Common", "P1 4"},
|
||||||
"P1 6",
|
{"Common", "P1 5"},
|
||||||
"P1 7",
|
{"Common", "P1 6"},
|
||||||
"P2 1",
|
{"Common", "P1 7"},
|
||||||
"P2 2",
|
{"Common", "P2 1"},
|
||||||
"P2 3",
|
{"Common", "P2 2"},
|
||||||
"P2 4",
|
{"Common", "P2 3"},
|
||||||
"P2 5",
|
{"Common", "P2 4"},
|
||||||
"P2 6",
|
{"Common", "P2 5"},
|
||||||
"P2 7",
|
{"Common", "P2 6"},
|
||||||
"P1 Start",
|
{"Common", "P2 7"},
|
||||||
"P2 Start",
|
{"Common", "P1 Start"},
|
||||||
"VEFX",
|
{"Common", "P2 Start"},
|
||||||
"Effect",
|
{"Common", "VEFX"},
|
||||||
"Spot Light 1",
|
{"Common", "Effect"},
|
||||||
"Spot Light 2",
|
{"DX", "Spot Light 1"},
|
||||||
"Spot Light 3",
|
{"DX", "Spot Light 2"},
|
||||||
"Spot Light 4",
|
{"DX", "Spot Light 3"},
|
||||||
"Spot Light 5",
|
{"DX", "Spot Light 4"},
|
||||||
"Spot Light 6",
|
{"DX", "Spot Light 5"},
|
||||||
"Spot Light 7",
|
{"DX", "Spot Light 6"},
|
||||||
"Spot Light 8",
|
{"DX", "Spot Light 7"},
|
||||||
"Neon Lamp",
|
{"DX", "Spot Light 8"},
|
||||||
"Woofer R",
|
{"DX", "Neon Lamp"},
|
||||||
"Woofer G",
|
{"Lightning Model", "Woofer R"},
|
||||||
"Woofer B",
|
{"Lightning Model", "Woofer G"},
|
||||||
"IC Card Reader P1 R",
|
{"Lightning Model", "Woofer B"},
|
||||||
"IC Card Reader P1 G",
|
{"Lightning Model", "IC Card Reader P1 R"},
|
||||||
"IC Card Reader P1 B",
|
{"Lightning Model", "IC Card Reader P1 G"},
|
||||||
"IC Card Reader P2 R",
|
{"Lightning Model", "IC Card Reader P1 B"},
|
||||||
"IC Card Reader P2 G",
|
{"Lightning Model", "IC Card Reader P2 R"},
|
||||||
"IC Card Reader P2 B",
|
{"Lightning Model", "IC Card Reader P2 G"},
|
||||||
"Turntable P1 R",
|
{"Lightning Model", "IC Card Reader P2 B"},
|
||||||
"Turntable P1 G",
|
{"Lightning Model", "Turntable P1 R"},
|
||||||
"Turntable P1 B",
|
{"Lightning Model", "Turntable P1 G"},
|
||||||
"Turntable P2 R",
|
{"Lightning Model", "Turntable P1 B"},
|
||||||
"Turntable P2 G",
|
{"Lightning Model", "Turntable P2 R"},
|
||||||
"Turntable P2 B",
|
{"Lightning Model", "Turntable P2 G"},
|
||||||
"Turntable P1 Resistance",
|
{"Lightning Model", "Turntable P2 B"},
|
||||||
"Turntable P2 Resistance",
|
{"Lightning Model", "Turntable P1 Resistance"},
|
||||||
"Stage Left Avg R",
|
{"Lightning Model", "Turntable P2 Resistance"},
|
||||||
"Stage Left Avg G",
|
{"Lightning Model", "Stage Left Avg R"},
|
||||||
"Stage Left Avg B",
|
{"Lightning Model", "Stage Left Avg G"},
|
||||||
"Stage Right Avg R",
|
{"Lightning Model", "Stage Left Avg B"},
|
||||||
"Stage Right Avg G",
|
{"Lightning Model", "Stage Right Avg R"},
|
||||||
"Stage Right Avg B",
|
{"Lightning Model", "Stage Right Avg G"},
|
||||||
"Cabinet Left Avg R",
|
{"Lightning Model", "Stage Right Avg B"},
|
||||||
"Cabinet Left Avg G",
|
{"Lightning Model", "Cabinet Left Avg R"},
|
||||||
"Cabinet Left Avg B",
|
{"Lightning Model", "Cabinet Left Avg G"},
|
||||||
"Cabinet Right Avg R",
|
{"Lightning Model", "Cabinet Left Avg B"},
|
||||||
"Cabinet Right Avg G",
|
{"Lightning Model", "Cabinet Right Avg R"},
|
||||||
"Cabinet Right Avg B",
|
{"Lightning Model", "Cabinet Right Avg G"},
|
||||||
"Control Panel Under Avg R",
|
{"Lightning Model", "Cabinet Right Avg B"},
|
||||||
"Control Panel Under Avg G",
|
{"Lightning Model", "Control Panel Under Avg R"},
|
||||||
"Control Panel Under Avg B",
|
{"Lightning Model", "Control Panel Under Avg G"},
|
||||||
"Ceiling Left Avg R",
|
{"Lightning Model", "Control Panel Under Avg B"},
|
||||||
"Ceiling Left Avg G",
|
{"Lightning Model", "Ceiling Left Avg R"},
|
||||||
"Ceiling Left Avg B",
|
{"Lightning Model", "Ceiling Left Avg G"},
|
||||||
"Title Left Avg R",
|
{"Lightning Model", "Ceiling Left Avg B"},
|
||||||
"Title Left Avg G",
|
{"Lightning Model", "Title Left Avg R"},
|
||||||
"Title Left Avg B",
|
{"Lightning Model", "Title Left Avg G"},
|
||||||
"Title Right Avg R",
|
{"Lightning Model", "Title Left Avg B"},
|
||||||
"Title Right Avg G",
|
{"Lightning Model", "Title Right Avg R"},
|
||||||
"Title Right Avg B",
|
{"Lightning Model", "Title Right Avg G"},
|
||||||
"Ceiling Right Avg R",
|
{"Lightning Model", "Title Right Avg B"},
|
||||||
"Ceiling Right Avg G",
|
{"Lightning Model", "Ceiling Right Avg R"},
|
||||||
"Ceiling Right Avg B",
|
{"Lightning Model", "Ceiling Right Avg G"},
|
||||||
"Touch Panel Left Avg R",
|
{"Lightning Model", "Ceiling Right Avg B"},
|
||||||
"Touch Panel Left Avg G",
|
{"Lightning Model", "Touch Panel Left Avg R"},
|
||||||
"Touch Panel Left Avg B",
|
{"Lightning Model", "Touch Panel Left Avg G"},
|
||||||
"Touch Panel Right Avg R",
|
{"Lightning Model", "Touch Panel Left Avg B"},
|
||||||
"Touch Panel Right Avg G",
|
{"Lightning Model", "Touch Panel Right Avg R"},
|
||||||
"Touch Panel Right Avg B",
|
{"Lightning Model", "Touch Panel Right Avg G"},
|
||||||
"Side Panel Left Inner Avg R",
|
{"Lightning Model", "Touch Panel Right Avg B"},
|
||||||
"Side Panel Left Inner Avg G",
|
{"Lightning Model", "Side Panel Left Inner Avg R"},
|
||||||
"Side Panel Left Inner Avg B",
|
{"Lightning Model", "Side Panel Left Inner Avg G"},
|
||||||
"Side Panel Left Outer Avg R",
|
{"Lightning Model", "Side Panel Left Inner Avg B"},
|
||||||
"Side Panel Left Outer Avg G",
|
{"Lightning Model", "Side Panel Left Outer Avg R"},
|
||||||
"Side Panel Left Outer Avg B",
|
{"Lightning Model", "Side Panel Left Outer Avg G"},
|
||||||
"Side Panel Left Avg R",
|
{"Lightning Model", "Side Panel Left Outer Avg B"},
|
||||||
"Side Panel Left Avg G",
|
{"Lightning Model", "Side Panel Left Avg R"},
|
||||||
"Side Panel Left Avg B",
|
{"Lightning Model", "Side Panel Left Avg G"},
|
||||||
"Side Panel Right Outer Avg R",
|
{"Lightning Model", "Side Panel Left Avg B"},
|
||||||
"Side Panel Right Outer Avg G",
|
{"Lightning Model", "Side Panel Right Outer Avg R"},
|
||||||
"Side Panel Right Outer Avg B",
|
{"Lightning Model", "Side Panel Right Outer Avg G"},
|
||||||
"Side Panel Right Inner Avg R",
|
{"Lightning Model", "Side Panel Right Outer Avg B"},
|
||||||
"Side Panel Right Inner Avg G",
|
{"Lightning Model", "Side Panel Right Inner Avg R"},
|
||||||
"Side Panel Right Inner Avg B",
|
{"Lightning Model", "Side Panel Right Inner Avg G"},
|
||||||
"Side Panel Right Avg R",
|
{"Lightning Model", "Side Panel Right Inner Avg B"},
|
||||||
"Side Panel Right Avg G",
|
{"Lightning Model", "Side Panel Right Avg R"},
|
||||||
"Side Panel Right Avg B"
|
{"Lightning Model", "Side Panel Right Avg G"},
|
||||||
|
{"Lightning Model", "Side Panel Right Avg B"}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#include "games/iidx/local_camera.h"
|
#include "games/iidx/local_camera.h"
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "mf_wrappers.h"
|
#include "mf_wrappers.h"
|
||||||
|
|
||||||
@@ -128,7 +129,7 @@ namespace games::iidx {
|
|||||||
// TODO: Color space conversion
|
// TODO: Color space conversion
|
||||||
// if (SUCCEEDED(hr)) {
|
// if (SUCCEEDED(hr)) {
|
||||||
// hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE);
|
// hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE);
|
||||||
// }
|
// }
|
||||||
// if (SUCCEEDED(hr)) {
|
// if (SUCCEEDED(hr)) {
|
||||||
// hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, FALSE);
|
// hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, FALSE);
|
||||||
// }
|
// }
|
||||||
@@ -189,11 +190,11 @@ namespace games::iidx {
|
|||||||
&pType
|
&pType
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
if (hr != MF_E_NO_MORE_TYPES) {
|
if (hr != MF_E_NO_MORE_TYPES) {
|
||||||
log_warning("iidx:camhook", "[{}] Cannot get media type {} {:#x}", m_name, i, (ULONG)hr);
|
log_warning("iidx:camhook", "[{}] Cannot get media type {} {:#x}", m_name, i, (ULONG)hr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = TryMediaType(pType, &bestWidth, &bestFrameRate);
|
hr = TryMediaType(pType, &bestWidth, &bestFrameRate);
|
||||||
@@ -246,7 +247,7 @@ namespace games::iidx {
|
|||||||
pSelectedMediaType = m_pAutoMediaType;
|
pSelectedMediaType = m_pAutoMediaType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
hr = ChangeMediaType(pSelectedMediaType);
|
hr = ChangeMediaType(pSelectedMediaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +282,7 @@ namespace games::iidx {
|
|||||||
m_cameraHeight = info.height;
|
m_cameraHeight = info.height;
|
||||||
UpdateDrawRect();
|
UpdateDrawRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,10 +396,11 @@ namespace games::iidx {
|
|||||||
|
|
||||||
m_device->ColorFill(m_pDestSurf, &targetRect, D3DCOLOR_XRGB(0, 0, 0));
|
m_device->ColorFill(m_pDestSurf, &targetRect, D3DCOLOR_XRGB(0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IIDXLocalCamera::CreateThread() {
|
void IIDXLocalCamera::CreateThread() {
|
||||||
// Create thread
|
// Create thread
|
||||||
m_drawThread = new std::thread([this]() {
|
m_drawThread = new std::thread([this]() {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
|
||||||
double accumulator = 0.0;
|
double accumulator = 0.0;
|
||||||
@@ -411,8 +413,8 @@ namespace games::iidx {
|
|||||||
if (accumulator > 1.0) {
|
if (accumulator > 1.0) {
|
||||||
accumulator -= 1.0;
|
accumulator -= 1.0;
|
||||||
floorFrameTimeMicroSec += 1;
|
floorFrameTimeMicroSec += 1;
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(floorFrameTimeMicroSec));
|
timer.sleep(std::chrono::microseconds(floorFrameTimeMicroSec));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -430,12 +432,12 @@ namespace games::iidx {
|
|||||||
|
|
||||||
log_misc("iidx:camhook", "[{}] Init camera control", m_name);
|
log_misc("iidx:camhook", "[{}] Init camera control", m_name);
|
||||||
|
|
||||||
hr = m_pSource->QueryInterface(IID_IAMCameraControl, (void**)&m_pCameraControl);
|
hr = m_pSource->QueryInterface(IID_IAMCameraControl, (void**)&m_pCameraControl);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
// The device does not support IAMCameraControl
|
// The device does not support IAMCameraControl
|
||||||
log_warning("iidx:camhook", "[{}] Camera control not supported", m_name);
|
log_warning("iidx:camhook", "[{}] Camera control not supported", m_name);
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < CAMERA_CONTROL_PROP_SIZE; i++) {
|
for (size_t i = 0; i < CAMERA_CONTROL_PROP_SIZE; i++) {
|
||||||
long minValue = 0;
|
long minValue = 0;
|
||||||
@@ -472,9 +474,9 @@ namespace games::iidx {
|
|||||||
CameraControlProp prop = m_controlProps.at(i);
|
CameraControlProp prop = m_controlProps.at(i);
|
||||||
|
|
||||||
log_misc(
|
log_misc(
|
||||||
"iidx:camhook", "[{}] >> {} range=({}, {}) default={} delta={} dFlags={} value={} vFlags={}",
|
"iidx:camhook", "[{}] >> {} range=({}, {}) default={} delta={} dFlags={} value={} vFlags={}",
|
||||||
m_name,
|
m_name,
|
||||||
CAMERA_CONTROL_LABELS[i],
|
CAMERA_CONTROL_LABELS[i],
|
||||||
prop.minValue, prop.maxValue,
|
prop.minValue, prop.maxValue,
|
||||||
prop.defaultValue,
|
prop.defaultValue,
|
||||||
prop.delta,
|
prop.delta,
|
||||||
@@ -570,9 +572,9 @@ namespace games::iidx {
|
|||||||
|
|
||||||
// Get frame rate
|
// Get frame rate
|
||||||
hr = MFGetAttributeRatio(
|
hr = MFGetAttributeRatio(
|
||||||
pType,
|
pType,
|
||||||
MF_MT_FRAME_RATE,
|
MF_MT_FRAME_RATE,
|
||||||
(UINT32*)&frameRate.Numerator,
|
(UINT32*)&frameRate.Numerator,
|
||||||
(UINT32*)&frameRate.Denominator
|
(UINT32*)&frameRate.Denominator
|
||||||
);
|
);
|
||||||
if (FAILED(hr)) { goto done; }
|
if (FAILED(hr)) { goto done; }
|
||||||
@@ -580,7 +582,7 @@ namespace games::iidx {
|
|||||||
info.frameRate = frameRate.Numerator / frameRate.Denominator;
|
info.frameRate = frameRate.Numerator / frameRate.Denominator;
|
||||||
|
|
||||||
info.description = fmt::format(
|
info.description = fmt::format(
|
||||||
"{}x{} @{}FPS {}",
|
"{}x{} @{}FPS {}",
|
||||||
info.width,
|
info.width,
|
||||||
info.height,
|
info.height,
|
||||||
(int)info.frameRate,
|
(int)info.frameRate,
|
||||||
@@ -606,7 +608,7 @@ namespace games::iidx {
|
|||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return values:
|
* Return values:
|
||||||
* S_OK: this is a "better" media type than the existing one
|
* S_OK: this is a "better" media type than the existing one
|
||||||
* S_FALSE: valid media type, but not "better"
|
* S_FALSE: valid media type, but not "better"
|
||||||
@@ -620,17 +622,17 @@ namespace games::iidx {
|
|||||||
|
|
||||||
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("iidx:camhook", "[{}] Failed to get subtype: {:#x}", m_name, (ULONG)hr);
|
log_warning("iidx:camhook", "[{}] Failed to get subtype: {:#x}", m_name, (ULONG)hr);
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
|
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("iidx:camhook", "[{}] Failed to get frame size: {:#x}", m_name, (ULONG)hr);
|
log_warning("iidx:camhook", "[{}] Failed to get frame size: {:#x}", m_name, (ULONG)hr);
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only support format with converters
|
// Only support format with converters
|
||||||
// TODO: verify conversion support with DXVA
|
// TODO: verify conversion support with DXVA
|
||||||
if (subtype != MFVideoFormat_YUY2 && subtype != MFVideoFormat_NV12) {
|
if (subtype != MFVideoFormat_YUY2 && subtype != MFVideoFormat_NV12) {
|
||||||
@@ -639,12 +641,12 @@ namespace games::iidx {
|
|||||||
|
|
||||||
// Frame rate
|
// Frame rate
|
||||||
hr = MFGetAttributeRatio(
|
hr = MFGetAttributeRatio(
|
||||||
pType,
|
pType,
|
||||||
MF_MT_FRAME_RATE,
|
MF_MT_FRAME_RATE,
|
||||||
(UINT32*)&frameRate.Numerator,
|
(UINT32*)&frameRate.Numerator,
|
||||||
(UINT32*)&frameRate.Denominator
|
(UINT32*)&frameRate.Denominator
|
||||||
);
|
);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("iidx:camhook", "[{}] Failed to get frame rate: {:#x}", m_name, (ULONG)hr);
|
log_warning("iidx:camhook", "[{}] Failed to get frame rate: {:#x}", m_name, (ULONG)hr);
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
@@ -667,7 +669,7 @@ namespace games::iidx {
|
|||||||
// Check if this format has better resolution / frame rate
|
// Check if this format has better resolution / frame rate
|
||||||
if ((width > *pBestWidth) || (width >= (UINT32)TARGET_SURFACE_WIDTH && frameRateValue >= *pBestFrameRate)) {
|
if ((width > *pBestWidth) || (width >= (UINT32)TARGET_SURFACE_WIDTH && frameRateValue >= *pBestFrameRate)) {
|
||||||
// log_misc(
|
// log_misc(
|
||||||
// "iidx:camhook", "Better media type {} ({}x{}) @({} FPS)",
|
// "iidx:camhook", "Better media type {} ({}x{}) @({} FPS)",
|
||||||
// GetVideoFormatName(subtype),
|
// GetVideoFormatName(subtype),
|
||||||
// width,
|
// width,
|
||||||
// height,
|
// height,
|
||||||
@@ -730,7 +732,7 @@ namespace games::iidx {
|
|||||||
|
|
||||||
HRESULT IIDXLocalCamera::FlushDrawCommands() {
|
HRESULT IIDXLocalCamera::FlushDrawCommands() {
|
||||||
IDirect3DQuery9* pEventQuery = nullptr;
|
IDirect3DQuery9* pEventQuery = nullptr;
|
||||||
// It is necessary to flush the command queue
|
// It is necessary to flush the command queue
|
||||||
// or the data is not ready for the receiver to read.
|
// or the data is not ready for the receiver to read.
|
||||||
// Adapted from : https://msdn.microsoft.com/en-us/library/windows/desktop/bb172234%28v=vs.85%29.aspx
|
// Adapted from : https://msdn.microsoft.com/en-us/library/windows/desktop/bb172234%28v=vs.85%29.aspx
|
||||||
// Also see : http://www.ogre3d.org/forums/viewtopic.php?f=5&t=50486
|
// Also see : http://www.ogre3d.org/forums/viewtopic.php?f=5&t=50486
|
||||||
@@ -784,8 +786,8 @@ namespace games::iidx {
|
|||||||
for (int y = 0; y < TARGET_SURFACE_HEIGHT; y++) {
|
for (int y = 0; y < TARGET_SURFACE_HEIGHT; y++) {
|
||||||
for (int x = 0; x < TARGET_SURFACE_WIDTH; x++) {
|
for (int x = 0; x < TARGET_SURFACE_WIDTH; x++) {
|
||||||
memcpy(
|
memcpy(
|
||||||
pDest + x * pixelSize,
|
pDest + x * pixelSize,
|
||||||
pSrc + (flip_h ? (TARGET_SURFACE_WIDTH - x - 1) : x) * pixelSize,
|
pSrc + (flip_h ? (TARGET_SURFACE_WIDTH - x - 1) : x) * pixelSize,
|
||||||
pixelSize
|
pixelSize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
#include <dxva2api.h>
|
#include <dxva2api.h>
|
||||||
@@ -89,7 +89,7 @@ namespace games::iidx {
|
|||||||
protected:
|
protected:
|
||||||
virtual ~IIDXLocalCamera() {};
|
virtual ~IIDXLocalCamera() {};
|
||||||
|
|
||||||
ULONG m_nRefCount;
|
LONG m_nRefCount;
|
||||||
CRITICAL_SECTION m_critsec;
|
CRITICAL_SECTION m_critsec;
|
||||||
|
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user