Compare commits

...

32 Commits

Author SHA1 Message Date
bicarus-dev 4e86cb16a2 cfg: tweaks to analog binding UI (#434)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add the raw device handle string to tooltip.
Add `Reset` button that clears all values.

## Testing
manual testing
2025-12-06 01:47:00 -08:00
bicarus-dev 76f401de95 iidx: rework wasapi/asio detection for iidx33+ (#433)
## Link to GitHub Issue, if one exists
Fixes #432 

## Description of change
Newer versions of iidx33+ reintroduces `SOUND_OUTPUT_DEVICE` env var.
iidx module must account for its return

## Testing
2025-12-05 15:33:09 -08:00
bicarus-dev 5947779502 iidx,sdvx: warn user when using -monitor with TDJ/UFC mode (#431)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Warn user via warning message in log + auto-troubleshooter entry when
-monitor option is used in full screen with TDJ and UFC mode.

This covers yet another case where TDJ subscreen does not respond to
mouse clicks (because the game expects them to come through the primary
display). For UFC, -monitor usually results in a failure to create DX
adapter.

## Testing
Manual verification with TDJ/UFC modes and two monitors.
2025-12-04 21:57:36 -08:00
bicarus-dev 61c97ccaab net: ignore -ea if -url is set (#430)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Ignore -ea if -url is provided

## Testing
2025-12-01 16:04:32 -08:00
bicarus-dev 12d6a88c60 iidx: turn off camera access by default (#428)
## Link to GitHub Issue, if one exists
For #345 
Similar idea as https://github.com/spice2x/spice2x.github.io/pull/350

## Description of change
Disable camera access by default by setting `CONNECT_CAMERA` environment
var to 0 before launching. This was one of those "required" things
people clicked on, but now it's done automatically to reduce friction
for new users.

On cab setups (i.e., running without -iidx module), cameras will remain
on, unless user overrides it.

On franken setups (partial I/O) - user will need to specify what they
want via the new option, we can't guess correctly.

## Testing
2025-11-30 16:13:34 -08:00
bicarus-dev 984b41a1ae cfg: fix combo boxes (#429)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Combo boxes for options were not highlighting the currently selected
item. Also, when there were no description provided for an option, it
was still showing the empty `()` in the selected value.

## Testing
2025-11-30 01:59:25 -08:00
duel0213 d06b8bd731 mfc: fix 2025 version touch (#426)
## Link to GitHub Issue, if one exists
n/a

## Description of change
HG cabinet uses wintouch and it requires TOUCHEVENTF_PRIMARY flag in
order to accept touch inputs.
Added wintouchemu on game hook and TOUCHEVENTF_PRIMARY flag on
wintouchemu.
 
## Testing
The game boots into test menu, touch now works. (tested in I/O TEST ->
TOUCH PANEL CHECK with '-s' flag)
TDJ subscreen overlay and nostalgia seems works fine regardless added
TOUCHEVENTF_PRIMARY flag but more testing required.
2025-11-25 17:14:07 -08:00
bicarus-dev 0dad96b876 check for standalone cfg for fatal options 2025-11-23 09:36:38 -08:00
bicarus-dev def296095b reword log message to avoid confusion 2025-11-22 16:45:27 -08:00
bicarus-dev 750b1fea80 patches: disable patches tab for unity games (#425)
## Description of change
To redirect confused users why no one is developing a remote patch
server for PolCho

## Testing
checked spicecfg in working vs non-working games
2025-11-17 19:16:19 -08:00
aixxe 4c87078807 Refactor IIDX camhook texture replacement (#424)
## Link to GitHub Issue, if one exists
Fixes #393 

## Description of change
- Fixes compatibility with IIDX 33
- Adjusts the pattern scan for "hook A" to be compatible with IIDX 27
- Refactors texture replacement to use Camera::CCameraManager2, removing
the need for a second hook

Related: https://github.com/aixxe/2dxcamhook/compare/0.4.0.0...0.5.0.0

## Testing
Booted with `-iidxcamhook` on IIDX 27, 28, 29, 30, 31, 32 & 33
Confirmed both cameras were visible in I/O test, music select settings &
gameplay
2025-11-17 01:21:39 -08:00
bicarus-dev 052698afa3 rawinput: use UTF-8 for device descriptor (#422)
## Link to GitHub Issue, if one exists
n/a

## Description of change
For HID device descriptor, use the "W" version of Windows API, and make
sure UTF-8 is being used throughout since that's the only format IMGUI
supports.

Before this change, non-Latin characters would have shown up as `HID ???
?????` for a mouse, for example.

Due to the way ImGui loads fonts, only the following languages are
supported:

* Simplified Chinese
* Cyrillic
* Japanese
* Korean
* Thai
* Vietnamese

## Testing
Tested various combination of Windows UI language and non-Unicode
language.
2025-11-16 00:08:30 -08:00
bicarus-dev 9e2b04d5db troubleshooter: update link for vcredist (#423)
## Description of change
RIP abbodi1406
2025-11-16 00:07:36 -08:00
bicarus-dev 5641e06a19 iidx: fix kana input broken in subscreen when system language is not Japanese (#421)
## Link to GitHub Issue, if one exists
user-reported

## Description of change
When the system non-Unicode language is not set to Japanese, TDJ
subscreen katakana keyboard was broken (weird behavior with characters
not erasing, ending with corrupt characters, etc)

To fix this, add a hook for `IsDBCSLeadByte`. `IsDBCSLeadByte` assumes
system ACP (which may not be SHIFT_JIS). Instead, redirect bm2dx.dll to
use `IsDBCSLeadByteEx` instead with SHIFT_JIS explicitly specified as a
parameter.

Presumably the game was using IsDBCSLeadByte to manage the keyboard
typing logic.... which is weird because SDVX doesn't do the same.

## Testing
Tested iidx 31 and tried to do searches.
2025-11-10 11:35:46 -08:00
bicarus-dev 88ec6dbcee launcher: hide pcbid when -cmdoverride specified (#420)
## Link to GitHub Issue, if one exists
Fixes #419 

## Description of change
-cmdoverride caused pcbid to be logged in plain text

## Testing
2025-11-07 14:20:37 -08:00
bicarus-dev 1b614c94a7 mfc: remove log spam (#418)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Remove mutex lock/unlock log spam

Are they important? Idk

## Testing
2025-11-06 17:51:39 -08:00
bicarus-dev 37c5913f69 mfc: detect 2025 version (#416)
## Link to GitHub Issue, if one exists
n/a

## Description of change
They moved some DLLs around so MFC wasn't being auto-detected, and I/O
hooks were not being performed. Fix that.

## Testing
The game boots into test menu, some i/o works, touch doesn't, not
playable yet.
2025-11-05 19:23:10 -08:00
wrigglebug 2eca668593 update readme.txt (#415)
please merge this important code
2025-11-02 23:29:48 -08:00
bicarus-dev aec1da9d16 troubleshooter: detect ea3_report_posev error (#413)
## Link to GitHub Issue, if one exists
#345 

## Description of change
When `ea3-pos: ea3_report_posev: no such node:` is detected, add a
deferred error message and guide user to the FAQ.

## Testing
Tested SDVX with the missing XML node.
2025-11-02 14:13:04 -08:00
sp2xdev 548b441f1f update build script 2025-11-01 17:33:40 -07:00
bicarus-dev 98bc285d98 logger: show message popup for log_fatal (#412)
## Link to GitHub Issue, if one exists
#410 

## Description of change
When raising fatal errors via `log_fatal`, also show a modal popup.

Update instances of `log_fatal` where the message was too long to fit.

## Testing
Tested various failure conditions.
2025-11-01 13:41:58 -07:00
bicarus-dev 553c180bee signal: force minimize widnows before showing crash handler dialog (#411)
## Link to GitHub Issue, if one exists
#410 

## Description of change
Forcibly minimize all windows before showing the crash dialog. Without
this, the dialog shows "behind" the game when launching multi-monitor
fullscreen games (TDJ, VM)

## Testing
Tested TDJ and VM in fullscreen.
2025-11-01 02:08:14 -07:00
bicarus-dev 537252440e signal: show Windows popup window on crash (#410)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Show a MessageBox when signal detects a crash.

## Testing
Tested full screen and windowed games.
2025-10-31 20:54:56 -07:00
sp2xdev 13a0877d38 fix up deferred messages formatting 2025-10-28 16:49:14 -07:00
bicarus-dev 50691b79f1 troubleshooter: add cases for audio hook and dx9on12 (#408)
## Link to GitHub Issue, if one exists
#345

## Description of change
Add auto-troubleshooter messages for when audio hooks are disabled, and
when 9on12 is turned on by user.

## Testing
Manual verification
2025-10-28 16:29:49 -07:00
bicarus-dev ad229dabdb update build script (#407)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Create a "full" version of the archive with api and extra binaries. The
normal archive remains in place with just src and stubs.
2025-10-27 23:00:32 -07:00
bicarus-dev bdff33a420 overlay: fix font loading (#406)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Fix font loading - when the code was refactored in #286 I used the wrong
file extension for couple of them.
2025-10-20 00:53:28 -07:00
bicarus-dev 4b21821aa4 Introduce builds for Linux, split archive into release and extras (#405)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add a "Linux" variant of spicecfg/spice/spice64.exe - currently these
are the same as Windows binaries but built without dependency on
WinSCard, which makes it easier to run on WINE.

Split dist archive into two, move api and extras into the new extras
zip.

## Testing
I only checked to see that Linux variants run fine on Windows, and
checked with dumpbin to confirm that the dependency on winscard DLL is
removed. No official support will be provided for Linux builds.
2025-10-16 16:19:10 -07:00
bicarus-dev 6b47b5db50 iidx: set CONNECT_CAMERA env var even when iidx module is not enabled (#404)
## Link to GitHub Issue, if one exists
Fixes #403 

## Description of change
Set CONNECT_CAMERA env var even when iidx module is not enabled, so that
camhook can work without -iidx being set with real IO

## Testing
tested with emulated i/o and real i/o by user (see linked issue)
2025-10-15 13:50:12 -07:00
kazakhstan-sila a10ca5236b add option to remove winscard dependency (#400)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add CMake option to disable SCard functionality. Needed by some builds
of WINE, where distributor opted to not enable scard functionality for
whatever reason

## Testing
Building with `cmake -DWITH_SCARD=OFF` does not link against winscard
Building without this option at all links against winscard
2025-10-13 14:13:51 -07:00
bicarus-dev be2dab9a1a Image duplication for screen resize (#401)
## Link to GitHub Issue, if one exists
#399 

## Description of change
Add image duplication option to screen resize, allowing overflow images
to wrap around the other side.

## Testing
Tested with DDR, IIDX (windowed and full screen TDJ), SDVX (orientation
swap)
2025-10-12 20:34:45 -07:00
AllanCat 02711cdee1 Fixed an issue where ext (release_code) wasn't detected and replaced correctly when using user specified bootstrap.xml path. (#398)
## Link to GitHub Issue, if one exists
#0

## Description of change
The original launcher::detect_bootstrap_release_code() wasn't detecting
user specified bootstrap.xml path, and was causing issues for
patch_manager and other ext check spots prior to ea3 init (as ea3 does
proper path check itself).
I've added the user path check and proper xml parsing from
detect_gameversion().

## Testing
Tested with prop/bootstrap.xml presents.
Tested with prop/bootstrap.xml not presents and -b
/path/to/bootstrap.xml
2025-10-10 21:35:34 -07:00
42 changed files with 812 additions and 221 deletions
+65 -9
View File
@@ -357,12 +357,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
external/cardio/cardio_runner.cpp external/cardio/cardio_runner.cpp
# external misc # external misc
external/scard/scard.cpp
external/stackwalker/stackwalker.cpp external/stackwalker/stackwalker.cpp
external/tinyxml2/tinyxml2.cpp external/tinyxml2/tinyxml2.cpp
external/http-parser/http_parser.c external/http-parser/http_parser.c
external/usbhidusage/usb-hid-usage.c external/usbhidusage/usb-hid-usage.c
external/toojpeg/toojpeg.cpp external/toojpeg/toojpeg.cpp
external/scard/scard.cpp
# games # games
games/game.cpp games/game.cpp
@@ -622,8 +622,9 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOUR
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc) set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES}) add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_spice target_link_libraries(spicetools_spice
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features) PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
target_link_libraries(spicetools_spice PUBLIC winscard)
set_target_properties(spicetools_spice PROPERTIES PREFIX "") set_target_properties(spicetools_spice PROPERTIES PREFIX "")
set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice") set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice")
@@ -637,8 +638,9 @@ endif()
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc) set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES}) add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_spice_laa target_link_libraries(spicetools_spice_laa
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features) PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
target_link_libraries(spicetools_spice_laa PUBLIC winscard)
set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "") set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "")
set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa") set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa")
target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1) target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1)
@@ -647,6 +649,22 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware") set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware")
endif() endif()
# spice_linux.exe
###########
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice_linux ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_spice_linux
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_spice_linux PROPERTIES PREFIX "")
set_target_properties(spicetools_spice_linux PROPERTIES OUTPUT_NAME "spice_linux")
target_compile_definitions(spicetools_spice_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
IF(NOT MSVC)
set_target_properties(spicetools_spice_linux PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
endif()
# spice64.exe # spice64.exe
############# #############
@@ -655,9 +673,9 @@ add_executable(spicetools_spice64 ${SOURCE_FILES} ${RESOURCE_FILES})
# do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break # do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break
target_link_libraries(spicetools_spice64 target_link_libraries(spicetools_spice64
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp mfuuid strmiids dxva2 PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp mfuuid strmiids dxva2
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features) PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
target_link_libraries(spicetools_spice64 PUBLIC winscard)
set_target_properties(spicetools_spice64 PROPERTIES PREFIX "") set_target_properties(spicetools_spice64 PROPERTIES PREFIX "")
set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64") set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64")
target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1) target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1)
@@ -666,6 +684,25 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64") set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif() endif()
# spice64_linux.exe
#############
set(RESOURCE_FILES build/manifest.manifest build/manifest64.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice64_linux ${SOURCE_FILES} ${RESOURCE_FILES})
# do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break
target_link_libraries(spicetools_spice64_linux
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp mfuuid strmiids dxva2
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_spice64_linux PROPERTIES PREFIX "")
set_target_properties(spicetools_spice64_linux PROPERTIES OUTPUT_NAME "spice64_linux")
target_compile_definitions(spicetools_spice64_linux PRIVATE SPICE64=1)
target_compile_definitions(spicetools_spice64_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
IF(NOT MSVC)
set_target_properties(spicetools_spice64_linux PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
# spicecfg.exe # spicecfg.exe
############## ##############
@@ -673,8 +710,9 @@ set(SOURCE_FILES ${SOURCE_FILES} launcher/options.h launcher/options.cpp)
set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc) set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc)
add_executable(spicetools_cfg WIN32 ${SOURCE_FILES} ${RESOURCE_FILES}) add_executable(spicetools_cfg WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_cfg target_link_libraries(spicetools_cfg
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp strmiids PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp strmiids
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features) PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
target_link_libraries(spicetools_cfg PUBLIC winscard)
set_target_properties(spicetools_cfg PROPERTIES PREFIX "") set_target_properties(spicetools_cfg PROPERTIES PREFIX "")
set_target_properties(spicetools_cfg PROPERTIES OUTPUT_NAME "spicecfg") set_target_properties(spicetools_cfg PROPERTIES OUTPUT_NAME "spicecfg")
target_compile_definitions(spicetools_cfg PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1) target_compile_definitions(spicetools_cfg PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
@@ -683,6 +721,24 @@ if(NOT MSVC)
set_target_properties(spicetools_cfg PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") set_target_properties(spicetools_cfg PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
endif() endif()
# spicecfg_linux.exe
##############
set(SOURCE_FILES ${SOURCE_FILES} launcher/options.h launcher/options.cpp)
set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc)
add_executable(spicetools_cfg_linux WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_cfg_linux
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp strmiids
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_cfg_linux PROPERTIES PREFIX "")
set_target_properties(spicetools_cfg_linux PROPERTIES OUTPUT_NAME "spicecfg_linux")
target_compile_definitions(spicetools_cfg_linux PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
target_compile_definitions(spicetools_cfg_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
if(NOT MSVC)
set_target_properties(spicetools_cfg_linux PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
endif()
# stubs # stubs
####### #######
@@ -776,19 +832,19 @@ endif()
#################### ####################
# output config # output config
set_target_properties(spicetools_cfg set_target_properties(spicetools_cfg spicetools_cfg_linux
PROPERTIES PROPERTIES
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_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
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_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
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"
+2 -1
View File
@@ -1503,7 +1503,8 @@ namespace avs {
" * 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.string()) };
log_fatal("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);
} }
} }
+3 -1
View File
@@ -150,7 +150,9 @@ namespace avs {
" * 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.string()) };
log_fatal("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);
} }
} }
+2 -1
View File
@@ -98,7 +98,7 @@ namespace avs {
// ddr gamemdx.dll user error // ddr gamemdx.dll user error
if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") { if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") {
log_fatal( log_warning(
"ddr", "ddr",
"BAD GAME DLL ERROR\n\n" "BAD GAME DLL ERROR\n\n"
"!!! !!!\n" "!!! !!!\n"
@@ -107,6 +107,7 @@ namespace avs {
"!!! remove -exec argument and try again. !!!\n" "!!! remove -exec argument and try again. !!!\n"
"!!! !!!\n" "!!! !!!\n"
); );
log_fatal("ddr", "BAD GAME DLL ERROR (don't use -exec gamemdx.dll, that's the wrong DLL)");
} }
// file not found on disk // file not found on disk
+21 -8
View File
@@ -49,6 +49,7 @@ BUILDDIR_64_RELEASE="./cmake-build-release-64"
BUILDDIR_64_DEBUG="./cmake-build-debug-64" BUILDDIR_64_DEBUG="./cmake-build-debug-64"
DEBUG=0 DEBUG=0
OUTDIR="./bin/spice2x" OUTDIR="./bin/spice2x"
OUTDIR_EXTRAS="./bin/spice2x/extras"
# disabled UPX since it tends to falsely trigger malware detection # disabled UPX since it tends to falsely trigger malware detection
UPX_ENABLE=0 UPX_ENABLE=0
@@ -59,9 +60,10 @@ INCLUDE_SRC=1
DIST_ENABLE=1 DIST_ENABLE=1
DIST_FOLDER="./dist" 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_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_spice spicetools_spice_laa 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"
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64" TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux"
# determine build type # determine build type
BUILD_TYPE="Release" BUILD_TYPE="Release"
@@ -179,11 +181,17 @@ fi
# copy to output directory # copy to output directory
echo "Copy files to output directory..." echo "Copy files to output directory..."
rm -rf ${OUTDIR} rm -rf ${OUTDIR}
mkdir -p ${OUTDIR} mkdir -p ${OUTDIR}
mkdir -p ${OUTDIR}/stubs/32 mkdir -p ${OUTDIR}/stubs/32
mkdir -p ${OUTDIR}/stubs/64 mkdir -p ${OUTDIR}/stubs/64
mkdir -p ${OUTDIR}/extras/largeaddressaware
rm -rf ${OUTDIR_EXTRAS}
mkdir -p ${OUTDIR_EXTRAS}
mkdir -p ${OUTDIR_EXTRAS}/largeaddressaware
mkdir -p ${OUTDIR_EXTRAS}/linux
if false # ((DEBUG > 0)) if false # ((DEBUG > 0))
then then
# debug files # debug files
@@ -204,11 +212,14 @@ then
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
cp ${BUILDDIR_32}/spicetools/spicecfg_linux.exe ${OUTDIR_EXTRAS}/linux/spicecfg.exe 2>/dev/null
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/kbt.dll ${OUTDIR}/stubs/32 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_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/kbt.dll ${OUTDIR}/stubs/64 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/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
@@ -228,10 +239,10 @@ then
fi fi
# copy resources # copy resources
rm -rf ${OUTDIR}/api rm -rf ${OUTDIR_EXTRAS}/api
mkdir ${OUTDIR}/api mkdir ${OUTDIR_EXTRAS}/api
find ./api/resources/python -name "__pycache__" -exec rm -rf {} + find ./api/resources/python -name "__pycache__" -exec rm -rf {} +
cp -r ./api/resources/* ${OUTDIR}/api cp -r ./api/resources/* ${OUTDIR_EXTRAS}/api
# build distribution archive # build distribution archive
if ((DIST_ENABLE > 0)) if ((DIST_ENABLE > 0))
@@ -240,7 +251,9 @@ then
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} . -z <<< "$DIST_COMMENT" zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
echo "Building extras..."
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x -z <<< "$DIST_COMMENT"
popd > /dev/null popd > /dev/null
fi fi
+1
View File
@@ -1,2 +1,3 @@
del /s /q .ccache del /s /q .ccache
del /s /q dist
call build_docker.bat call build_docker.bat
+8 -3
View File
@@ -82,18 +82,23 @@ public:
return this->index != 0xFF; return this->index != 0xFF;
} }
inline void clearBindings() { inline void resetValues() {
device_identifier = "";
index = 0xFF;
setSensitivity(1.f); setSensitivity(1.f);
setDeadzone(0.f); setDeadzone(0.f);
invert = false; invert = false;
smoothing = false; smoothing = false;
deadzone_mirror = false;
setMultiplier(1); setMultiplier(1);
setRelativeMode(false); setRelativeMode(false);
setDelayBufferDepth(0); setDelayBufferDepth(0);
} }
inline void clearBindings() {
device_identifier = "";
index = 0xFF;
resetValues();
}
inline const std::string &getName() const { inline const std::string &getName() const {
return this->name; return this->name;
} }
+5
View File
@@ -87,6 +87,10 @@ namespace cfg {
load_float_value(doc, root + prefix + "scale_x", scene.scale_x); load_float_value(doc, root + prefix + "scale_x", scene.scale_x);
load_float_value(doc, root + prefix + "scale_y", scene.scale_y); load_float_value(doc, root + prefix + "scale_y", scene.scale_y);
load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio); load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio);
int duplicate = 0;
load_int_value(doc, root + prefix + "duplicate", duplicate);
scene.duplicate = static_cast<cfg::ScreenDuplicateMode>(duplicate);
} }
// windowed settings are always under game settings // windowed settings are always under game settings
@@ -201,6 +205,7 @@ namespace cfg {
rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x); rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x);
rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y); rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y);
rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio); rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio);
rapidjson::Pointer(root + prefix + "duplicate").Set(doc, scene.duplicate);
} }
// windowed mode settings // windowed mode settings
+7
View File
@@ -14,12 +14,19 @@ namespace cfg {
ResizableFrame = 2 ResizableFrame = 2
}; };
enum ScreenDuplicateMode {
None = 0,
CopyLeft = 1,
CopyRight = 2,
};
struct fullscreen_setting { struct fullscreen_setting {
int offset_x = 0; int offset_x = 0;
int offset_y = 0; int offset_y = 0;
float scale_x = 1.0; float scale_x = 1.0;
float scale_y = 1.0; float scale_y = 1.0;
bool keep_aspect_ratio = true; bool keep_aspect_ratio = true;
ScreenDuplicateMode duplicate = ScreenDuplicateMode::None;
}; };
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE; extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
+3
View File
@@ -23,6 +23,7 @@
* Modified version. * Modified version.
*/ */
#ifndef NO_SCARD
#include "scard.h" #include "scard.h"
#include <windows.h> #include <windows.h>
@@ -409,3 +410,5 @@ void scard_threadstart() {
void scard_fini() { void scard_fini() {
should_exit = true; should_exit = true;
} }
#endif
+3 -1
View File
@@ -125,7 +125,7 @@ namespace games::ddr {
void DDRGame::pre_attach() { void DDRGame::pre_attach() {
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) {
log_fatal( log_warning(
"ddr", "ddr",
"BAD MODEL NAME ERROR\n\n\n" "BAD MODEL NAME ERROR\n\n\n"
"!!! model name set to TDX, this is WRONG and will break your game !!!\n" "!!! model name set to TDX, this is WRONG and will break your game !!!\n"
@@ -136,6 +136,8 @@ namespace games::ddr {
"!!! !!!\n" "!!! !!!\n"
"!!! model name set to TDX, this is WRONG and will break your game !!!\n\n\n" "!!! model name set to TDX, this is WRONG and will break your game !!!\n\n\n"
); );
log_fatal("ddr", "BAD MODEL NAME ERROR - TDX specified, should be MDX instead");
} }
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::DEST[0] == 'U') { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::DEST[0] == 'U') {
+72 -70
View File
@@ -22,35 +22,31 @@
static std::filesystem::path dll_path; static std::filesystem::path dll_path;
static HMODULE iidx_module; static HMODULE iidx_module;
static uintptr_t addr_hook_a = 0; static uintptr_t addr_hook_a = 0;
static uintptr_t addr_hook_b = 0;
static uintptr_t addr_textures = 0; static uintptr_t addr_textures = 0;
static uintptr_t addr_camera_manager = 0;
static uintptr_t addr_device_offset = 0; static uintptr_t addr_device_offset = 0;
static uintptr_t addr_afp_texture_offset = 0;
typedef void **(__fastcall *camera_hook_a_t)(PBYTE); typedef void **(__fastcall *camera_hook_a_t)(PBYTE);
typedef LPDIRECT3DTEXTURE9 (*camera_hook_b_t)(void*, int);
static camera_hook_a_t camera_hook_a_orig = nullptr; static camera_hook_a_t camera_hook_a_orig = nullptr;
static camera_hook_b_t camera_hook_b_orig = nullptr;
auto static hook_a_init = std::once_flag {}; auto static hook_a_init = std::once_flag {};
static LPDIRECT3DDEVICE9EX device; static LPDIRECT3DDEVICE9EX device;
static LPDIRECT3DTEXTURE9 *texture_a = nullptr; static LPDIRECT3DTEXTURE9 *camera_texture_a = nullptr;
static LPDIRECT3DTEXTURE9 *texture_b = nullptr; static LPDIRECT3DTEXTURE9 *camera_texture_b = nullptr;
static LPDIRECT3DTEXTURE9 *preview_texture_a = nullptr;
static LPDIRECT3DTEXTURE9 *preview_texture_b = nullptr;
struct PredefinedHook { struct PredefinedHook {
std::string pe_identifier; std::string pe_identifier;
uintptr_t hook_a; uintptr_t hook_a;
uintptr_t hook_b;
uintptr_t hook_textures; uintptr_t hook_textures;
uintptr_t hook_camera_manager;
uintptr_t hook_device_offset; uintptr_t hook_device_offset;
uintptr_t hook_afp_texture_offset;
}; };
PredefinedHook g_predefinedHooks[] = PredefinedHook g_predefinedHooks[] = {};
{
// 27 003
{ "5f713b52_6d4090", 0x005971b0, 0x005fb2c0, 0x04e49898, 0x000000d0 },
// 27 010
{ "5f713946_7f52b0", 0x006b89e0, 0x0071c950, 0x04fd08b8, 0x000000d0 },
};
const DWORD g_predefinedHooksLength = ARRAYSIZE(g_predefinedHooks); const DWORD g_predefinedHooksLength = ARRAYSIZE(g_predefinedHooks);
@@ -75,8 +71,8 @@ namespace games::iidx {
if (sscanf( if (sscanf(
s.c_str(), s.c_str(),
"%i,%i,%i,%i", "%i,%i,%i,%i,%i",
(int*)&addr_hook_a, (int*)&addr_hook_b, (int*)&addr_textures, (int*)&addr_device_offset) == 4) { (int*)&addr_hook_a, (int*)&addr_textures, (int*)&addr_camera_manager, (int*)&addr_device_offset, (int*)&addr_afp_texture_offset) == 5) {
return true; return true;
@@ -101,8 +97,8 @@ namespace games::iidx {
// there are three different ways we try to hook the camera entry points // there are three different ways we try to hook the camera entry points
// 1) user-provided override via cmd line // 1) user-provided override via cmd line
// 2) predefined offsets using PE header matching (needed for iidx27 and few others) // 2) predefined offsets using PE header matching
// 3) signature match (should work for most iidx28-31) // 3) signature match (should work for most iidx27-33)
// method 1: user provided // method 1: user provided
if (TDJ_CAMERA_OVERRIDE.has_value() && parse_cmd_params()) { if (TDJ_CAMERA_OVERRIDE.has_value() && parse_cmd_params()) {
@@ -117,10 +113,11 @@ namespace games::iidx {
for (DWORD i = 0; i < g_predefinedHooksLength; i++) { for (DWORD i = 0; i < g_predefinedHooksLength; i++) {
if (pe.compare(g_predefinedHooks[i].pe_identifier) == 0) { if (pe.compare(g_predefinedHooks[i].pe_identifier) == 0) {
log_misc("iidx:camhook", "Found predefined addresses"); log_misc("iidx:camhook", "Found predefined addresses");
addr_hook_a = g_predefinedHooks[i].hook_a; addr_hook_a = g_predefinedHooks[i].hook_a;
addr_hook_b = g_predefinedHooks[i].hook_b; addr_textures = g_predefinedHooks[i].hook_textures;
addr_textures = g_predefinedHooks[i].hook_textures; addr_camera_manager = g_predefinedHooks[i].hook_camera_manager;
addr_device_offset = g_predefinedHooks[i].hook_device_offset; addr_device_offset = g_predefinedHooks[i].hook_device_offset;
addr_afp_texture_offset = g_predefinedHooks[i].hook_afp_texture_offset;
return TRUE; return TRUE;
} }
} }
@@ -131,8 +128,8 @@ namespace games::iidx {
// --- addr_hook_a --- // --- addr_hook_a ---
uint8_t *addr_hook_a_ptr = reinterpret_cast<uint8_t *>(find_pattern( uint8_t *addr_hook_a_ptr = reinterpret_cast<uint8_t *>(find_pattern(
iidx_module, iidx_module,
"488BC4565741564883EC5048C740D8FEFFFFFF", "E800000000488B8F0000000048894D",
"XXXXXXXXXXXXXXXXXXX", "X????XXX????XXX",
0, 0)); 0, 0));
if (addr_hook_a_ptr == nullptr) { if (addr_hook_a_ptr == nullptr) {
@@ -141,24 +138,10 @@ namespace games::iidx {
return FALSE; return FALSE;
} }
addr_hook_a = (uint64_t) (addr_hook_a_ptr - (uint8_t*) iidx_module);
// addr_hook_b
uint8_t* addr_hook_b_ptr = reinterpret_cast<uint8_t *>(find_pattern(
iidx_module,
"E8000000004885C07439E8",
"X????XXXXXX",
0, 0));
if (addr_hook_b_ptr == nullptr) {
log_warning("iidx:camhook", "failed to find hook: addr_hook_b");
return FALSE;
}
// displace with the content of wildcard bytes // displace with the content of wildcard bytes
int32_t disp_b = *((int32_t *) (addr_hook_b_ptr + 1)); int32_t disp_a = *((int32_t *) (addr_hook_a_ptr + 1));
addr_hook_b = (addr_hook_b_ptr - (uint8_t*) iidx_module) + disp_b + 5; addr_hook_a = (addr_hook_a_ptr - (uint8_t*) iidx_module) + disp_a + 5;
// --- addr_textures --- // --- addr_textures ---
uint8_t* addr_textures_ptr = reinterpret_cast<uint8_t *>(find_pattern( uint8_t* addr_textures_ptr = reinterpret_cast<uint8_t *>(find_pattern(
@@ -194,6 +177,28 @@ namespace games::iidx {
addr_textures = (addr_textures_ptr - (uint8_t*) iidx_module) + disp_textures + 7; addr_textures = (addr_textures_ptr - (uint8_t*) iidx_module) + disp_textures + 7;
// --- addr_camera_manager ---
uint8_t *addr_camera_manager_ptr = reinterpret_cast<uint8_t *>(find_pattern(
iidx_module,
"E80000000033FF488B58",
"X????XXXXX",
0, 0));
if (addr_camera_manager_ptr == nullptr) {
log_warning("iidx:camhook", "failed to find hook: addr_get_camera_manager");
return FALSE;
}
// displace with the content of wildcard bytes
int32_t disp_camera_manager = *((int32_t *) (addr_camera_manager_ptr + 1));
addr_camera_manager_ptr = addr_camera_manager_ptr + disp_camera_manager + 5;
// once more to get the final address
disp_camera_manager = *((int32_t *) (addr_camera_manager_ptr + 3));
addr_camera_manager = (addr_camera_manager_ptr - (uint8_t*) iidx_module) + disp_camera_manager + 7;
// addr_device_offset // addr_device_offset
search_from = (addr_hook_a_ptr - (uint8_t*) iidx_module); search_from = (addr_hook_a_ptr - (uint8_t*) iidx_module);
uint8_t *addr_device_ptr = reinterpret_cast<uint8_t *>(find_pattern_from( uint8_t *addr_device_ptr = reinterpret_cast<uint8_t *>(find_pattern_from(
@@ -201,39 +206,47 @@ namespace games::iidx {
"488B89", "488B89",
"XXX", "XXX",
3, 0, search_from)); 3, 0, search_from));
addr_device_offset = *addr_device_ptr;
if (addr_textures_ptr == nullptr) { if (addr_device_ptr == nullptr) {
log_warning("iidx:camhook", "failed to find hook: addr_textures (part 3)"); log_warning("iidx:camhook", "failed to find hook: addr_device_ptr");
return FALSE; return FALSE;
} }
addr_device_offset = *addr_device_ptr;
// --- addr_afp_texture_offset ---
uint8_t *addr_afp_texture_ptr = reinterpret_cast<uint8_t *>(find_pattern(
iidx_module,
"488B41004885C07400488D5424",
"XXX?XXXX?XXXX",
0, 0));
if (addr_afp_texture_ptr == nullptr) {
log_warning("iidx:camhook", "failed to find hook: addr_afp_texture_offset");
return FALSE;
}
addr_afp_texture_offset = *(addr_afp_texture_ptr + 3);
return TRUE; return TRUE;
} }
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 textures = *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);
texture_a = textures; camera_texture_a = manager->cameras->a->d3d9_texture(addr_afp_texture_offset);
texture_b = textures + 2; camera_texture_b = manager->cameras->b->d3d9_texture(addr_afp_texture_offset);
preview_texture_a = preview;
preview_texture_b = preview + 2;
init_local_camera(); init_local_camera();
}); });
return camera_hook_a_orig(a1); return camera_hook_a_orig(a1);
} }
static LPDIRECT3DTEXTURE9 camera_hook_b(void* a1, int idx) {
if (idx == 0 && top_camera) {
return top_camera->GetTexture();
}
if (idx == 1 && front_camera) {
return front_camera->GetTexture();
}
return camera_hook_b_orig(a1, idx);
}
bool create_hooks() { bool create_hooks() {
auto *hook_a_ptr = reinterpret_cast<uint16_t *>( auto *hook_a_ptr = reinterpret_cast<uint16_t *>(
reinterpret_cast<intptr_t>(iidx_module) + addr_hook_a); reinterpret_cast<intptr_t>(iidx_module) + addr_hook_a);
@@ -246,17 +259,6 @@ namespace games::iidx {
return FALSE; return FALSE;
} }
auto *hook_b_ptr = reinterpret_cast<uint16_t *>(
reinterpret_cast<intptr_t>(iidx_module) + addr_hook_b);
if (!detour::trampoline(
reinterpret_cast<camera_hook_b_t>(hook_b_ptr),
camera_hook_b,
&camera_hook_b_orig)) {
log_warning("iidx:camhook", "failed to trampoline hook_b");
return FALSE;
}
return TRUE; return TRUE;
} }
@@ -443,7 +445,7 @@ namespace games::iidx {
} }
IIDXLocalCamera* add_top_camera(IMFActivate* pActivate) { IIDXLocalCamera* add_top_camera(IMFActivate* pActivate) {
auto top_camera = new IIDXLocalCamera("top", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, texture_a); auto top_camera = new IIDXLocalCamera("top", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, camera_texture_a, preview_texture_a);
if (top_camera->m_initialized) { if (top_camera->m_initialized) {
LOCAL_CAMERA_LIST.push_back(top_camera); LOCAL_CAMERA_LIST.push_back(top_camera);
} else { } else {
@@ -454,7 +456,7 @@ namespace games::iidx {
} }
IIDXLocalCamera* add_front_camera(IMFActivate* pActivate) { IIDXLocalCamera* add_front_camera(IMFActivate* pActivate) {
auto front_camera = new IIDXLocalCamera("front", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, texture_b); auto front_camera = new IIDXLocalCamera("front", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, camera_texture_b, preview_texture_b);
if (front_camera->m_initialized) { if (front_camera->m_initialized) {
LOCAL_CAMERA_LIST.push_back(front_camera); LOCAL_CAMERA_LIST.push_back(front_camera);
} else { } else {
@@ -469,8 +471,8 @@ namespace games::iidx {
if (result) { if (result) {
log_misc( log_misc(
"iidx:camhook", "iidx:camhook",
"found hooks:\n hook_a 0x{:x}\n hook_b 0x{:x}\n textures 0x{:x}\n device_offset 0x{:x}", "found hooks:\n hook_a 0x{:x}\n textures 0x{:x}\n camera_manager 0x{:x}\n device_offset 0x{:x}\n afp_texture_offset 0x{:x}",
addr_hook_a, addr_hook_b, addr_textures, addr_device_offset); addr_hook_a, addr_textures, addr_camera_manager, addr_device_offset, addr_afp_texture_offset);
result = create_hooks(); result = create_hooks();
if (result) { if (result) {
init_mf_library(); init_mf_library();
+67 -36
View File
@@ -53,7 +53,7 @@ namespace games::iidx {
// settings // settings
bool FLIP_CAMS = false; bool FLIP_CAMS = false;
bool DISABLE_CAMS = false; std::optional<bool> DISABLE_CAMS;
bool TDJ_CAMERA = false; bool TDJ_CAMERA = false;
bool TDJ_CAMERA_PREFER_16_9 = true; bool TDJ_CAMERA_PREFER_16_9 = true;
bool TDJ_MODE = false; bool TDJ_MODE = false;
@@ -334,11 +334,6 @@ namespace games::iidx {
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true; wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE); wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
} }
// prevent crash on TDJ mode without correct DLL
if (!GetModuleHandle("nvcuda.dll")) {
DISABLE_CAMS = true;
}
} }
// insert BI2X hooks // insert BI2X hooks
@@ -399,37 +394,44 @@ namespace games::iidx {
"RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE); "RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE);
// check if cam hook should be enabled // check if cam hook should be enabled
if (!DISABLE_CAMS) { if (!DISABLE_CAMS.has_value()) {
log_fatal("iidx", "assertion failure - DISABLE_CAMS not set during attach");
}
if (!DISABLE_CAMS.value()) {
init_legacy_camera_hook(FLIP_CAMS); init_legacy_camera_hook(FLIP_CAMS);
} }
// init cfgmgr32 hooks // init cfgmgr32 hooks
cfgmgr32hook_init(avs::game::DLL_INSTANCE); cfgmgr32hook_init(avs::game::DLL_INSTANCE);
// report common errors on iidx31 and above
if (avs::game::is_ext(2023091500, MAXINT) && GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
deferredlogs::defer_error_messages({
"common incompatibility with DX 9on12 + IIDX 31 and above",
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes"
});
}
} }
void IIDXGame::pre_attach() { void IIDXGame::pre_attach() {
Game::pre_attach(); Game::pre_attach();
auto options = games::get_options(eamuse_get_game());
// environment variables must be set before the DLL is loaded as the VC++ runtime copies all // environment variables must be set before the DLL is loaded as the VC++ runtime copies all
// environment variables at startup // environment variables at startup
if (DISABLE_CAMS) {
SetEnvironmentVariable("CONNECT_CAMERA", "0");
}
if (SCREEN_MODE.has_value()) { if (SCREEN_MODE.has_value()) {
log_misc("iidx", "SCREEN_MODE env var set to {}", SCREEN_MODE.value().c_str());
SetEnvironmentVariable("SCREEN_MODE", SCREEN_MODE.value().c_str()); SetEnvironmentVariable("SCREEN_MODE", SCREEN_MODE.value().c_str());
} }
// check for cab camera access for the second time (first time was in launcher.cpp)
// this time, we are inside -iidx module hook, which means the user is likely NOT on a cab
// therefore, start with cams OFF by default, and allow user to forcibly override to ON
if (!games::iidx::DISABLE_CAMS.has_value()) {
games::iidx::DISABLE_CAMS = true;
if (options->at(launcher::Options::IIDXCabCamAccess).is_active() &&
options->at(launcher::Options::IIDXCabCamAccess).value_text() == "on") {
games::iidx::DISABLE_CAMS = false;
}
if (games::iidx::DISABLE_CAMS.value()) {
log_misc("iidx", "CONNECT_CAMERA env var set to 0");
SetEnvironmentVariable("CONNECT_CAMERA", "0");
}
}
// windowed subscreen, enabled by default, unless turned off by user // windowed subscreen, enabled by default, unless turned off by user
auto options = games::get_options(eamuse_get_game());
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) { if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
GRAPHICS_IIDX_WSUB = true; GRAPHICS_IIDX_WSUB = true;
} }
@@ -440,7 +442,7 @@ namespace games::iidx {
// check bad model name // check bad model name
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDJ")) { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDJ")) {
log_fatal( log_warning(
"iidx", "iidx",
"BAD MODEL NAME ERROR\n\n\n" "BAD MODEL NAME ERROR\n\n\n"
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n" "!!! model name set to TDJ, this is WRONG and will break your game !!!\n"
@@ -449,7 +451,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"
@@ -458,6 +460,34 @@ namespace games::iidx {
"!!! !!!\n" "!!! !!!\n"
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n\n\n" "!!! model name set to TDJ, this is WRONG and will break your game !!!\n\n\n"
); );
log_fatal("iidx", "BAD MODEL NAME ERROR - TDJ specified, must be LDJ instead");
}
// check -monitor + TDJ mode
if (!GRAPHICS_WINDOWED &&
options->at(launcher::Options::DisplayAdapter).is_active() &&
TDJ_MODE) {
log_warning(
"iidx",
"\n\n!!! using -monitor option with TDJ is NOT recommended due to known !!!\n"
"!!! compatibility issues with the game !!!\n"
"!!! !!!\n"
"!!! * game may launch in wrong resolution or refresh rate !!!\n"
"!!! * touch / mouse input may stop working in subscreen / overlay !!!\n"
"!!! !!!\n"
"!!! recommendation is to NOT use -monitor and instead set the !!!\n"
"!!! primary monitor in Windows settings before launching the game !!!\n\n"
);
deferredlogs::defer_error_messages({
"-monitor option is NOT recommended when running with TDJ mode",
" due to known compatibility issues with the game:",
" * game may launch in wrong resolution or refresh rate",
" * touch / mouse input may stop working in subscreen / overlay",
" recommended fix is to NOT use -monitor and instead set the primary",
" monitor in Windows settings before launching the game"
});
} }
} }
@@ -783,7 +813,15 @@ namespace games::iidx {
// <=24 : 32-bit only // <=24 : 32-bit only
// 25-26: has neither (no patch needed - WASAPI Exclusive by default) // 25-26: has neither (no patch needed - WASAPI Exclusive by default)
// 27-30: has both (envvar will be respected, ASIO or WASAPI) // 27-30: has both (envvar will be respected, ASIO or WASAPI)
// 31+: only has XONAR (ASIO by default, signature patch can be used to force WASAPI - for now) // 31-32: only has XONAR (ASIO by default, signature patch can be used to force WASAPI - for now)
// 33 : early versions only have XONAR, but later datecodes have both; SOUND_OUTPUT_DEVICE
// returned and it works again when set as env var
log_info(
"iidx",
"has_SOUND_OUTPUT_DEVICE: {}, has_XONAR_SOUND_CARD: {}",
(has_SOUND_OUTPUT_DEVICE != 0),
(has_XONAR_SOUND_CARD != 0));
if (!has_SOUND_OUTPUT_DEVICE && !has_XONAR_SOUND_CARD) { if (!has_SOUND_OUTPUT_DEVICE && !has_XONAR_SOUND_CARD) {
// iidx 25-26 // iidx 25-26
@@ -791,16 +829,10 @@ namespace games::iidx {
return; return;
} }
if (has_SOUND_OUTPUT_DEVICE && has_XONAR_SOUND_CARD) { // if the game doesn't accept SOUND_OUTPUT_DEVICE, patch game to force wasapi
// iidx 27-30 if (!has_SOUND_OUTPUT_DEVICE &&
log_info("iidx", "This game accepts SOUND_OUTPUT_DEVICE environment variable"); SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
return; SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi") {
}
log_info("iidx", "This game supports ASIO but does not accept SOUND_OUTPUT_DEVICE environment variable");
// patch game to force wasapi
if (SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() && SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi") {
intptr_t result = replace_pattern( intptr_t result = replace_pattern(
avs::game::DLL_INSTANCE, avs::game::DLL_INSTANCE,
"FF5008E8??????FF83780803740D", "FF5008E8??????FF83780803740D",
@@ -818,8 +850,6 @@ namespace games::iidx {
"Successfully forced WASAPI as audio engine using signature matching @ 0x{:x}.", "Successfully forced WASAPI as audio engine using signature matching @ 0x{:x}.",
result); result);
} }
} else {
log_info("iidx", "Not applying force wasapi patch; game will use ASIO");
} }
// patch iidx32+ for asio compatibility // patch iidx32+ for asio compatibility
@@ -827,7 +857,8 @@ namespace games::iidx {
// 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, MAXINT) &&
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() && SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) { !(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
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
+1 -1
View File
@@ -14,7 +14,7 @@ namespace games::iidx {
// settings // settings
extern bool FLIP_CAMS; extern bool FLIP_CAMS;
extern bool DISABLE_CAMS; extern std::optional<bool> DISABLE_CAMS;
extern bool TDJ_CAMERA; extern bool TDJ_CAMERA;
extern bool TDJ_CAMERA_PREFER_16_9; extern bool TDJ_CAMERA_PREFER_16_9;
extern std::optional<std::string> TDJ_CAMERA_OVERRIDE; extern std::optional<std::string> TDJ_CAMERA_OVERRIDE;
+2
View File
@@ -213,6 +213,8 @@ namespace games::iidx {
} }
void init_legacy_camera_hook(bool flip_cams) { void init_legacy_camera_hook(bool flip_cams) {
log_info("iidx::cam", "initializing legacy camera hook");
should_flip_cams = flip_cams; should_flip_cams = flip_cams;
// camera media framework hook // camera media framework hook
+10 -5
View File
@@ -46,14 +46,17 @@ namespace games::iidx {
IMFActivate *pActivate, IMFActivate *pActivate,
IDirect3DDeviceManager9 *pD3DManager, IDirect3DDeviceManager9 *pD3DManager,
LPDIRECT3DDEVICE9EX device, LPDIRECT3DDEVICE9EX device,
LPDIRECT3DTEXTURE9 *texture_target LPDIRECT3DTEXTURE9 *camera_texture_target,
LPDIRECT3DTEXTURE9 *preview_texture_target
): ):
m_nRefCount(1), m_nRefCount(1),
m_name(name), m_name(name),
m_prefer_16_by_9(prefer_16_by_9), m_prefer_16_by_9(prefer_16_by_9),
m_device(device), m_device(device),
m_texture_target(texture_target), m_camera_texture_target(camera_texture_target),
m_texture_original(*texture_target) m_preview_texture_target(preview_texture_target),
m_camera_texture_original(*camera_texture_target),
m_preview_texture_original(*preview_texture_target)
{ {
InitializeCriticalSection(&m_critsec); InitializeCriticalSection(&m_critsec);
@@ -691,7 +694,8 @@ namespace games::iidx {
if (FAILED(hr)) { goto done; } if (FAILED(hr)) { goto done; }
// Make the game use this new texture as camera stream source // Make the game use this new texture as camera stream source
*m_texture_target = m_texture; *m_camera_texture_target = m_texture;
*m_preview_texture_target = m_texture;
m_active = TRUE; m_active = TRUE;
// Create texture for colour conversion // Create texture for colour conversion
@@ -860,7 +864,8 @@ namespace games::iidx {
ULONG uCount = InterlockedDecrement(&m_nRefCount); ULONG uCount = InterlockedDecrement(&m_nRefCount);
*m_texture_target = m_texture_original; *m_camera_texture_target = m_camera_texture_original;
*m_preview_texture_target = m_preview_texture_original;
for (size_t i = 0; i < m_mediaTypeInfos.size(); i++) { for (size_t i = 0; i < m_mediaTypeInfos.size(); i++) {
SafeRelease(&(m_mediaTypeInfos.at(i).p_mediaType)); SafeRelease(&(m_mediaTypeInfos.at(i).p_mediaType));
+27 -5
View File
@@ -66,6 +66,25 @@ extern std::string CAMERA_CONTROL_LABELS[];
extern std::string DRAW_MODE_LABELS[]; extern std::string DRAW_MODE_LABELS[];
namespace games::iidx { namespace games::iidx {
namespace Camera {
struct PlayVideoCamera {
IDirect3DTexture9** d3d9_texture(const uintptr_t offset) {
auto const afp_texture = *reinterpret_cast<uint8_t**>
(reinterpret_cast<uint8_t*>(this) + offset);
return reinterpret_cast<IDirect3DTexture9**>(afp_texture + 0x8);
}
};
struct CCameraManager2 {
using camera_pointers = struct {
PlayVideoCamera* a;
PlayVideoCamera* b;
};
void* vftbl;
camera_pointers* cameras;
};
}
class IIDXLocalCamera { class IIDXLocalCamera {
protected: protected:
virtual ~IIDXLocalCamera() {}; virtual ~IIDXLocalCamera() {};
@@ -98,8 +117,9 @@ namespace games::iidx {
// DirectX9 DeviceEx // DirectX9 DeviceEx
LPDIRECT3DDEVICE9EX m_device; LPDIRECT3DDEVICE9EX m_device;
// Address to hook camera texture onto the game // Address to hook camera textures onto the game
LPDIRECT3DTEXTURE9 *m_texture_target = nullptr; LPDIRECT3DTEXTURE9 *m_camera_texture_target = nullptr;
LPDIRECT3DTEXTURE9 *m_preview_texture_target = nullptr;
// Target texture (to be shown in the game) // Target texture (to be shown in the game)
LPDIRECT3DTEXTURE9 m_texture = nullptr; LPDIRECT3DTEXTURE9 m_texture = nullptr;
@@ -117,8 +137,9 @@ namespace games::iidx {
LPDIRECT3DTEXTURE9 m_transformResultTexture = nullptr; LPDIRECT3DTEXTURE9 m_transformResultTexture = nullptr;
IDirect3DSurface9 *m_pTransformResultSurf = nullptr; IDirect3DSurface9 *m_pTransformResultSurf = nullptr;
// Storing original texture for clean up // Storing original textures for clean up
LPDIRECT3DTEXTURE9 m_texture_original = nullptr; LPDIRECT3DTEXTURE9 m_camera_texture_original = nullptr;
LPDIRECT3DTEXTURE9 m_preview_texture_original = nullptr;
IAMCameraControl *m_pCameraControl = nullptr; IAMCameraControl *m_pCameraControl = nullptr;
@@ -154,7 +175,8 @@ namespace games::iidx {
IMFActivate *pActivate, IMFActivate *pActivate,
IDirect3DDeviceManager9 *pD3DManager, IDirect3DDeviceManager9 *pD3DManager,
LPDIRECT3DDEVICE9EX device, LPDIRECT3DDEVICE9EX device,
LPDIRECT3DTEXTURE9 *texture_target LPDIRECT3DTEXTURE9 *camera_texture_target,
LPDIRECT3DTEXTURE9 *preview_texture_target
); );
LPDIRECT3DTEXTURE9 GetTexture(); LPDIRECT3DTEXTURE9 GetTexture();
ULONG Release(); ULONG Release();
+5 -1
View File
@@ -169,7 +169,7 @@ namespace games::jb {
const auto current_path = std::filesystem::current_path(); const auto current_path = std::filesystem::current_path();
log_misc("jubeat", "current working directory: {}", current_path.string()); log_misc("jubeat", "current working directory: {}", current_path.string());
if (current_path.parent_path() == current_path.root_path()) { if (current_path.parent_path() == current_path.root_path()) {
log_fatal( log_warning(
"jubeat", "jubeat",
"\n\nInvalid path error; jubeat cannot run from a directory in the drive root\n" "\n\nInvalid path error; jubeat cannot run from a directory in the drive root\n"
"The game will overflow the stack and silently fail to boot\n\n" "The game will overflow the stack and silently fail to boot\n\n"
@@ -179,6 +179,10 @@ namespace games::jb {
"To fix this, create a new directory and move ALL game files there.\n\n" "To fix this, create a new directory and move ALL game files there.\n\n"
"Your current working directory: {}\n", "Your current working directory: {}\n",
current_path.string()); current_path.string());
log_fatal(
"jubeat",
"Invalid path error; jubeat cannot run from a directory in the drive root");
} }
} }
} }
+35 -2
View File
@@ -4,6 +4,7 @@
#include "avs/game.h" #include "avs/game.h"
#include "hooks/graphics/graphics.h" #include "hooks/graphics/graphics.h"
#include "misc/eamuse.h" #include "misc/eamuse.h"
#include "misc/wintouchemu.h"
#include "touch/touch.h" #include "touch/touch.h"
#include "util/detour.h" #include "util/detour.h"
#include "util/libutils.h" #include "util/libutils.h"
@@ -32,6 +33,8 @@ namespace games::mfc {
static uint8_t CARD_TYPE = 0; static uint8_t CARD_TYPE = 0;
static uint8_t CARD_UID[8]; static uint8_t CARD_UID[8];
bool HG_MODE = false;
typedef int (__cdecl *inifile_param_num_t)(const char *, int *); typedef int (__cdecl *inifile_param_num_t)(const char *, int *);
static inifile_param_num_t inifile_param_num_real; static inifile_param_num_t inifile_param_num_real;
@@ -50,8 +53,10 @@ namespace games::mfc {
static void __cdecl touch_init(int width, int height) { static void __cdecl touch_init(int width, int height) {
log_info("mfc", "call touch_init(width: {}, height: {})", width, height); log_info("mfc", "call touch_init(width: {}, height: {})", width, height);
// attach touch module if (HG_MODE) {
if (!TOUCH_ATTACHED) { wintouchemu::ADD_TOUCH_FLAG_PRIMARY = true;
wintouchemu::hook("MFC9", avs::game::DLL_INSTANCE);
} else if (!TOUCH_ATTACHED) { // attach touch module
// store touch size specification // store touch size specification
TOUCH_MAX_X = width; TOUCH_MAX_X = width;
@@ -288,6 +293,21 @@ namespace games::mfc {
MFCGame::MFCGame() : Game("Mahjong Fight Club") { MFCGame::MFCGame() : Game("Mahjong Fight Club") {
} }
static bool spam_remover(void *user, const std::string &data, logger::Style style, std::string &out) {
if (data.empty() || data[0] != '[') {
return false;
}
if (data.find("W:mutex: lock: mid 00000000 != AVS_DTYPE_MUTEX") != std::string::npos) {
out = "";
return true;
}
if (data.find("W:mutex: unlock: mid 00000000 != AVS_DTYPE_MUTEX") != std::string::npos) {
out = "";
return true;
}
return false;
}
void MFCGame::attach() { void MFCGame::attach() {
Game::attach(); Game::attach();
@@ -308,6 +328,17 @@ namespace games::mfc {
auto allinone_module = libutils::try_module("allinone.dll"); auto allinone_module = libutils::try_module("allinone.dll");
auto system_module = libutils::try_module("system.dll"); auto system_module = libutils::try_module("system.dll");
// Mahjong Fight Club - 2025?
// they removed allinone.dll and moved i/o into system.dll
if (allinone_module == nullptr) {
log_misc("mfc", "using system.dll instead of allinone.dll for i/o hooks");
allinone_module = system_module;
if (avs::game::SPEC[0] == 'F') {
HG_MODE = true;
}
}
// network fix // network fix
detour::iat_try("?nic_dhcp_maybe_exist@@YAEXZ", nic_dhcp_maybe_exist, system_module); detour::iat_try("?nic_dhcp_maybe_exist@@YAEXZ", nic_dhcp_maybe_exist, system_module);
@@ -378,5 +409,7 @@ namespace games::mfc {
detour::inline_hook((void *) mouse_utl_step, libutils::try_proc( detour::inline_hook((void *) mouse_utl_step, libutils::try_proc(
allinone_module, "?mouse_utl_step@@YAXXZ")); allinone_module, "?mouse_utl_step@@YAXXZ"));
} }
logger::hook_add(spam_remover, nullptr);
} }
} }
+2
View File
@@ -4,6 +4,8 @@
namespace games::mfc { namespace games::mfc {
extern bool HG_MODE;
struct joystick_state { struct joystick_state {
bool up = false; bool up = false;
bool down = false; bool down = false;
+49 -4
View File
@@ -4,6 +4,7 @@
#include "avs/game.h" #include "avs/game.h"
#include "games/shared/lcdhandle.h" #include "games/shared/lcdhandle.h"
#include "games/io.h"
#include "hooks/audio/audio.h" #include "hooks/audio/audio.h"
#include "hooks/graphics/graphics.h" #include "hooks/graphics/graphics.h"
#include "hooks/devicehook.h" #include "hooks/devicehook.h"
@@ -12,6 +13,7 @@
#include "hooks/powrprof.h" #include "hooks/powrprof.h"
#include "hooks/sleephook.h" #include "hooks/sleephook.h"
#include "hooks/winuser.h" #include "hooks/winuser.h"
#include "launcher/options.h"
#include "touch/touch.h" #include "touch/touch.h"
#include "util/deferlog.h" #include "util/deferlog.h"
#include "util/detour.h" #include "util/detour.h"
@@ -106,8 +108,8 @@ namespace games::sdvx {
if (ENABLE_COM_PORT_SCAN_HOOK && if (ENABLE_COM_PORT_SCAN_HOOK &&
lpSubKey != nullptr && phkResult != nullptr && lpSubKey != nullptr && phkResult != nullptr &&
_stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) { _stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) {
log_info("sdvx::io", "failing HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA"); log_info("sdvx::io", "hook access to HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA");
return 2; //ERROR_FILE_NOT_FOUND return 2; // ERROR_FILE_NOT_FOUND
} }
return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult); return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult);
@@ -215,6 +217,15 @@ namespace games::sdvx {
deferredlogs::defer_error_messages(deferredlogs::SUPERSTEP_SOUND_ERROR_MESSAGE); deferredlogs::defer_error_messages(deferredlogs::SUPERSTEP_SOUND_ERROR_MESSAGE);
return false; return false;
} }
if (data.find("W:ea3-pos: ea3_report_posev: no such node:") != std::string::npos) {
deferredlogs::defer_error_messages({
"bad prop\\ea3-config.xml detected by game",
std::string(" ") + data,
" this will most likely cause Server Busy errors in certain conditions",
" follow the FAQ to fix your ea3-config.xml and try again"
});
return false;
}
return false; return false;
} }
@@ -268,7 +279,7 @@ namespace games::sdvx {
AUTO_INSERT_CARD_COOLDOWN = 15.f; AUTO_INSERT_CARD_COOLDOWN = 15.f;
// check bad model name // check bad model name
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("UFC")) { if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("UFC")) {
log_fatal( log_warning(
"sdvx", "sdvx",
"BAD MODEL NAME ERROR\n\n\n" "BAD MODEL NAME ERROR\n\n\n"
"!!! model name set to UFC, this is WRONG and will break your game !!!\n" "!!! model name set to UFC, this is WRONG and will break your game !!!\n"
@@ -278,7 +289,41 @@ namespace games::sdvx {
"!!! !!!\n" "!!! !!!\n"
"!!! model name set to UFC, this is WRONG and will break your game !!!\n\n\n" "!!! model name set to UFC, this is WRONG and will break your game !!!\n\n\n"
); );
} log_fatal(
"sdvx",
"BAD MODEL NAME ERROR - model name set to UFC, must be KFC instead");
}
#ifdef SPICE64 // SDVX5+ specific code
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
auto options = games::get_options(eamuse_get_game());
// check -monitor + UFC mode
if (!GRAPHICS_WINDOWED &&
options->at(launcher::Options::DisplayAdapter).is_active() &&
isValkyrieCabinetMode) {
log_warning(
"sdvx",
"\n\n!!! using -monitor option with VM mode is NOT recommended due to !!!\n"
"!!! known compatibility issues with the game !!!\n"
"!!! !!!\n"
"!!! * game may launch in wrong resolution or refresh rate !!!\n"
"!!! * touch / mouse input may stop working in subscreen / overlay !!!\n"
"!!! !!!\n"
"!!! recommendation is to NOT use -monitor and instead set the !!!\n"
"!!! primary monitor in Windows settings before launching the game !!!\n\n"
);
deferredlogs::defer_error_messages({
"-monitor option is NOT recommended when running with Valkyrie mode",
" due to known compatibility issues with the game:",
" * game may launch in wrong resolution or refresh rate",
" * touch / mouse input may stop working in subscreen / overlay",
" recommended fix is to NOT use -monitor and instead set the primary",
" monitor in Windows settings before launching the game"
});
}
#endif
} }
void SDVXGame::attach() { void SDVXGame::attach() {
@@ -15,6 +15,7 @@
#include "cfg/screen_resize.h" #include "cfg/screen_resize.h"
#include "games/iidx/iidx.h" #include "games/iidx/iidx.h"
#include "games/sdvx/sdvx.h" #include "games/sdvx/sdvx.h"
#include "games/mfc/mfc.h"
#include "games/io.h" #include "games/io.h"
#include "hooks/graphics/graphics.h" #include "hooks/graphics/graphics.h"
#include "launcher/launcher.h" #include "launcher/launcher.h"
@@ -238,14 +239,17 @@ static void log_create_device_failure(HRESULT hresult) {
} }
static bool is_dx9_on_12_enabled() { static bool is_dx9_on_12_enabled() {
bool result = false;
switch (GRAPHICS_9_ON_12_STATE) { switch (GRAPHICS_9_ON_12_STATE) {
case DX9ON12_FORCE_OFF: case DX9ON12_FORCE_OFF:
log_info("graphics::d3d9", "DirectX 9on12: forced OFF by user (-dx9on12)"); log_info("graphics::d3d9", "DirectX 9on12: forced OFF by user (-dx9on12)");
return false; result = false;
break;
case DX9ON12_FORCE_ON: case DX9ON12_FORCE_ON:
log_info("graphics::d3d9", "DirectX 9on12: forced ON by user (-9on12 or -dx9on12)"); log_info("graphics::d3d9", "DirectX 9on12: forced ON by user (-9on12 or -dx9on12)");
return true; result = true;
break;
case DX9ON12_AUTO: case DX9ON12_AUTO:
default: default:
@@ -253,14 +257,33 @@ static bool is_dx9_on_12_enabled() {
log_info( log_info(
"graphics::d3d9", "graphics::d3d9",
"DirectX 9on12: enabled automatically for current game (tip: use -dx9on12 to force on or off)"); "DirectX 9on12: enabled automatically for current game (tip: use -dx9on12 to force on or off)");
return true; result = true;
} else { } else {
log_info( log_info(
"graphics::d3d9", "graphics::d3d9",
"DirectX 9on12: disabled by default, using DX9 (tip: use -dx9on12 to force on or off)"); "DirectX 9on12: disabled by default, using DX9 (tip: use -dx9on12 to force on or off)");
return false; result = false;
} }
break;
} }
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, MAXINT)) {
deferredlogs::defer_error_messages({
"dx9on12 was force enabled by user (-dx9on12)",
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
" try again with -dx9on12 option set to default value"
});
} else {
deferredlogs::defer_error_messages({
"dx9on12 was force enabled by user (-dx9on12)",
" not very game is compatible with this, and can lead to crashes",
" try without force enabling this if you are seeing issues"
});
}
}
return result;
} }
static IDirect3D9 *WINAPI Direct3DCreate9_hook(UINT SDKVersion) { static IDirect3D9 *WINAPI Direct3DCreate9_hook(UINT SDKVersion) {
@@ -1338,6 +1361,11 @@ void graphics_d3d9_on_present(
graphics_d3d9_ldj_on_present(wrapped_device); graphics_d3d9_ldj_on_present(wrapped_device);
} }
const bool is_mfc = avs::game::is_model("KK9") && games::mfc::HG_MODE;
if (is_mfc) {
wintouchemu::update();
}
// check screenshot key // check screenshot key
static bool trigger_last = false; static bool trigger_last = false;
auto buttons = games::get_buttons_overlay(eamuse_get_game()); auto buttons = games::get_buttons_overlay(eamuse_get_game());
@@ -733,6 +733,16 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
const int rectTop = param.BackBufferHeight; const int rectTop = param.BackBufferHeight;
const int w = param.BackBufferWidth; const int w = param.BackBufferWidth;
const int h = param.BackBufferHeight; const int h = param.BackBufferHeight;
if (cfg::SCREENRESIZE->enable_screen_resize) {
RECT rect {
0,
0,
(LONG)topSurface_width,
(LONG)topSurface_height,
};
pReal->ColorFill(topSurface, &rect, D3DCOLOR_XRGB(0, 0, 0));
}
D3DLOCKED_RECT rect; D3DLOCKED_RECT rect;
HRESULT hr; HRESULT hr;
@@ -750,6 +760,7 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
(LONG)(rectLeft + w), (LONG)(rectLeft + w),
(LONG)(rectTop + h), (LONG)(rectTop + h),
}; };
if (GRAPHICS_FS_ORIENTATION_SWAP) { if (GRAPHICS_FS_ORIENTATION_SWAP) {
originRect.left = (topSurface_width / 2) - (GRAPHICS_FS_ORIGINAL_WIDTH / 2); originRect.left = (topSurface_width / 2) - (GRAPHICS_FS_ORIGINAL_WIDTH / 2);
originRect.top = (topSurface_height / 2) - (GRAPHICS_FS_ORIGINAL_HEIGHT / 2); originRect.top = (topSurface_height / 2) - (GRAPHICS_FS_ORIGINAL_HEIGHT / 2);
@@ -757,6 +768,21 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT; originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT;
} }
bool duplicate = false;
RECT originRect2 = originRect;
if (cfg::SCREENRESIZE->enable_screen_resize) {
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
if (scene.duplicate == cfg::ScreenDuplicateMode::CopyLeft) {
duplicate = true;
originRect2.right = originRect.left;
originRect2.left = (LONG)(originRect2.left - (originRect.right - originRect.left));
} else if (scene.duplicate == cfg::ScreenDuplicateMode::CopyRight) {
duplicate = true;
originRect2.left = originRect.right;
originRect2.right = (LONG)(originRect2.left + (originRect.right - originRect.left));
}
}
// log_misc( // log_misc(
// "graphics::d3d9", "originRect: {} {} {} {}", // "graphics::d3d9", "originRect: {} {} {} {}",
// originRect.left, originRect.top, originRect.right, originRect.bottom); // originRect.left, originRect.top, originRect.right, originRect.bottom);
@@ -768,9 +794,21 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
if (hr != D3D_OK) { if (hr != D3D_OK) {
log_warning( log_warning(
"graphics::d3d9", "graphics::d3d9",
"SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}", "SurfaceHook - StretchRect backbuffer failed for main image, rect: {} {} {} {}",
originRect.left, originRect.top, originRect.right, originRect.bottom); originRect.left, originRect.top, originRect.right, originRect.bottom);
} }
if (duplicate) {
hr = pReal->StretchRect(
backbuffer, nullptr,
topSurface, &originRect2,
D3DTEXF_LINEAR);
if (hr != D3D_OK) {
log_warning(
"graphics::d3d9",
"SurfaceHook - StretchRect backbuffer failed for the duplicate, rect: {} {} {} {}",
originRect2.left, originRect2.top, originRect2.right, originRect2.bottom);
}
}
topSurface->UnlockRect(); topSurface->UnlockRect();
// phase 2 - viewport calculation - do the actual zoom / offset math // phase 2 - viewport calculation - do the actual zoom / offset math
+26
View File
@@ -8,6 +8,7 @@
#include <ntstatus.h> #include <ntstatus.h>
#include "avs/game.h" #include "avs/game.h"
#include "games/iidx/iidx.h"
#include "util/detour.h" #include "util/detour.h"
#include "util/logging.h" #include "util/logging.h"
#include "util/utils.h" #include "util/utils.h"
@@ -22,6 +23,7 @@ static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr;
#ifdef SPICE64 #ifdef SPICE64
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr; static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
#endif #endif
static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook( static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook(
@@ -162,6 +164,17 @@ static int WINAPI GetLocaleInfoEx_hook (
return GetLocaleInfoEx_orig(lpLocaleName, LCType, lpLCData, cchData); return GetLocaleInfoEx_orig(lpLocaleName, LCType, lpLCData, cchData);
} }
#ifdef SPICE64
static BOOL WINAPI IsDBCSLeadByte_hook (
BYTE TestChar
)
{
return IsDBCSLeadByteEx(CODEPAGE_SHIFT_JIS, TestChar);
}
#endif
void hooks::lang::early_init() { void hooks::lang::early_init() {
log_info("hooks::lang", "early initialization"); log_info("hooks::lang", "early initialization");
@@ -189,6 +202,19 @@ void hooks::lang::early_init() {
GetLocaleInfoEx_hook, GetLocaleInfoEx_hook,
&GetLocaleInfoEx_orig); &GetLocaleInfoEx_orig);
} }
#ifdef SPICE64
// for TDJ subscreen search keyboard
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
log_info("hooks::lang", "hooking IsDBCSLeadByte");
detour::trampoline_try(
"kernel32.dll",
"IsDBCSLeadByte",
IsDBCSLeadByte_hook,
&IsDBCSLeadByte_orig);
}
#endif
} }
void hooks::lang::init() { void hooks::lang::init() {
+70 -26
View File
@@ -3,6 +3,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <assert.h>
#include <shlwapi.h> #include <shlwapi.h>
#include <cfg/configurator.h> #include <cfg/configurator.h>
@@ -20,7 +21,9 @@
#include "cfg/screen_resize.h" #include "cfg/screen_resize.h"
#include "easrv/easrv.h" #include "easrv/easrv.h"
#include "external/cardio/cardio_runner.h" #include "external/cardio/cardio_runner.h"
#ifndef NO_SCARD
#include "external/scard/scard.h" #include "external/scard/scard.h"
#endif
#include "games/game.h" #include "games/game.h"
#include "games/io.h" #include "games/io.h"
#include "games/bbc/bbc.h" #include "games/bbc/bbc.h"
@@ -263,7 +266,8 @@ int main_implementation(int argc, char *argv[]) {
// detect model used to load option overrides // detect model used to load option overrides
auto options_version = launcher::detect_gameversion( auto options_version = launcher::detect_gameversion(
LAUNCHER_OPTIONS->at(launcher::Options::PathToEa3Config).value LAUNCHER_OPTIONS->at(launcher::Options::PathToEa3Config).value,
LAUNCHER_OPTIONS->at(launcher::Options::PathToBootstrap).value
); );
if (!options_version.model.empty() && options_version.model.size() < 4) { if (!options_version.model.empty() && options_version.model.size() < 4) {
if (options_version.dest.size() == 1) { if (options_version.dest.size() == 1) {
@@ -297,25 +301,13 @@ int main_implementation(int argc, char *argv[]) {
cfg_run = true; cfg_run = true;
} }
// -ea, but ignored if -url is set
if (options[launcher::Options::EAmusementEmulation].value_bool() && if (options[launcher::Options::EAmusementEmulation].value_bool() &&
options[launcher::Options::ServiceURL].is_active() && !options[launcher::Options::ServiceURL].is_active()) {
!cfg::CONFIGURATOR_STANDALONE) {
log_fatal(
"launcher",
"BAD EAMUSE SETTINGS ERROR\n\n\n"
"-------------------------------------------------------------------\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"A service URL is set **AND** E-Amusement emulation is enabled.\n"
"Either remove the service URL, or disable E-Amusement emulation.\n"
"Otherwise you may experience problems logging in.\n"
"-------------------------------------------------------------------\n\n\n"
);
}
if (options[launcher::Options::EAmusementEmulation].value_bool()) {
avs::ea3::URL_SLASH = 1; avs::ea3::URL_SLASH = 1;
easrv_port = 8080; easrv_port = 8080;
} }
if (options[launcher::Options::SmartEAmusement].value_bool()) { if (options[launcher::Options::SmartEAmusement].value_bool()) {
easrv_smart = true; easrv_smart = true;
} }
@@ -471,14 +463,26 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::IIDXCameraOrderFlip].value_bool()) { if (options[launcher::Options::IIDXCameraOrderFlip].value_bool()) {
games::iidx::FLIP_CAMS = true; games::iidx::FLIP_CAMS = true;
} }
// IIDX CONNECT_CAMERA logic here for cases where user is running without -iidx module
// for now, we assume that user may be running on a cab
// (games::iidx::DISABLE_CAMS starts out as false unless user overrides)
// we will check again in IIDX module with a different default
assert(!games::iidx::DISABLE_CAMS.has_value());
if (options[launcher::Options::IIDXDisableCameras].value_bool()) { if (options[launcher::Options::IIDXDisableCameras].value_bool()) {
games::iidx::DISABLE_CAMS = true; games::iidx::DISABLE_CAMS = true;
} }
if (options[launcher::Options::IIDXCabCamAccess].is_active() &&
options[launcher::Options::IIDXCabCamAccess].value_text() == "off") {
games::iidx::DISABLE_CAMS = true;
}
if (options[launcher::Options::IIDXCamHook].value_bool()) { if (options[launcher::Options::IIDXCamHook].value_bool()) {
games::iidx::TDJ_CAMERA = true; games::iidx::TDJ_CAMERA = true;
// Disable legacy behaviour to avoid conflict // Disable legacy behaviour to avoid conflict
games::iidx::DISABLE_CAMS = true; games::iidx::DISABLE_CAMS = true;
} }
// CONNECT_CAMERA env var will be set once logging is enabled
if (options[launcher::Options::IIDXCamHookOverride].is_active()) { if (options[launcher::Options::IIDXCamHookOverride].is_active()) {
games::iidx::TDJ_CAMERA_OVERRIDE = options[launcher::Options::IIDXCamHookOverride].value_text(); games::iidx::TDJ_CAMERA_OVERRIDE = options[launcher::Options::IIDXCamHookOverride].value_text();
} }
@@ -816,6 +820,7 @@ int main_implementation(int argc, char *argv[]) {
for (auto &sextet : options[launcher::Options::SextetStreamPort].values_text()) { for (auto &sextet : options[launcher::Options::SextetStreamPort].values_text()) {
sextet_devices.emplace_back(sextet); sextet_devices.emplace_back(sextet);
} }
#ifndef NO_SCARD
if (options[launcher::Options::HIDSmartCard].value_bool()) { if (options[launcher::Options::HIDSmartCard].value_bool()) {
WINSCARD_CONFIG.cardinfo_callback = eamuse_scard_callback; WINSCARD_CONFIG.cardinfo_callback = eamuse_scard_callback;
scard_threadstart(); scard_threadstart();
@@ -835,6 +840,7 @@ int main_implementation(int argc, char *argv[]) {
WINSCARD_CONFIG.add_padding_to_felica = true; WINSCARD_CONFIG.add_padding_to_felica = true;
} }
} }
#endif
if (options[launcher::Options::CardIOHIDReaderOrderFlip].value_bool()) { if (options[launcher::Options::CardIOHIDReaderOrderFlip].value_bool()) {
CARDIO_RUNNER_FLIP = true; CARDIO_RUNNER_FLIP = true;
} }
@@ -938,6 +944,11 @@ int main_implementation(int argc, char *argv[]) {
} }
if (options[launcher::Options::DisableAudioHooks].value_bool()) { if (options[launcher::Options::DisableAudioHooks].value_bool()) {
hooks::audio::ENABLED = false; hooks::audio::ENABLED = false;
deferredlogs::defer_error_messages({
"audio hooks are forcibly disabled by user (-audiohookdisable)",
" having hooks disabled means some required audio fixes are not being applied",
" if you have audio-related crashes, you should try again after clearing this option"
});
} }
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) { if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
hooks::audio::VOLUME_HOOK_ENABLED = false; hooks::audio::VOLUME_HOOK_ENABLED = false;
@@ -1200,15 +1211,28 @@ int main_implementation(int argc, char *argv[]) {
} }
// log // log
#ifdef SPICE64 #ifndef SPICE_LINUX
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)"); #ifdef SPICE64
#elif SPICE32_LARGE_ADDRESS_AWARE log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
log_info("launcher", "SpiceTools Bootstrap (x32) (Large Address Aware) (spice2x)"); #elif SPICE32_LARGE_ADDRESS_AWARE
log_info("launcher", "SpiceTools Bootstrap (x32 - Large Address Aware) (spice2x)");
#else
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
#endif
#else #else
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)"); #ifdef SPICE64
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x) for Linux");
#else
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x) for Linux");
#endif
#endif #endif
log_info("launcher", "{}", VERSION_STRING); log_info("launcher", "{}", VERSION_STRING);
// note: distribution of modified version of this software without providing source is GPLv3 license violation.
log_info("launcher", "spice2x is free & open source; if you paid money for it, you got scammed");
log_info("launcher", "visit https://spice2x.github.io to download the latest version for free");
// log command line arguments // log command line arguments
std::ostringstream arguments; std::ostringstream arguments;
for (auto &root_option : options) { for (auto &root_option : options) {
@@ -1252,18 +1276,19 @@ int main_implementation(int argc, char *argv[]) {
for (const auto &option : options) { for (const auto &option : options) {
if (option.conflicting && option.get_definition().type != OptionType::Bool) { if (option.conflicting && option.get_definition().type != OptionType::Bool) {
conflicts += 1; conflicts += 1;
const auto& value = option.get_definition().sensitive ? "*****" : option.value;
if (launcher::USE_CMD_OVERRIDE) { if (launcher::USE_CMD_OVERRIDE) {
log_warning( log_warning(
"launcher", "launcher",
"multiple values for -{}, command line args take precedence: {}", "multiple values for -{}, command line args take precedence: {}",
option.get_definition().name, option.get_definition().name,
option.value); value);
} else { } else {
log_warning( log_warning(
"launcher", "launcher",
"multiple values for -{}, spicecfg values take precedence: {}", "multiple values for -{}, spicecfg values take precedence: {}",
option.get_definition().name, option.get_definition().name,
option.value); value);
} }
} }
} }
@@ -1303,7 +1328,7 @@ int main_implementation(int argc, char *argv[]) {
// SDVXFullscreenLandscape disabled due to it messing with in-game camera angle // SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
// FullscreenOrientationFlip continues to live on, however. // FullscreenOrientationFlip continues to live on, however.
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool()) { if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!"); log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!");
} }
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) { // if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
@@ -1318,8 +1343,15 @@ int main_implementation(int argc, char *argv[]) {
GRAPHICS_FS_ORIENTATION_SWAP = true; GRAPHICS_FS_ORIENTATION_SWAP = true;
} }
if (games::iidx::DISABLE_CAMS.has_value() &&
games::iidx::DISABLE_CAMS.value() &&
!cfg::CONFIGURATOR_STANDALONE) {
log_misc("launcher", "CONNECT_CAMERA env var set to 0");
SetEnvironmentVariable("CONNECT_CAMERA", "0");
}
// deleted options // deleted options
if (options[launcher::Options::OpenKFControl].value_bool()) { if (options[launcher::Options::OpenKFControl].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version."); log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version.");
} }
if (options[launcher::Options::VREnable].value_bool()) { if (options[launcher::Options::VREnable].value_bool()) {
@@ -1647,6 +1679,14 @@ int main_implementation(int argc, char *argv[]) {
attach_io = true; attach_io = true;
attach_mfc = true; attach_mfc = true;
break; break;
// Mahjong Fight Club - 2025?
// they removed allinone.dll and moved i/o into system.dll
} else if (check_dll("system.dll") && fileutils::file_exists("data/mfc.ini")) {
avs::game::DLL_NAME = "system.dll";
attach_io = true;
attach_mfc = true;
break;
} }
// FutureTomTom // FutureTomTom
@@ -1791,7 +1831,7 @@ int main_implementation(int argc, char *argv[]) {
// usage error // usage error
if (!cfg::CONFIGURATOR_STANDALONE if (!cfg::CONFIGURATOR_STANDALONE
&& (!CHECK_DLL_IGNORE_ARCH)) { && (!CHECK_DLL_IGNORE_ARCH)) {
log_fatal( log_warning(
"launcher", "launcher",
"module auto detection failed!\n\n" "module auto detection failed!\n\n"
"This usually means one of the following:\n\n" "This usually means one of the following:\n\n"
@@ -1799,6 +1839,8 @@ int main_implementation(int argc, char *argv[]) {
" 2. XML files in prop directory are malformed or missing, or\n" " 2. XML files in prop directory are malformed or missing, or\n"
" 3. You passed in invalid options (usually, path overrides like -exec)\n\n" " 3. You passed in invalid options (usually, path overrides like -exec)\n\n"
); );
log_fatal("launcher", "module auto detection failed, see log.txt for troubleshooting");
} }
break; break;
@@ -2314,8 +2356,10 @@ int main_implementation(int argc, char *argv[]) {
// debug hook // debug hook
debughook::detach(); debughook::detach();
#ifndef NO_SCARD
// scard // scard
scard_fini(); scard_fini();
#endif
// stop reader thread in case it was running // stop reader thread in case it was running
stop_reader_thread(); stop_reader_thread();
+55 -14
View File
@@ -6,6 +6,12 @@
#include <fstream> #include <fstream>
#ifdef NO_SCARD
#define WITH_SCARD 0
#else
#define WITH_SCARD 1
#endif
static const std::vector<std::string> CATEGORY_ORDER_API = { static const std::vector<std::string> CATEGORY_ORDER_API = {
"SpiceCompanion and API", "SpiceCompanion and API",
"API (Serial)", "API (Serial)",
@@ -450,13 +456,30 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
{ {
.title = "IIDX Disable Cameras", // IIDXDisableCameras
.title = "IIDX Disable Cameras (DEPRECATED - no longer needed)",
.name = "iidxdisablecams", .name = "iidxdisablecams",
.desc = "Disables cameras", .desc = "Disables cameras",
.type = OptionType::Bool, .type = OptionType::Bool,
.hidden = true,
.game_name = "Beatmania IIDX", .game_name = "Beatmania IIDX",
.category = "Game Options", .category = "Game Options",
}, },
{
// IIDXCabCamAccess
.title = "IIDX Use Official AC Cams",
.name = "iidxcabcams",
.desc = "For IIDX25+, allow direct access to cameras from real arcade cabinets. "
"Only turn this on if you have OFFICIAL arcade cameras connected to the correct USB ports. Default: auto",
.type = OptionType::Enum,
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.elements = {
{"auto", ""},
{"off", ""},
{"on", ""}
},
},
{ {
// IIDXCamHook // IIDXCamHook
.title = "IIDX Cam Hook", .title = "IIDX Cam Hook",
@@ -491,10 +514,10 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.display_name = "iidxcamhookoffset", .display_name = "iidxcamhookoffset",
.aliases = "iidxcamhookoffset", .aliases = "iidxcamhookoffset",
.desc = "Override DLL offsets for camera hook; " .desc = "Override DLL offsets for camera hook; "
"format: 4 hex values separated by commas " "format: 5 hex values separated by commas "
"(offset_hook_a, offset_hook_b, offset_textures, offset_d3d_device)", "(offset_hook_a, offset_textures, offset_camera_manager, offset_d3d_device, offset_afp_texture)",
.type = OptionType::Text, .type = OptionType::Text,
.setting_name = "0x5817a0,0x5ea3b0,0x6fffbd8,0xe0", .setting_name = "0x5817a0,0x6fffbd8,0xbbae40,0xe0,0x30",
.game_name = "Beatmania IIDX", .game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
@@ -1385,21 +1408,27 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "scard", .name = "scard",
.desc = "Detects and uses HID smart card readers for card input. Developed for ACR122U reader", .desc = "Detects and uses HID smart card readers for card input. Developed for ACR122U reader",
.type = OptionType::Bool, .type = OptionType::Bool,
.hidden = !WITH_SCARD,
.category = "NFC Card Readers", .category = "NFC Card Readers",
.disabled = !WITH_SCARD,
}, },
{ {
.title = "HID SmartCard Order Flip", .title = "HID SmartCard Order Flip",
.name = "scardflip", .name = "scardflip",
.desc = "Flips the order of detection for P1/P2", .desc = "Flips the order of detection for P1/P2",
.type = OptionType::Bool, .type = OptionType::Bool,
.hidden = !WITH_SCARD,
.category = "NFC Card Readers", .category = "NFC Card Readers",
.disabled = !WITH_SCARD,
}, },
{ {
.title = "HID SmartCard Order Toggle", .title = "HID SmartCard Order Toggle",
.name = "scardtoggle", .name = "scardtoggle",
.desc = "Toggles reader between P1/P2 using the NumLock key state", .desc = "Toggles reader between P1/P2 using the NumLock key state",
.type = OptionType::Bool, .type = OptionType::Bool,
.hidden = !WITH_SCARD,
.category = "NFC Card Readers", .category = "NFC Card Readers",
.disabled = !WITH_SCARD,
}, },
{ {
// HIDSmartCardIdConvert // HIDSmartCardIdConvert
@@ -1413,12 +1442,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"For example, abcd56780123 is converted to e00401abcd567801 on card in. This results in different card numbers so " "For example, abcd56780123 is converted to e00401abcd567801 on card in. This results in different card numbers so "
"be careful when connecting to servers!", "be careful when connecting to servers!",
.type = OptionType::Enum, .type = OptionType::Enum,
.hidden = !WITH_SCARD,
.category = "NFC Card Readers", .category = "NFC Card Readers",
.elements = { .elements = {
{"legacy", "Use cards as-is"}, {"legacy", "Use cards as-is"},
{"fix", "Fix bad cards only"}, {"fix", "Fix bad cards only"},
{"all", "Force all cards"}, {"all", "Force all cards"},
}, },
.disabled = !WITH_SCARD,
}, },
{ {
// SextetStreamPort // SextetStreamPort
@@ -2608,8 +2639,19 @@ std::vector<Option> launcher::merge_options(
return merged; return merged;
} }
std::string launcher::detect_bootstrap_release_code() { std::string launcher::detect_bootstrap_release_code(const std::string& bootstrap_user) {
std::string bootstrap_path = "prop/bootstrap.xml"; std::string bootstrap_path;
if (!bootstrap_user.empty()) {
bootstrap_path = bootstrap_user;
} else if (fileutils::file_exists("prop/bootstrap.xml")) {
bootstrap_path = "prop/bootstrap.xml";
}
if (bootstrap_path.empty()) {
log_warning("options", "unable to detect bootstrap.xml file");
return "";
}
// load XML // load XML
tinyxml2::XMLDocument bootstrap; tinyxml2::XMLDocument bootstrap;
@@ -2632,7 +2674,7 @@ std::string launcher::detect_bootstrap_release_code() {
return ""; return "";
} }
static launcher::GameVersion detect_gameversion_ident() { static launcher::GameVersion detect_gameversion_ident(const std::string& bootstrap_user) {
// detect ea3-ident path // detect ea3-ident path
std::string ident_path; std::string ident_path;
@@ -2678,7 +2720,7 @@ static launcher::GameVersion detect_gameversion_ident() {
auto node_ext = node_soft->FirstChildElement("ext"); auto node_ext = node_soft->FirstChildElement("ext");
if (node_ext) { if (node_ext) {
version.ext = node_ext->GetText(); version.ext = node_ext->GetText();
auto bootstrap_ext = launcher::detect_bootstrap_release_code(); auto bootstrap_ext = launcher::detect_bootstrap_release_code(bootstrap_user);
if (version.ext.size() != 10 && bootstrap_ext.size() == 10) { if (version.ext.size() != 10 && bootstrap_ext.size() == 10) {
version.ext = bootstrap_ext; version.ext = bootstrap_ext;
} else if (bootstrap_ext.size() == 10) { } else if (bootstrap_ext.size() == 10) {
@@ -2714,8 +2756,7 @@ static launcher::GameVersion detect_gameversion_ident() {
return launcher::GameVersion(); return launcher::GameVersion();
} }
launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user) { launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user, const std::string& bootstrap_user) {
// detect ea3-config path // detect ea3-config path
std::string ea3_path; std::string ea3_path;
if (!ea3_user.empty()) { if (!ea3_user.empty()) {
@@ -2729,7 +2770,7 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
} }
if (ea3_path.empty()) { if (ea3_path.empty()) {
log_warning("options", "unable to detect EA3 configuration file"); log_warning("options", "unable to detect EA3 configuration file");
return detect_gameversion_ident(); return detect_gameversion_ident(bootstrap_user);
} }
// load XML // load XML
@@ -2769,7 +2810,7 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
if (ea3_config.Parse(xml_str.c_str()) != tinyxml2::XML_SUCCESS) { if (ea3_config.Parse(xml_str.c_str()) != tinyxml2::XML_SUCCESS) {
// if we still failed, give up // if we still failed, give up
log_warning("options", "unable to parse fixed xml"); log_warning("options", "unable to parse fixed xml");
return detect_gameversion_ident(); return detect_gameversion_ident(bootstrap_user);
} }
} }
@@ -2800,7 +2841,7 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
auto node_ext = node_soft->FirstChildElement("ext"); auto node_ext = node_soft->FirstChildElement("ext");
if (node_ext) { if (node_ext) {
version.ext = node_ext->GetText(); version.ext = node_ext->GetText();
auto bootstrap_ext = launcher::detect_bootstrap_release_code(); auto bootstrap_ext = launcher::detect_bootstrap_release_code(bootstrap_user);
if (version.ext.size() != 10 && bootstrap_ext.size() == 10) { if (version.ext.size() != 10 && bootstrap_ext.size() == 10) {
version.ext = bootstrap_ext; version.ext = bootstrap_ext;
} else if (bootstrap_ext.size() == 10) { } else if (bootstrap_ext.size() == 10) {
@@ -2833,5 +2874,5 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
// error // error
log_warning("options", "unable to find /ea3/soft/model in {}", ea3_path); log_warning("options", "unable to find /ea3/soft/model in {}", ea3_path);
return detect_gameversion_ident(); return detect_gameversion_ident(bootstrap_user);
} }
+3 -2
View File
@@ -55,6 +55,7 @@ namespace launcher {
LoadIIDXModule, LoadIIDXModule,
IIDXCameraOrderFlip, IIDXCameraOrderFlip,
IIDXDisableCameras, IIDXDisableCameras,
IIDXCabCamAccess,
IIDXCamHook, IIDXCamHook,
IIDXCamHookRatio, IIDXCamHookRatio,
IIDXCamHookOverride, IIDXCamHookOverride,
@@ -279,6 +280,6 @@ namespace launcher {
std::string ext; std::string ext;
}; };
std::string detect_bootstrap_release_code(); std::string detect_bootstrap_release_code(const std::string& bootstrap_user);
GameVersion detect_gameversion(const std::string& ea3_user); GameVersion detect_gameversion(const std::string& ea3_user, const std::string& bootstrap_user);
} }
+4
View File
@@ -1,5 +1,6 @@
#include "signal.h" #include "signal.h"
#include <future>
#include <exception> #include <exception>
#include <windows.h> #include <windows.h>
@@ -174,6 +175,9 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
log_warning("signal", "minidump creation function not available, skipping"); log_warning("signal", "minidump creation function not available, skipping");
} }
// this will stall all UI threads for this process
show_popup_for_crash();
log_fatal("signal", "end"); log_fatal("signal", "end");
} }
+25
View File
@@ -34,6 +34,7 @@ namespace wintouchemu {
bool FORCE = false; bool FORCE = false;
bool INJECT_MOUSE_AS_WM_TOUCH = false; bool INJECT_MOUSE_AS_WM_TOUCH = false;
bool LOG_FPS = false; bool LOG_FPS = false;
bool ADD_TOUCH_FLAG_PRIMARY = false;
static inline bool is_emu_enabled() { static inline bool is_emu_enabled() {
return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR; return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR;
@@ -187,16 +188,28 @@ namespace wintouchemu {
switch (touch_event->type) { switch (touch_event->type) {
case TOUCH_DOWN: case TOUCH_DOWN:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_DOWN; touch_input->dwFlags |= TOUCHEVENTF_DOWN;
} }
break; break;
case TOUCH_MOVE: case TOUCH_MOVE:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_MOVE; touch_input->dwFlags |= TOUCHEVENTF_MOVE;
} }
break; break;
case TOUCH_UP: case TOUCH_UP:
// don't check valid so that this touch ID can be released // don't check valid so that this touch ID can be released
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_UP; touch_input->dwFlags |= TOUCHEVENTF_UP;
break; break;
} }
@@ -238,16 +251,28 @@ namespace wintouchemu {
switch (mouse_state.touch_event) { switch (mouse_state.touch_event) {
case TOUCHEVENTF_DOWN: case TOUCHEVENTF_DOWN:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_DOWN; touch_input->dwFlags |= TOUCHEVENTF_DOWN;
} }
break; break;
case TOUCHEVENTF_MOVE: case TOUCHEVENTF_MOVE:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_MOVE; touch_input->dwFlags |= TOUCHEVENTF_MOVE;
} }
break; break;
case TOUCHEVENTF_UP: case TOUCHEVENTF_UP:
// don't check valid so that this touch ID can be released // don't check valid so that this touch ID can be released
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_UP; touch_input->dwFlags |= TOUCHEVENTF_UP;
break; break;
} }
+1
View File
@@ -8,6 +8,7 @@ namespace wintouchemu {
extern bool FORCE; extern bool FORCE;
extern bool INJECT_MOUSE_AS_WM_TOUCH; extern bool INJECT_MOUSE_AS_WM_TOUCH;
extern bool LOG_FPS; extern bool LOG_FPS;
extern bool ADD_TOUCH_FLAG_PRIMARY;
void hook(const char *window_title, HMODULE module = nullptr, int delay_in_s=0); void hook(const char *window_title, HMODULE module = nullptr, int delay_in_s=0);
void hook_title_ends( void hook_title_ends(
+4 -3
View File
@@ -270,13 +270,14 @@ void overlay::SpiceOverlay::init() {
ImFontConfig config {}; ImFontConfig config {};
config.MergeMode = true; config.MergeMode = true;
log_misc("overlay", "loading fonts, failures are not fatal");
add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesCyrillic()); add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesCyrillic());
add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese()); add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese()); add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean()); add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean());
add_font("cordia.ttc", &config, io.Fonts->GetGlyphRangesThai()); add_font("cordia.ttf", &config, io.Fonts->GetGlyphRangesThai());
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesVietnamese()); add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesVietnamese());
// add special font // add special font
if (avs::game::is_model("LDJ")) { if (avs::game::is_model("LDJ")) {
+37 -4
View File
@@ -556,9 +556,16 @@ namespace overlay::windows {
} }
// disclaimer // disclaimer
// note: distribution of modified version of this software without providing source is GPLv3 license violation.
ImGui::TextColored( ImGui::TextColored(
ImVec4(1, 0.5f, 0.5f, 1.f), ImVec4(1, 0.5f, 0.5f, 1.f),
"Do NOT stream or upload game data anywhere public! Support arcades when you can. Thanks."); "spice2x is free & open source; if you paid money for it, you got scammed.");
if (cfg::CONFIGURATOR_STANDALONE) {
ImGui::SameLine();
if (ImGui::TextLink(PROJECT_URL)) {
launch_shell(PROJECT_URL);
}
}
} }
void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) { void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) {
@@ -1720,6 +1727,13 @@ namespace overlay::windows {
std::vector<int> analogs_midi_indices; std::vector<int> analogs_midi_indices;
if (this->analogs_devices_selected >= 0) { if (this->analogs_devices_selected >= 0) {
auto device = this->analogs_devices.at(this->analogs_devices_selected); auto device = this->analogs_devices.at(this->analogs_devices_selected);
// add a tooltip to devices selector
if (!device->name.empty()) {
ImGui::SameLine();
ImGui::HelpMarker(device->name.c_str());
}
switch (device->type) { switch (device->type) {
case rawinput::MOUSE: { case rawinput::MOUSE: {
@@ -2029,6 +2043,11 @@ namespace overlay::windows {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine();
if (ImGui::Button("Reset")) {
analog.resetValues();
}
// clean up // clean up
ImGui::EndPopup(); ImGui::EndPopup();
} }
@@ -2821,7 +2840,10 @@ namespace overlay::windows {
std::string current_item = option.value_text(); std::string current_item = option.value_text();
for (auto &element : definition.elements) { for (auto &element : definition.elements) {
if (element.first == current_item) { if (element.first == current_item) {
current_item += fmt::format(" ({})", element.second); if (!element.second.empty()) {
current_item += fmt::format(" ({})", element.second);
}
break;
} }
} }
if (current_item.empty()) { if (current_item.empty()) {
@@ -2829,11 +2851,12 @@ namespace overlay::windows {
} }
if (ImGui::BeginCombo("##combo", current_item.c_str(), 0)) { if (ImGui::BeginCombo("##combo", current_item.c_str(), 0)) {
for (auto &element : definition.elements) { for (auto &element : definition.elements) {
bool selected = current_item == element.first;
std::string label = element.first; std::string label = element.first;
if (!element.second.empty()) { if (!element.second.empty()) {
label += fmt::format(" ({})", element.second); label += fmt::format(" ({})", element.second);
} }
bool selected = current_item == label;
if (ImGui::Selectable(label.c_str(), selected)) { if (ImGui::Selectable(label.c_str(), selected)) {
this->options_dirty = true; this->options_dirty = true;
option.value = element.first; option.value = element.first;
@@ -2904,13 +2927,20 @@ namespace overlay::windows {
} }
void Config::build_about() { void Config::build_about() {
#ifndef SPICE_LINUX
ImGui::TextUnformatted(std::string( ImGui::TextUnformatted(std::string(
"spice2x (a fork of SpiceTools)\r\n" "spice2x (a fork of SpiceTools)\r\n"
"=========================\r\n" + "=========================\r\n" +
to_string(VERSION_STRING)).c_str()); to_string(VERSION_STRING)).c_str());
#else
ImGui::TextUnformatted(std::string(
"spice2x (a fork of SpiceTools) for Linux\r\n"
"=========================\r\n" +
to_string(VERSION_STRING)).c_str());
#endif
ImGui::TextUnformatted(""); ImGui::TextUnformatted("");
if (ImGui::Button(PROJECT_URL)) { if (ImGui::TextLink(PROJECT_URL)) {
launch_shell(PROJECT_URL); launch_shell(PROJECT_URL);
} }
@@ -2971,6 +3001,7 @@ namespace overlay::windows {
void Config::build_menu(int *game_selected) { void Config::build_menu(int *game_selected) {
bool about_popup = false; bool about_popup = false;
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 1.0f));
if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenuBar()) {
// [spice2x] // [spice2x]
@@ -3031,6 +3062,8 @@ namespace overlay::windows {
ImGui::EndMenuBar(); ImGui::EndMenuBar();
} }
ImGui::PopStyleColor(); // ImGuiCol_PopupBg
// workaround for popups triggered by menu, see https://github.com/ocornut/imgui/issues/331 // workaround for popups triggered by menu, see https://github.com/ocornut/imgui/issues/331
if (about_popup) { if (about_popup) {
ImGui::OpenPopup("About##topbarpopup"); ImGui::OpenPopup("About##topbarpopup");
@@ -288,6 +288,12 @@ namespace overlay::windows {
this->reload_local_patches(); this->reload_local_patches();
} }
if (avs::game::DLL_NAME == "kamunity.dll") {
ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "Patches are not supported for Unity-based games.");
ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "Instead, look for downloads of pre-patched DLLs.");
return;
}
// game code info // game code info
std::string identifiers; std::string identifiers;
identifiers += avs::game::get_identifier() + "\n\n"; identifiers += avs::game::get_identifier() + "\n\n";
@@ -55,6 +55,7 @@ namespace overlay::windows {
scene.offset_y = 0; scene.offset_y = 0;
scene.scale_x = 1.f; scene.scale_x = 1.f;
scene.scale_y = 1.f; scene.scale_y = 1.f;
scene.duplicate = cfg::ScreenDuplicateMode::None;
} }
cfg::SCREENRESIZE->enable_window_resize = false; cfg::SCREENRESIZE->enable_window_resize = false;
@@ -149,6 +150,17 @@ namespace overlay::windows {
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value."); ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
} }
static const char* dupe_items[] = { "None", "Copy Left", "Copy Right" };
ImGui::Combo(
"Duplicate",
reinterpret_cast<int *>(&scene.duplicate),
dupe_items,
ARRAYSIZE(dupe_items));
ImGui::SameLine();
ImGui::HelpMarker(
"Show an identical copy of the image on the left or right, allowing you to achieve "
"wrap-around effect when an offset is set.");
ImGui::EndDisabled(); ImGui::EndDisabled();
} }
+11 -13
View File
@@ -347,40 +347,38 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
if (hid_handle != INVALID_HANDLE_VALUE) { if (hid_handle != INVALID_HANDLE_VALUE) {
// check manufacturer string // check manufacturer string
std::wstring man_ws; std::string man_ws;
wchar_t man_str_buffer[256] {}; wchar_t man_str_buffer[256] {};
if (HidD_GetManufacturerString(hid_handle, man_str_buffer, sizeof(man_str_buffer))) { if (HidD_GetManufacturerString(hid_handle, man_str_buffer, sizeof(man_str_buffer))) {
man_ws = std::wstring(man_str_buffer); man_ws = wchar_to_u8(man_str_buffer);
} }
// get product string // get product string
std::wstring prod_ws; std::string prod_ws;
wchar_t prod_str_buffer[256] {}; wchar_t prod_str_buffer[256] {};
if (HidD_GetProductString(hid_handle, prod_str_buffer, sizeof(prod_str_buffer))) { if (HidD_GetProductString(hid_handle, prod_str_buffer, sizeof(prod_str_buffer))) {
prod_ws = std::wstring(prod_str_buffer); prod_ws = wchar_to_u8(prod_str_buffer);
} }
// For Bluetooth LE HID devices (e.g., newer Xbox controllers) HidD_GetProductString // For Bluetooth LE HID devices (e.g., newer Xbox controllers) HidD_GetProductString
// and HidD_GetManufacturerString will return blank strings. // and HidD_GetManufacturerString will return blank strings.
// https://docs.microsoft.com/en-us/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html // https://docs.microsoft.com/en-us/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html
if (man_ws.empty() && prod_ws.empty()) { if (man_ws.empty() && prod_ws.empty()) {
prod_ws = s2ws(device_description); prod_ws = device_description;
} }
// build desc string // build desc string
std::wstring desc_ws; std::string desc_ws;
if (string_begins_with(prod_ws, man_ws)) { if (string_begins_with(prod_ws, man_ws)) {
desc_ws = prod_ws; desc_ws = prod_ws;
} else { } else {
desc_ws = man_ws; desc_ws = man_ws;
if (!man_ws.empty() && !prod_ws.empty()) { if (!man_ws.empty() && !prod_ws.empty()) {
desc_ws += L" "; desc_ws += " ";
} }
desc_ws += prod_ws; desc_ws += prod_ws;
} }
new_device.desc = desc_ws;
// convert to normal string
new_device.desc = ws2s(desc_ws);
// get attributes // get attributes
if (!HidD_GetAttributes(hid_handle, &hid_attributes)) { if (!HidD_GetAttributes(hid_handle, &hid_attributes)) {
@@ -1148,7 +1146,7 @@ std::string rawinput::RawInputManager::rawinput_get_device_description(const raw
// get property // get property
DWORD desc_size = 0; DWORD desc_size = 0;
if (SetupDiGetDeviceRegistryProperty( if (SetupDiGetDeviceRegistryPropertyW(
devinfo, &devinfo_data, SPDRP_DEVICEDESC, nullptr, nullptr, 0, &desc_size)) devinfo, &devinfo_data, SPDRP_DEVICEDESC, nullptr, nullptr, 0, &desc_size))
{ {
continue; continue;
@@ -1160,14 +1158,14 @@ std::string rawinput::RawInputManager::rawinput_get_device_description(const raw
// allocate buffer // allocate buffer
auto desc_data = std::make_unique<BYTE[]>(desc_size); auto desc_data = std::make_unique<BYTE[]>(desc_size);
if (!SetupDiGetDeviceRegistryProperty( if (!SetupDiGetDeviceRegistryPropertyW(
devinfo, &devinfo_data, SPDRP_DEVICEDESC, nullptr, desc_data.get(), desc_size, nullptr)) devinfo, &devinfo_data, SPDRP_DEVICEDESC, nullptr, desc_data.get(), desc_size, nullptr))
{ {
continue; continue;
} }
// kthxbye // kthxbye
device_description = std::string(reinterpret_cast<char *>(desc_data.get())); device_description = wchar_to_u8(reinterpret_cast<PWCHAR>(desc_data.get()));
} }
} }
} }
+1
View File
@@ -3,6 +3,7 @@ developers when you can. Sources should be included, have fun!
spice2x fork maintainers: spice2x fork maintainers:
* sp2xdev team * sp2xdev team
* the lab
SpiceTools original contributors: SpiceTools original contributors:
* cube, felix, dinsfire, nolm, s2, crazyredmachine (jotaro44), mon, nibs * cube, felix, dinsfire, nolm, s2, crazyredmachine (jotaro44), mon, nibs
+4 -2
View File
@@ -49,8 +49,10 @@ namespace deferredlogs {
if (is_crash) { if (is_crash) {
msg += " the game has crashed\n"; msg += " the game has crashed\n";
msg += " share this entire log file with someone for troubleshooting (log.txt)\n"; msg += " * share this entire log file with someone for troubleshooting (log.txt)\n";
msg += " spice will also attempt to create a minidump (minidump.dmp)\n"; msg += " * spice will also attempt to create a minidump (minidump.dmp)\n";
msg += " minidump should only be shared with people you trust as it may contain\n";
msg += " sensitive data (PCBID, card ID, etc)\n";
msg += "\n"; msg += "\n";
} }
+1 -1
View File
@@ -12,7 +12,7 @@ namespace {
{ {
"msvcr100.dll", "msvcr100.dll",
"Visual Studio 2010 (VC++ 10.0) SP1 Redistributable", "Visual Studio 2010 (VC++ 10.0) SP1 Redistributable",
"Download and install VisualCppRedist_AIO_x86_x64.exe from https://github.com/abbodi1406/vcredist/releases/latest", "Download and install from https://github.com/spice2x/spice2x.github.io/wiki/Visual-Cpp-Redistributable-Runtimes",
}, },
{ {
"d3dx9_43.dll", "d3dx9_43.dll",
+2 -1
View File
@@ -43,7 +43,8 @@ static inline void load_library_fail(const std::string &file_name, bool fatal) {
" b. http://www.dependencywalker.com/ (for old OS) \n" " b. http://www.dependencywalker.com/ (for old OS) \n"
, file_name) }; , file_name) };
if (fatal) { if (fatal) {
log_fatal("libutils", "{}", info_str); log_warning("libutils", "{}", info_str);
log_fatal("libutils", "DLL failed to load: {}, see log.txt for troubleshooting", file_name);
} else { } else {
log_warning("libutils", "{}", info_str); log_warning("libutils", "{}", info_str);
} }
+69
View File
@@ -1,4 +1,10 @@
#include <atomic>
#include <chrono>
#include <future>
#include "logging.h" #include "logging.h"
#include "build/defs.h"
#include "hooks/graphics/graphics.h"
std::string_view log_get_datetime() { std::string_view log_get_datetime() {
return log_get_datetime(time(nullptr)); return log_get_datetime(time(nullptr));
@@ -12,3 +18,66 @@ std::string_view log_get_datetime(std::time_t now) {
return buf; return buf;
} }
static void show_popup(const std::string text) {
static std::once_flag shown;
std::call_once(shown, [text]() {
// minimize all windows
// only needed because in multi-monitor full screen games, MessageBox fails to show above the game
for (auto &hwnd : GRAPHICS_WINDOWS) {
ShowWindow(hwnd, SW_FORCEMINIMIZE);
}
// schedule a worker to terminate the game in 30 seconds in case user can't dismiss UI
std::atomic_bool cancel_popup = false;
auto worker = std::async(std::launch::async, [&cancel_popup]{
for (int ms = 300; ms > 0; ms--) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (cancel_popup) {
return;
}
}
launcher::stop_subsystems();
launcher::kill();
std::terminate();
});
// MessageBox will block until user presses OK
const std::string title = "spice2x (" + to_string(VERSION_STRING_CFG) + ")";
MessageBox(
nullptr,
text.c_str(),
title.c_str(),
MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);
// cancel the worker since the user acknowledged the popup
cancel_popup = true;
});
}
void show_popup_for_crash() {
std::string text;
text += "Game has crashed.\n\n";
text += "Check log.txt and look for error messages near the end of file.\n\n";
text += "Unsure what to do next?\n";
text += " * update spice2x to the latest version\n";
text += " * check the FAQ on spice2x github wiki\n";
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
show_popup(text);
}
void show_popup_for_fatal_error(std::string message) {
std::string text;
text += "A fatal error was encountered. For details, check log.txt.\n\n";
text += "----------------------------------------------------------\n";
text += message;
text += "----------------------------------------------------------\n\n";
text += "Unsure what to do next?\n";
text += " * update spice2x to the latest version\n";
text += " * check the FAQ on spice2x github wiki\n";
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
show_popup(text);
}
+10 -2
View File
@@ -71,20 +71,28 @@ struct fmt::formatter<fmt_hresult> {
} }
}; };
void show_popup_for_crash();
void show_popup_for_fatal_error(std::string message);
// misc log // misc log
#define LOG_FORMAT(level, module, fmt_str, ...) fmt::format(FMT_COMPILE("{}" fmt_str "\n"), \ #define LOG_FORMAT(level, module, fmt_str, ...) fmt::format(FMT_COMPILE("{}" fmt_str "\n"), \
fmt_log { std::time(nullptr), level, module }, ## __VA_ARGS__) fmt_log { std::time(nullptr), level, module }, ## __VA_ARGS__)
#define LOG_FORMAT_POPUP(module, fmt_str, ...) fmt::format(FMT_COMPILE("{}: " fmt_str "\n"), module, ## __VA_ARGS__)
#define log_misc(module, format_str, ...) logger::push( \ #define log_misc(module, format_str, ...) logger::push( \
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY) LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
#define log_info(module, format_str, ...) logger::push( \ #define log_info(module, format_str, ...) logger::push( \
LOG_FORMAT("I", module, format_str, ## __VA_ARGS__), logger::Style::DEFAULT) LOG_FORMAT("I", module, format_str, ## __VA_ARGS__), logger::Style::DEFAULT)
#define log_warning(module, format_str, ...) logger::push( \ #define log_warning(module, format_str, ...) logger::push( \
LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::YELLOW) LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::YELLOW)
#define log_fatal(module, format_str, ...) { \ #define log_fatal(module, format_str, ...) { \
logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \ logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \
logger::push(LOG_FORMAT("F", "spice", "encountered a fatal error, you can close the window or press ctrl + c"), logger::Style::RED); \ show_popup_for_fatal_error(LOG_FORMAT_POPUP(module, format_str, ## __VA_ARGS__)); \
launcher::stop_subsystems(); \ launcher::stop_subsystems(); \
Sleep(10000); \
launcher::kill(); \ launcher::kill(); \
std::terminate(); \ std::terminate(); \
} ((void) 0 ) } ((void) 0 )
+10
View File
@@ -339,4 +339,14 @@ static inline bool parse_width_height(const std::string wh, std::pair<uint32_t,
} else { } else {
return false; return false;
} }
}
static inline std::string wchar_to_u8(const wchar_t* wstr) {
int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
if (size <= 1) {
return "";
}
std::string out(size - 1, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, out.data(), size - 1, nullptr, nullptr);
return out;
} }