Compare commits

...

114 Commits

Author SHA1 Message Date
sp2xdev 0fc3aa5818 update changelog 2025-09-30 13:07:49 -07:00
sp2xdev 276874f882 update options 2025-09-29 19:28:08 -07:00
sp2xdev 1fdd270480 hide bio2 firmware update option 2025-09-29 19:03:13 -07:00
bicarus-dev 07cb6048a0 Add explanation for common exception codes (#386)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Add a little warning message that shows when well-known exception codes
are encountered & caught by signal hooks.

## Testing
Tested by injecting bad instructions into the DLL.
2025-09-28 23:49:08 -07:00
bicarus-dev 61d79f76b2 ea3: remove verbose debug logs by default (#385)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Recently distributed iidx data has this enabled in ea3-config.xml by
default, exposing people's PCBID in the logs.

Find the node in the XML and remove it on boot, unless user overrides it
with a new option.

## Testing
Tested using asphyxia.
2025-09-28 20:31:48 -07:00
sp2xdev 7335d07e30 update changelog 2025-09-27 18:38:57 -07:00
bicarus-dev 9f99b71112 avs: when nvram/raw folder creation fails, show a warning message when the game eventually crashes (#383)
## Link to GitHub Issue, if one exists
#345 

## Description of change
When the directory specified in the mounttable (in avs-config.xml) lead
to bad path, remember this and show a warning message block before the
game crashes.

## Testing
Tested with simulated bad mounttable set up.
2025-09-27 18:16:20 -07:00
sp2xdev 5c3921a99c fix up error messages 2025-09-27 14:31:29 -07:00
sp2xdev 9fc397b8bc update loadlibrary failure message, again 2025-09-27 02:16:28 -07:00
bicarus-dev ddc8061268 iidx: apply signature patch for ASIO compatibility fix in iidx32 (#381)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Apply this fix automatically when needed -
https://github.com/two-torial/sp2xpatcher/pull/33

## Testing
Tested iidx31 to see that the patch does not apply
Checked -audiohookdisable to see that the logic gets skipped
Tested 2025041500 and 2025082500.
2025-09-26 01:26:49 -07:00
sp2xdev c0698a3872 update changelog 2025-09-21 19:44:47 -07:00
kazakhstan-sila 1ed3521091 patcher: add gitadora boot.dll to extra DLL list (#379)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Allow importing patches for `boot.dll`

## Testing
Observed patch for the new DLL being attempted to be imported in the
logs.
2025-09-21 12:51:04 -07:00
bicarus-dev 0dda920448 iidx, sdvx: show error text when subscreen fails to render (#378)
## Link to GitHub Issue, if one exists
#345 

## Description of change
For IIDX / SDVX, when the subscreen surface failed to capture, display
an error message, suggesting the user to patch the game as needed.

Prior to this change, the subscreen overlay just showed an invisible
window if this happened.

## Testing
Tested IIDX windowed / full screen, and voltex full screen.
2025-09-21 00:20:41 -07:00
bicarus-dev 5f7580b69e pc: add polaris chord button lights (#377)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add lights output for the buttons lamp array. Only implemented the top
row of lights and not all of them, this is good enough.

## Testing
Tested with a Nostroller and also spiceremote.py.
2025-09-20 16:37:39 -07:00
sp2xdev 3ef1b3ecaa update changelog 2025-09-20 04:31:11 -07:00
bicarus-dev 931811da14 iidx,sdvx: detect SuperstepSound init failures and log a message upon crash (#376)
## Link to GitHub Issue, if one exists
#345 

## Description of change
If we see SuperstepSound failure during boot, remember it, and when the
game eventually crashes, log a series of warning messages to surface why
the game likely failed to launch.

## Testing
Tested both iidx and sdvx with an audio device set to not allow
exclusive mode.
2025-09-20 04:17:40 -07:00
bicarus-dev 7624ded512 pc: add option to translate absolute analog values to fader input (#375)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add a new option that allows absolute position analog input, such as
SDVX controller knobs and IIDX turntables, to be bound as input for
Polaris Chord faders.

In practice this means turning a knob counter-clockwise results in fader
being held left (relative to velocity), and vice versa.

## Testing
Tested using FauceTwo and a mouse.
2025-09-20 03:40:18 -07:00
sp2xdev a5252939dd update iidx audio patch signature 2025-09-20 03:40:04 -07:00
GEEKiDoS 808ac0c557 Fix msvc compiler warnings (#374)
To be noticed I edited thirdparty header for fmt library (#373), idk if
it is acceptable
2025-09-18 21:57:38 -07:00
sp2xdev 4e138b244e add dsound.dll to custom DLL detection 2025-09-18 21:49:43 -07:00
sp2xdev 3fcd1bdbc9 fix option strings for force load 2025-09-18 15:59:06 -07:00
sp2xdev f857e63d5d update changelog 2025-09-18 15:27:22 -07:00
GEEKiDoS b462c838f6 Fix broken UI in windowed mode for Polaris Chord (#372) 2025-09-18 09:44:23 -07:00
sp2xdev 4d658bf273 Intel SDE is not an override 2025-09-18 02:35:32 -07:00
sp2xdev 67721c416b update UI strings 2025-09-18 02:33:18 -07:00
bicarus-dev 0b5f7787be launcher: show a giant warning when the user specifies -exec option (#371)
## Link to GitHub Issue, if one exists
#345

## Description of change
If `-exec` is specified, log a giant warning.

Also, fix some compiler warnings in MFG.

## Testing
Confirmed message being logged in log.txt.
2025-09-17 17:03:57 -07:00
sp2xdev a554c8f923 update changelog 2025-09-17 10:40:21 -07:00
bicarus-dev 6592191624 pc: fix certain controller bindings not working (#368)
## Link to GitHub Issue, if one exists
Fixes #364 

## Description of change
The game attempts to hook raw input, but raw input API can only be used
by one window per process. Hook `RegisterRawInputDevices` and prevent
the game from registration for raw input notifications.

## Testing
Tested that HID keyboard works with `Bind` option.
2025-09-17 03:55:02 -07:00
bicarus-dev 164592b16d pc: add language hook (#367)
## Link to GitHub Issue, if one exists
Fixes #362 

## Description of change
Add a hook for `GetLocaleInfoEx` and return `ja-JP` to fix the
"translation not found" issue in XIF.

## Testing
Tested with/without Disable ACP option.
2025-09-17 01:53:50 -07:00
bicarus-dev 81f879e7ff pc: fix fader emulation (#366)
## Link to GitHub Issue, if one exists
Fixes #365 

## Description of change
Always check both analog and digital input when calculating fader
offset.

## Testing
Tested with keyboard and xbox one controller.
2025-09-16 19:36:22 -07:00
sp2xdev 1af8b3de61 update discord rich presence 2025-09-15 21:54:59 -07:00
sp2xdev c33fef606f update change log 2025-09-15 21:39:02 -07:00
oleg238948234 a9fc6ef1b2 Add Polaris Chord Support (#361)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Adds support for Polaris Chord

## Testing
Tested with XIF:J:A:A:2024122300. IO/ICCA works, game successfully
connects to server and is playable.
2025-09-15 21:08:17 -07:00
bicarus-dev d530bfcd65 overlay: tweak imgui colors (#359)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Change the dark brown colors used in widget backgrounds to dark gray.

Decrease the in-game overlay transparency (more opaque): 80% -> 96%.

## Testing
Tested cfg and in-game overlay.
2025-09-14 02:29:14 -07:00
sp2xdev c96f4330d2 Merge branch 'main' of https://github.com/spice2x/spice2x.github.io 2025-08-21 08:29:33 -07:00
sp2xdev 26a2a0f481 update changelog 2025-08-21 08:29:27 -07:00
bicarus-dev 11b9790e6c cfg: rearrange options (#357)
## Link to GitHub Issue, if one exists
n/a

## Description of change
In an attempt to make the Options tab smaller, move out more options to
advanced / development tabs.

## Testing
n/a
2025-08-19 02:41:56 -07:00
bicarus-dev 1dfcf8c087 ddr: check for common DLL errors (#355)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Check if the path to codecs directory contains non-ASCII characters, and
display a giant warning. This is to help with troubleshooting, since
registration of xactengine silently fails (creates a bad registry entry)
if the path contains non-ASCII chars.

Log the size of codec DLLs, and log any failures from `regsvr32`.

Detect the case where user specified `-exec gamemdx.dll` for DDR which
seems to be a common pitfall. This doesn't work because gamemdx.dll does
not have a DLL entry, but for some reason people are really tempted to
do this.

## Testing
Validated the error cases.
2025-08-18 00:00:38 -07:00
bicarus-dev 5c7ad0d51e ea3: supply default pcbid if not present (#356)
## Link to GitHub Issue, if one exists
#345 

## Description of change
If ea3 xml is missing pcbid node, AND there is no pcbid override, supply
a default value. Useful for quickly getting offline clients up &
running.

## Testing
Tested with/without pcbid override, with/without pcbid node in xml.
2025-08-17 23:52:15 -07:00
sp2xdev 587ae86341 update changelog 2025-08-16 19:22:28 -07:00
bicarus-dev 926f2ef2a0 ddr: enclose file path in quotes & use wchar when registering codecs (#354)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Account for the path to codec DLL having spaces in it and use wide
chars.

## Testing
Testing 32 and 64 bits.
2025-08-16 19:18:15 -07:00
sp2xdev d262e02f8c update changelog 2025-08-11 00:20:04 -07:00
bicarus-dev f6b63473a0 ddr: automatically register codecs on launch (#351)
## Link to GitHub Issue, if one exists
#345 

## Description of change
On launch of spice.exe / spice64.exe, call `regsvr32` on known codecs in
the `com` directory.

Add an option to skip this (`-ddrnocodec`) as a chicken bit.

## Testing
Tested 32 and 64 bit DDR.
2025-08-09 20:53:09 -07:00
bicarus-dev 1ad45edd6e sdvx: remove -sdvxdisablecams, simplify camera hook (#350)
## Link to GitHub Issue, if one exists
#345

## Description of change
Simplify camera hook in SDVX5/6. Camera hook will always be installed
and return 0 cameras.

Deprecate `-sdvxdisablecams` option as it is no longer needed as a
result of this change.

## Testing
Tested SDVX5 final, SDVX6 year 1, and a recent SDVX6 build.
2025-08-09 03:01:10 -07:00
bicarus-dev e79de3b117 Dump button and analog bindings to log on launch (#349)
## Link to GitHub Issue, if one exists
#345 

## Description of change
On launch of spice/spice64, dump bindings in Buttons and Analog tab

## Testing
Tested on 32 bit and 64 bit
2025-08-04 21:40:09 -07:00
sp2xdev f783a5a1cd clarify wording for -loglevel 2025-08-02 14:19:56 -07:00
sp2xdev 7848d5c237 detect custom msvcr100.dll 2025-07-25 18:36:55 -07:00
Emma cec735a81b Check for IFS LayeredFS after loading hooks (#342)
## Link to GitHub Issue, if one exists
N/A

## Description of change
Minor fix for a warning in the log. Moves the check for IFS LayeredFS
_after_ loading hooks. Also checks for the module with `GetModuleHandle`
instead, which should cover all paths as long as `ifs_hook.dll` hasn't
been renamed. 😅

## Testing

Before:
```
[2025/07/19 15:59:56] W:launcher: data_mods directory was found, but ifs_hook.dll is not present; your mods will not load
[2025/07/19 15:59:56] W:launcher: to fix this, download ifs_layeredfs and add it as a DLL hook (-k)
[2025/07/19 15:59:56] W:launcher: https://github.com/mon/ifs_layeredfs
[2025/07/19 15:59:56] I:launcher: msvcrt!__argc value before: 0
[2025/07/19 15:59:56] I:launcher: msvcrt!__argc value after: 57
[2025/07/19 15:59:56] I:launcher: loading hook DLL C:\Users\Emma\AppData\Local\Programs\SpiceTools\hooks\64\ifs_hook.dll
[2025/07/19 15:59:56] I:libraryhook: LibraryHook Attach
```

After changes - no warning observed:
```
[2025/07/19 15:59:08] I:launcher: msvcrt!__argc value before: 0
[2025/07/19 15:59:08] I:launcher: msvcrt!__argc value after: 57
[2025/07/19 15:59:08] I:launcher: loading hook DLL C:\Users\Emma\AppData\Local\Programs\SpiceTools\hooks\64\ifs_hook.dll
[2025/07/19 15:59:08] I:libraryhook: LibraryHook Attach
```
2025-07-19 16:17:07 -07:00
sp2xdev 753702e32e Fix build break due to upstream header file changes 2025-07-19 15:36:21 -07:00
sp2xdev 7bd7503951 Update README.md 2025-06-25 01:03:39 -07:00
sp2xdev d0c38956bf update changelog 2025-06-02 17:51:04 -07:00
bicarus-dev 77bca2baee patcher: add gitadora libshare-pj.dll to extra DLL list (#335)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Allow importing patches for `libshare-pj.dll`

## Testing
Observed patch for the new DLL being attempted to be imported in the
logs.
2025-06-02 02:56:04 -07:00
bicarus-dev 46e8c8a5a5 acio: detect crash during acio init due to RTSS/Afterburner and complain (#334)
## Link to GitHub Issue, if one exists
n/a

## Description of change
When an exception is caught during ACIO hook init, print a special
message to suggest the user to disable RTSS and MSI Afterburner, since
they are known to cause issues.

## Testing
Tried with RTSS enabled with High detection and with it disabled. Also
tried to crash the game a different way and check that the message is
not printed and the callstack is shown propeerly.
2025-06-01 14:52:13 -07:00
sp2xdev 4d58e5d080 Merge branch 'main' of https://github.com/spice2x/spice2x.github.io 2025-05-30 22:21:11 -07:00
sp2xdev 168084d672 update changelog 2025-05-30 22:21:03 -07:00
bicarus-dev 740ed90d95 iidx: fix windowed subscreen mouse clicks being ignored when overlay is active (#333)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Fix an issue when clicking on the TDJ windowed subscreen with overlay
active will occasionally drop all future mouse clicks, until the overlay
is completely hidden.

## Testing
Tested TDJ windowed subscreen, with poke, with auto-pin, and with
spicecompanion.

SDVX: this was never an issue for UFC mode, but this change doesn't make
any functional difference either.
2025-05-30 22:20:03 -07:00
sp2xdev 61c17f15b4 update changelog 2025-05-30 20:19:03 -07:00
bicarus-dev 99035f312f cfg: reorganize menu bar (#330)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add `Help` menu. 

Move Licenses and About dialog under Help.

Add links to the wiki.

Make game selection drop-down slightly wider to make better use of space
that has been freed up.

## Testing
tested in cfg (with/without game), and in-game overlay
2025-05-25 01:32:59 -07:00
bicarus-dev aafc916bb6 launcher: detect custom nvidia DLLs (#329)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Complain if nvcuda.dll, nvcuvid.dll, or nvEncodeAPI64.dll are found in
modules or in root directory. This would complain for spice-provided
stubs as well.

## Testing
2025-05-24 12:49:58 -07:00
bicarus-dev c5eb1e999b launcher: option to make command line args take precedence (#327)
## Link to GitHub Issue, if one exists
Fixes #190 

## Description of change
When `-cmdoverride` is specified, any command line arguments are taken
first before merging with what is in the config file.

## Testing
Still testing...
2025-05-15 23:26:38 -07:00
ASleepyCat 7c15452c1e Add support for outputting IIDX tape LED data via spiceapi (#315)
## Link to GitHub Issue, if one exists
#41 (partially)

## Description of change
This PR adds support for outputting IIDX tape LED data via spiceapi
(`iidx_tapeled_get()`). It returns the data as a `dict`, with the key
being the LED name and the value being the list of RGB values. You can
optionally pass in a variadic list of names to filter the returned data.

The new API function is only implemented in Python since I'm not able to
test the wrappers in other languages.

An example script using the new API is available in this Beef Board PR:
https://github.com/HWXLR8/beef-board/pull/133.

Demo use case: https://streamable.com/pq5e1e

## Testing
Tested in IIDX 32 with custom Beef Board firmware along with a Python
script to read and send the tape LED data over USB.
2025-05-12 21:27:20 -07:00
sp2xdev 4bc849ccdb update changelog 2025-05-09 14:26:51 -07:00
bicarus-dev 58481a035b printer: fix orientation of resulting image (#324)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Rotate the printer output so that it's the correct orientation
(portrait, not landscape).

## Testing
Tested via test image, and also in-game, for LP and SDVX.
2025-05-09 09:48:59 -07:00
bicarus-dev 66b3de8e5c loveplus: add printer options (#325)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add the same options as SDVX printer.

## Testing
Tested all options with LP.
2025-05-09 09:48:00 -07:00
bicarus-dev 257afe0cd3 loveplus: remove log spam (#326)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Remove spammy logging in LP

## Testing
It works 👍
2025-05-09 09:47:38 -07:00
bicarus-dev aafea3b1c1 loveplus: stub impl of cpusbxpkm.dll (printer) (#319)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add a stub for `cpusbxpkm.dll`. The stub doesn't work, it's only enough
to allow the game to load; but that is irrelevant since spice hooks the
printer at runtime.

## Testing
Observed the game boot, and was able to print out the test image from
test menu. Will do more testing on a real server.
2025-05-08 23:03:26 -07:00
bicarus-dev 346a159157 loveplus: full screen mode (#323)
## Link to GitHub Issue, if one exists
Fixes #322 

## Description of change
Ensure game launches in full screen when windowed mode setting is off.
Before this PR, the game always launched in windowed mode.

## Testing
Seems to run fine in fullscreen with the monitor in portrait mode.
It does seem to run at even lower framerate (around 24fps, vs. 30fps in
windowed) but it might be expected.
Some weirdness with the mouse cursor when the overlay is active (two
cursors).
2025-05-08 17:25:15 -07:00
bicarus-dev 54028d8cbb loveplus: improve window searching for touch hook (#321)
## Link to GitHub Issue, if one exists
Fixes #320 

## Description of change
Replacing the existing logic that searches for game window to hook touch
on.

Eventually, we should fix all the other games that hook touch by
matching on window title with a call to `FindProcessWindowBeginsWith`.

## Testing
Tested with notepad open that was also titled `LovePlusAC`. Game hooked
correctly when the game was in focus during boot, and also when notepad
was in focus.
2025-05-07 21:41:48 -07:00
bicarus-dev 621b702ed4 loveplus: enable cam access (#318)
## Link to GitHub Issue, if one exists
#316 
(this does not fix the issue!)

## Description of change
Add an option to omit `-noCamera` option. Doing so allows the game to
boot and search for a compatible camera using KS API, but will likely
fail since we don't know what cameras are compatible. You end up with a
camera error during boot, which can be ignored by flipping a switch in
the test menu to disable the camera.

No hooks are currently done to make the camera work!

## Testing
Tested to see that turning this on triggers the camera error. No known
camera at the moment.
2025-05-07 20:43:16 -07:00
bicarus-dev 26ea8a1b92 api: check for window focus when accepting input (#317)
## Link to GitHub Issue, if one exists
Fixes #139 

## Description of change
Add an option for detecting window focus when accepting input.

The new default will be to have the following:
 * naive bindings will check for window focus
* rawinput binds are not affected, will continue to be accepted
regardless of focus

Other options (always / never choices) can be picked by the user to
tweak the behavior if needed.

Clean up usage of `0xff` constant to represent invalid naive vkey.

## Testing
Still in progress, especially in regards to performance.

On a i7-9700 system, 10000 calls to `superexit::has_focus()` takes
250-350 microseconds, so this should not be a big deal at all.
2025-05-07 13:22:29 -07:00
bicarus-dev 6e7bf99af8 util: print dimensions and refresh rate of monitors (#314)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Dump monitor resolution and refresh rate to the log on launch.

## Testing
Tested with two monitors.
2025-05-05 16:48:55 -07:00
bicarus-dev df9d9d8673 api: quality options for screen mirroring (#313)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Allow the server to override client's quality options for screen
mirroring.

On Windows / Android versions (flutter-based) clients, this is already
in the options of those apps. However, the iOS version lacks the quality
options.

## Testing
Tested with iOS and Windows clients.
2025-05-04 20:10:56 -07:00
sp2xdev b3cee1dfcc fix build 2025-05-04 15:53:46 -07:00
bicarus-dev f40c382903 net: mark netadapter IP as sensitive option (#312)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Mark the option as "sensitive" which censors the option value (adapter's
IP address)
Remove the log entry from spice

This isn't perfect because most of the games like to log the IP address
and subnet anyway, but at least spice itself will stop doing it.

## Testing
Tested 64 bit LDJ with parameter set.
2025-05-04 13:20:37 -07:00
sp2xdev e4cfb808bf Update CONTRIBUTING.md 2025-05-04 00:38:10 -07:00
sp2xdev 61d14a16e8 Update CONTRIBUTING.md 2025-05-04 00:33:43 -07:00
bicarus-dev 521e87ea41 patchmanager: always log patch identifier of main DLL (#311)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Always log the DLL identifier string in patch manager, even if there are
no patches.

## Testing
Tested 32 / 64 bits.
2025-05-04 00:23:51 -07:00
bicarus-dev 2b735ffd55 launcher: check for custom d3d9.dll in user's game directory (#309)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Log a message if d3d9.dll (and friends) are found in the game root or
the modules directory.

This is to help with troubleshooting in cases where the user is unaware
that d3d9 is being used to inject other DLLs (e.g., ifs_layeredfs), or
when DX9 reimplementation is causing issues / crashes (e.g., dxvk).

## Testing
Observed the new messages being logged in both modules / root directory.
2025-05-02 01:51:23 -07:00
bicarus-dev 4f72b0a98b graphics: more window options for windowed subscreen (#308)
## Link to GitHub Issue, if one exists
Fixes #307 

## Description of change
Add an option for making TDJ/UFC subscreen window borderless in windowed
mode

Add another option for keeping the subscreen "always on top".

Add size / position arguments to SDVX windowed subscreen, exactly the
same as what is allowed for IIDX.

## Testing
Seems to work....
2025-04-27 21:20:55 -07:00
sp2xdev 474f5f5203 fatal error for sdvxlandscape 2025-04-25 15:53:58 -07:00
sp2xdev 205cf29aff release build script and update changelog 2025-04-24 17:21:34 -07:00
bicarus-dev bf9074a76e launcher: complain when duplicate DLLs are detected (#306)
## Link to GitHub Issue, if one exists
#304 

## Description of change
Complain loudly about DLL conflicts. Show warnings for each DLL that
exists in both modules directory and the root.

This is not based on LoadLibrary hooks, just enumerating DLLs via
filesystem access.

Deliberately not using `log_fatal` since these are not necessarily going
to crash the game. Some people just love to have messy installs and if
the game booted fine before, it should continue to boot.

## Testing
Tested with/without `-modules`, with/without duplicate DLLs.
2025-04-24 10:35:34 -07:00
bicarus-dev 44e1edb50c avs: make max path check stricter (#305)
## Link to GitHub Issue, if one exists
n/a

## Description of change
The check to warn the user about potential MAX_PATH violation should be
stricter. Saw a user with 150 characters run into a crash during boot
for SDVX3.

## Testing
Tested paths under 130 / over 130, in both 32/64 bits.
2025-04-24 10:18:09 -07:00
bicarus-dev 41d0dce6e9 cfg: use %appdata%\spice2x for new config files (#300)
## Link to GitHub Issue, if one exists
n/a

## Description of change

For JSON config files of the following features:

* patch manager
* screen resize
* IIDX camera hook
* card manager

when saving a new file, store them in %appdata%\spice2x instead of
%appdata%.

On load:
1. If the JSON file exists in %appdata%\spice2x, use that (new path)
1. If the JSON file exists in %appdata%, continue to use that (legacy
path)

It's common for people to have mixed versions of spicetools/spice2x so
we'll continue to read from the %appdata% root if the files are there,
but with a preference for the new path. We will not forcibly move files.

spicetools.xml will continue to live in the %appdata% root. Moving this
will confuse a lot of people, so I'm avoiding this.

Also, this fixes `-patchcfgpath` and `-resizecfgpath` to create
directories as needed (previously the parent directory must have existed
first)

## Testing
Tested -

* existing config files are continued to be read from %appdata%
* new files get created in %appdata%\spice2x\... (both in spicecfg and
in overlay)
* can provide custom path for `-patchcfgpath` `-resizecfgpath` and
observe directories + file created in custom path, try absolute or local
relative paths
2025-04-22 00:35:10 -07:00
bicarus-dev 598422b701 build: large address aware variant of 32-bit spice.exe (#298)
## Link to GitHub Issue, if one exists
Fixes #280

## Description of change
Introduce a variant of 32bit spice.exe that is large address aware.

## Compiling
👍 

## Testing
Minimal testing has been done to see if the LAA variant can boot
existing 32 bit games.
2025-04-22 00:21:45 -07:00
bicarus-dev b58a1ccfcc sdvx: disable sdvx landscape option (#303)
## Link to GitHub Issue, if one exists
Fixes #277 

## Description of change
To avoid confusion, temporarily remove `-sdvxlandscape` because of the
issue described in the bug above.

`forceresswap` is still kept, so there is still a way to do exactly what
`-sdvxlandscape` used to do, if someone really wants it.

## Testing
Checked that `-sdvxlandscape` is ignored, and that `-sdvxlandscape`
continues to be honored.
2025-04-19 20:44:24 -07:00
ASleepyCat 60efd643d1 Add ccache support for faster incremental builds (#301)
## Description of change
Adds `ccache` support for local compiling and Github Actions
environments. This significantly speeds up incremental builds, but cold
starts will take a little longer to compile.

The build script now has an `-i` flag to ignore the build cache if
needed. You can also manually delete the `.ccache` folder to force a
fresh build too.

For the GA workflow, it uses the
[`ccache-action`](https://github.com/hendrikmuhs/ccache-action) action
to manage and store the cached build objects. This cache should be
accessible to all branches.

I also removed the `--no-cache` flag when building the
`spicetools/spice` image since Docker will automatically invalidate
`COPY` layers if it detects the copied files have changed.

### Compile times
Compiled with a 7800X3D, 32GB 6000MT/s RAM.
| `main` | `ccache` (fresh) | `ccache` (cached) | `ccache` (ignore
cache) |
|--------|--------|--------|--------|
| 2m46.047s | 3m8.886s | 0m32.638s | 2m45.148s |

## Testing
Tested incremental builds by using another branch that's rebased on top
of this.
2025-04-19 20:43:10 -07:00
sp2xdev 0596eeb6fe Update pull_request_template.md 2025-04-19 14:24:21 -07:00
ASleepyCat 3d5c64feb7 Fix button HID string ranges not being retrieved correctly (#299)
## Description of change
Currently, retrieving HID strings using string index minimum/maximum
ranges for buttons is broken as it uses the same index for each usage
cap.

Now it properly ranges over the minimum/maximum ranges.

Before:

![image](https://github.com/user-attachments/assets/da8d0162-07d9-4027-9933-2348fbf43a27)

After:

![image](https://github.com/user-attachments/assets/c00f81fe-d9d4-43f8-9bdd-d570ef298479)


## Compiling
Check CI.

## Testing
Tested with Beef Board firmware using string index ranges in the USB HID
descriptor.
2025-04-18 01:23:00 -07:00
sp2xdev 610984f40e update changelog 2025-04-07 17:04:36 -07:00
sp2xdev 1e271b7352 Merge branch 'main' of https://github.com/spice2x/spice2x.github.io 2025-04-07 17:04:14 -07:00
bicarus-dev 07c78caf01 build: add git commit hash to produced artifact (#297)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Previously, the artifact produced by the CI action was `spice2x.zip`.

With this change, it will be `spice2x-ci-1234567.zip` where 1234567 is
the first 7 chars of the commit hash.

Note that for PRs, github produces a merge commit before queueing the
action, so the commit hash won't match up with the HEAD of a fork. The
goal is to just generate unique-ish filenames so this is not a huge
deal.

## Compiling
## Testing

Action has been run in the fork.
2025-04-07 15:56:46 -07:00
sp2xdev d153209d8a update changelog 2025-04-07 02:14:51 -07:00
bicarus-dev 48186245fe overlay: update UI for card manager (#294)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Change the usage pattern for Card Manager UI.

### Previous behavior

Previously, it was a list of cards that you select, and click a button
to insert as P1 or P2.

This is fine, but users got confused when they pressed `Insert Card`
overlay key and got a different card inserted, or when auto-insert
didn't work as expected.

Additionally, if you play a game without continue (IIDX/DDR for
example), then it's cumbersome to bring up the overlay and click on
insert card every time.

### New behavior

Insert Card overlay now lets you pick a card and *slot* it into P1 or P2
side.

From that point on, the slotted card number is used as the override,
replacing what was previously passed to `-card0` or `-card1`. This means
that `Insert Card` button and auto-insert will pick up the new card and
use that instead.

## Compiling
👍 

## Testing
Tested 1p-only game (popn) and 2p games (DDR, IIDX).
2025-04-07 01:49:07 -07:00
bicarus-dev f7c274591a museca: add Start and 10KEY lights (#296)
## Link to GitHub Issue, if one exists
Fixes #295 

## Description of change
Add missing `Start` and `10KEY` lights output, as see in the test menu.

## Compiling
👍 

## Testing
Seems fine in museca test mode.
2025-04-06 19:25:39 -07:00
ASleepyCat d1a20e58b6 Add CI and improve build system (#293)
## Link to GitHub Issue, if one exists
Fixes #292

## Description of change
This PR adds CI with Github Actions. Runs on every push and pull
request. If a new commit is pushed, any previous runs are cancelled for
that branch.

In total, these optimisations (on a 7800X3D with 32GB 6000MT/s RAM and
an NVMe Gen4 SSD):
* Reduces `spicetools/deps` image size by around 200 MB, and build time
by almost 50%
* Reduces compilation time by around 9%

Changes:
* Pull archlinux image from GHCR so that we don't run into pull limits
from docker.io
* Use `base-devel` archlinux image
* Reduces image build time from 141 seconds to 109 seconds on my machine
  * Clean up unneeded packages
* Small reduction in `spicetools/deps` image size (4.35 GB vs 4.14 GB)
* Use precompiled `yay` binary
* Reduces image build time from 163 seconds to 141 seconds on my machine
* Use `nproc` to get the number of cores instead of parsing
`/proc/cpuinfo` with `awk`
* Use `ninja` to compile
  * Speeds up compile times a little, around 13 seconds on my machine
* Don't run `docker run` in interactive TTY in `build_docker.sh`
  * Github Actions disallows interactive TTYs
  * Still allowed for the batch script
* `time` the compilation process
* Enable BuildKit in the bash script
  * Required for build contexts
* Don't use `USER` instruction
  * Breaks Github Actions, doesn't seem to affect build?
* More info:
https://docs.github.com/en/actions/sharing-automations/creating-actions/dockerfile-support-for-github-actions#user
  * `USER` also seems to cause issues on a mounted ReFS image
* Add executable bits to bash scripts

Future ideas:
* Combine `external/Dockerfile` and `./Dockerfile` into one to
deduplicate images and save disk space
* Fix the weird `/src/src` directory structure in the Dockerfile and
build scripts
* Investigate CMake and ninja optimisations?

## Compiling
Check CI status on my fork.

## Testing
Tested with docker on Windows. Also play tested a bit to make sure the
Github Actions artifact works.
2025-04-06 00:29:02 -07:00
sp2xdev 1e02c31524 update changelog 2025-04-04 22:13:01 -07:00
bicarus-dev 66fab48a5b options: update monitor option text (#291)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Update tooltip for `-monitor` option. Move to `Graphics (Full Screen)`
to reduce clutter.

## Compilin
👍 

## Testing
n/a
2025-04-04 22:09:21 -07:00
bicarus-dev ae06652148 touch: avoid calling is_touch_available too early (#289)
## Link to GitHub Issue, if one exists
Fixes #120 

## Description of change
Don't call `is_touch_available` in launcher before RawInput stack is
initialized. This causes us to incorrectly fall back to the Win7/Win8
touch handler.

The desired behavior is to use rawinput touch by default for all games,
unless overridden by the user via `-wintouch`.

Add a bunch of logging so we can spot who's calling
`is_touch_available`.

As a side effect, a handful of games that previously used Win7/8 touch
will now use RawInput touch instead. If this causes any issues, they can
turn `-wintouch` on to force Win7/8 touch logic again.

## Compiling
👍 

## Testing
Test:

* RB
* -wintouch
* TDJ, UFC subscreen and native touch
* overlay, especially in multi-mon
2025-04-04 21:31:18 -07:00
sp2xdev bd6c8d3f3c add gitignore to root 2025-04-04 17:51:40 -07:00
bicarus-dev e88abec2a6 overlay: remove navigator input (#288)
## Link to GitHub Issue, if one exists
n/a

## Description of change
No one uses this, remove them to reduce clutter. This is in preparation
to migrate from deprecated ImGui IO model to new one, and this is part
of that.

## Compiling
👍 

## Testing
2025-04-03 21:16:48 -07:00
bicarus-dev 6de8b529d2 imgui: update to v1.91.4, make various fixes to overlay (#286)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Update ImGui to `v1.91.4`. We can't update to `v1.91.5` or newer just
yet because impl_spice depends on the old IO model of ImGui which got
removed in `v1.91.5`.

We also can't update the DX9 renderer right now, because at some point
overlay breaks in TDJ (and only in TDJ...)

Enable the new debug logging functionality in ImGui. Add a button in
overlay to toggle debug log window.

Fix some minor bugs spotted by the new ImGui debug log: mismatched API
calls, font loading issues.

## Compiling
👍 

## Testing
In progress.
2025-04-03 19:14:48 -07:00
bicarus-dev 7301cdaa6c imgui: reorganize imgui library files to mirror what's in the imgui releases (#285)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Functionally, no change.

Move ImGui library files around so that they mirror how they are
structured in imgui releases. This will make it easier to migrate to a
future version of ImGui.

Add a handful of static asserts to check that we have the necessary
features enabled / disabled in imgui header.

## Compiling
👍 

## Testing
Tested 32-bit overlay, 64-bit overlay, TDJ, and UFC overlays, and
spicecfg.

Interesting finds during ImGui testing (not part of this PR, but for
future reference when upgrading imgui version):
* Updating dx9 backend to the newest causes TDJ to stop rendering the
overlay completely (fine in UFC or any other game)
* Omitting `IMGUI_USE_BGRA_PACKED_COLOR` causes spicecfg software
renderer to use the wrong color (RGB format parsing error probably)
* At some point - v1.90? the `About` and `Licenses` buttons in spicecfg
stop responding after an upgrade, maybe our improper usage of MenuItem?
* Using the docking branch of ImGui is not required but we should just
stick to it for multi-viewport functionality which we may want to use
later.
2025-04-02 22:56:52 -07:00
bicarus-dev 6969dfcdd1 overlay: migrate configurator UI from ImGui Columns to Tables (#282)
## Link to GitHub Issue, if one exists
n/a

## Description of change
`ImGui::Columns` is a deprecated component with many issues. Migrate
Buttons/Analog/Overlay/Lights/Options... tabs to the new table API.
Clean up some UI elements around it to reduce clutter.

Note: there are still other usages of Columns API (MIDI debug output,
ACIO viewer, window manager) but no one hardly ever sees those.
Eventually they will need to be migrated if ImGui ever drops support for
them.

## Compiling
👍 

## Testing
Testing all games in spicecfg and overlay.
2025-04-02 18:33:39 -07:00
bicarus-dev edaf5a386f overlay: fix mouse cursor getting stuck in wrong shape when leaving and coming back to window (#283)
## Link to GitHub Issue, if one exists
Fixes #27 

## Description of change
In standalone configurator mode, when a mouse cursor moves outside the
window and comes back, it may get stuck in the wrong cursor type (e.g.,
get stuck in horizontal resize mode). This is a side effect of software
rendering we do that doesn't deal nicely with mouse events (i.e., not at
all).

## Compiling
Should be fine.

## Testing
Testing spicecfg, spice -cfg, spice64 -cfg, and in-game overlay.
2025-04-02 18:32:30 -07:00
bicarus-dev 302f08dd57 patcher: show errors on invalid json response from remote (#281)
## Link to GitHub Issue, if one exists
#0

## Description of change
When patcher URL returns an invalid response (as in, cannot be parsed as
JSON), show an error message to user.

Add a "copy text" button to copy the error message.

Fix WarnTooltip so that can handle the case where description is an
empty string for a patch - previously this was just showing as multiple
lines of empty space and then the warning.

## Compiling
It's fine except Docker build currently fails due to cmake version
issue.

## Testing
Tested valid / invalid datecodes and checked against responses.
2025-04-02 18:32:15 -07:00
sp2xdev 68f5a614ef update discord-rpc cmake version 2025-04-01 02:35:41 -07:00
sp2xdev 5db43031f2 fix option descriptions 2025-03-29 01:35:02 -07:00
sp2xdev 873ade757a show patch description in patch warn marker 2025-03-29 00:10:42 -07:00
sp2xdev 3c8dc9de32 overlay: change active option color to green 2025-03-28 23:49:12 -07:00
sp2xdev 4694b59cec update changelog 2025-03-28 20:36:52 -07:00
bicarus-dev 4f1ada7a2f overlay: help tooltip updates (#279)
## Link to GitHub Issue, if one exists
n/a

## Description of change

In Options (and in Advanced/API/Search tabs), remove the help marker
`(?)` and instead add a tooltip to the option name label and the control
widgets like the checkbox, text input, and combo. This is to help with
discoverability as many users failed to discover the `(?)` widget.

Similarly, update the Patches tab to remove `(?)` and add tooltips to
widgets. Patches with warnings `(!)` will continue to show it to
distinguish it from other patches.

Update styling for the tooltip (dark red background -> dark gray) so
that it stands out from other widgets.

Remove the purple option text used for game-specific options.

## Compiling
👍 

## Testing
Tested in spicecfg, and in-game overlay.
2025-03-28 20:34:58 -07:00
bicarus-dev 104a9cbffd graphics: rewrite image scaler (#278)
## Link to GitHub Issue, if one exists
Fixes #276 

## Description of change

Note: this change is not compatible with previous screen resize config;
i.e., if you had the zoom setting dialed into a certain view, after this
change it won't be the same area, so you'll have to set up a scene
again. I think this is a worthwhile cost since the old logic is just
broken in weird ways, and preserving old settings is really tedious math
(for a feature that not many people use). Besides, this change will only
use the new Scenes values from the JSON.

Rewrite the DX9 image scaler logic.

* Previously, the rendering surface was fixed at `4096*4096 px`. Now,
this is relative to the backbuffer dimensions, which depends on the game
& respects user provided `-forceres`.
* Remove "center" option, make it the default.

Above two changes fix the aspect ratio issue (scaling in portrait games
not respecting screen ratio) and things breaking at higher res (1080p
when zoomed out far enough, or when forced to run at 4k resolution for
example)

* Use `scenes` leaf of screen resize JSON config for Scene 1 as well,
completely removing ourselves from being compatible with older spice2x
versions for these values.
* Add additional bounds check before calling `StretchRect` to avoid
users getting stuck with a bad layout, which can kill performance.

## Compiling
🦾 

## Testing
Tested 32 bit (popn / ddr), 64 bit (ldj), portrait and landscape modes
(kfc)
2025-03-28 17:23:01 -07:00
bicarus-dev 45a52cff90 sdvx: landscape mode (#277)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add `SDVX Landscape Mode` option, allowing you to play SDVX in landscape
monitor orientation, with black borders on left and right - similar to
how EAC does it. This is meant to be an alternative to what people do
today, which is launching in windowed mode.

By default, with just `-sdvxlandscape` enabled, EG will launch at
1920x1080 (transposed from the original 1080x1920 resolution).

It is possible to combine with `-forceres` to launch at higher
resolutions. At some point though, when rendering at large resolutions
past 1440p, at some point scaling may break due to #276.

Disabled on 32-bit SDVX since dx9 `CreateDeviceEx` call is required;
`CreateDevice` won't work. For the same reason this isn't being made a
generic option for museca/rb/etc. In theory you can supply
`-sdvxlandscape` to games like IIDX and play landscape games in portrait
mode, although again, scaling my break due to #276.

Also, add new options (bottomleft/right) for subscreen overlay position
so that they don't overlap with the main screen in landscape mode.

## Compiling
🙂

## Testing
Tested a couple versions of VW and EG.
2025-03-26 20:21:05 -07:00
141 changed files with 19861 additions and 8630 deletions
+32
View File
@@ -0,0 +1,32 @@
on: [push, pull_request]
name: Continuous Integration
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
fw-ci:
name: Build
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./src/spice2x
steps:
- uses: actions/checkout@v4
- name: Set ccache environment variables
run: |
echo "CCACHE_DIR=${{ github.workspace }}/src/spice2x/.ccache" >> $GITHUB_ENV
- name: Install ccache
uses: hendrikmuhs/ccache-action@v1.2
- name: Calculate commit SHA
id: vars
run: |
calculatedSha=$(git rev-parse --short ${{ github.sha }})
echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV
- name: Compile
run: ./build_docker.sh
- uses: actions/upload-artifact@v4
with:
name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }}
path: src/spice2x/bin
if-no-files-found: error
+13
View File
@@ -0,0 +1,13 @@
.vscode/*
# JetBrains IDEs
.idea
.idea_modules
*.iws
# CMake
cmake-build*
# user config
build_all.local.sh
build_docker.local.sh
+8 -4
View File
@@ -30,12 +30,11 @@ Do not change the names of options, buttons binds, analogs, etc - since they are
* All currently supported games / versions must continue to work.
* Backwards compatibility must be preserved, unless there is a really good reason to break it. This includes (but not limited to): global/local config files, command line parameters, game patches, device interop (e.g., card readers), and SpiceAPI / Companion interop.
* Reasonable level of compatibility with the last release of original spicetools is expected. The stated goal of spice2x is to be a drop-in replacement for spicetools.
* Simply put, if someone has an existing install of spicetools/spice2x, copying over new version of spice2x should not result in different behavior or loss of functionality.
* Simply put, if someone has an existing install of spicetools/spice2x, copying over new version of spice2x should not result in vastly different behavior or major loss of functionality.
* Make sure you compile with the included Docker script and ensure you do not introduce **any** new compiler warnings or build breaks. The Docker script is the standard build environment, your custom Linux build environment or MSVC can be used during development, but you must validate the final build using Docker.
* Do not submit snippets of code as a "patch". Exceptions can be made for trivial changes (correct a typo, fix a single line of code...), but otherwise, a successfully compiled & fully tested patch file is required when submitting for review.
* Do not make code changes in unrelated areas; i.e., do not run code linters and auto-formatters for parts of the code that you didn't modify.
* Try to submit smaller chunks of code, instead one gigantic patch. For example, don't submit a patch for "Improve feature XYZ"; instead, submit "Change how A works to prepare for feature XYZ" "Refactor B for feature XYZ" "Add feature B to enable feature XYZ".
* Write to the log for anything useful - it helps immensely with debugging post-mortem. At the same time though, avoid spamming the log for something trivial.
* Write to the log for anything useful - it helps immensely with troubleshooting and debugging. At the same time though, avoid spamming the log for something trivial.
### UI text
spice2x has a global audience; majority of the user base do not speak English as their first language.
@@ -44,7 +43,7 @@ Use simple English, avoid colloquialism, and use concise language, even if it's
### Using OS APIs
Avoid making permanent changes to user's OS configuration. For example, spice should not make a call to set power profile to Maximum Performance, or switch default audio device. Making the reverse call to restore settings on game shutdown is **not** good enough; there is no guarantee that spice will gracefully shutdown, since games (or spice) can crash. This is to avoid inadvertently putting user's PC into a bad state, which can be seen as malware-like behavior.
Avoid making permanent changes to user's OS configuration. For example, spice must not make a call to set power profile to Maximum Performance, or switch default audio device. Making the reverse call to restore settings on game shutdown is **not** good enough; there is no guarantee that spice will gracefully shutdown, since games (or spice) can crash. This is to avoid inadvertently putting user's PC into a bad state, which can be seen as malware-like behavior.
Watch out for legacy OS compatibility. Currently, the minimum support floor is Windows 7. If you use any Windows API, make sure it's supported in Windows 7. If you need to use API that is not present in Windows 7, you must not directly link against it, otherwise spice will fail to launch on older OSes. Take a look at Windows 8 touch code (win8.cpp) for examples on how to discover OS APIs via pointers.
@@ -87,4 +86,9 @@ if (conditional)
}
```
* Please give [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) a read. A few things to point out in spice codebase:
* We don't use GSL.
* Don't throw exceptions.
* Stick to smart pointers. For interfacing with C Win32 API and raw buffers, use `unique_plain_ptr`.
* Writing in C is also completely acceptable.
* Other than that, there are no strict rules for code formatting, but please attempt to emulate the style around the code you are modifying.
+1
View File
@@ -36,6 +36,7 @@ please do not ask for these, as it will never happen here.
Rules for filing a new issue or adding comments to existing issues in the tracker:
* Low effort submissions will be simply deleted, and repeated attempts will get you banned.
* Check the [known issues](https://github.com/spice2x/spice2x.github.io/wiki/Known-issues) page first before reporting a new issue. If you have some new information or workarounds for a known issue, you can file a documentation bug as well.
* Use the search function and see if there is an existing issue.
* This is not the place to obtain a guide or receive basic troubleshooting.
+1 -3
View File
@@ -3,6 +3,7 @@
> * Please do note that this is a GPL v3.0 open source project.
> * Please read the [CONTRIBUTING](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md) guide.
> * Maintainers reserve the right to reject or modify your submission without reason.
> * No new compiler warnings must be introduced. Check the CI build results.
>
> Feel free to remove this section after you have read it.
@@ -12,8 +13,5 @@
## Description of change
*what does this code do?*
## Compiling
*you must be able to compile with the included Docker script with zero compiler warnings*
## Testing
*how was the code tested?*
+1
View File
@@ -2,3 +2,4 @@ bin/**
dist/**
docker/**
cmake-build*
.ccache/**
+8 -1
View File
@@ -16,4 +16,11 @@ build_docker.local.sh
bin/*
dist/*
external/cv2pdb/*
external/cv2pdb/*
.ccache/*
# Visual Studio
.vs
out
CMakeSettings.json
+56 -12
View File
@@ -80,6 +80,25 @@ if(MSVC)
# enable COMDAT folding for even smaller release builds
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/OPT:ICF>")
# always use UTF-8 (fix 4819)
add_compile_options("/utf-8")
# spectre mitigation warning
add_compile_options("/wd5045")
# implicit type convert warnings
add_compile_options("/wd4244")
add_compile_options("/wd4267")
add_compile_options("/wd4305")
# unreferenced local variable
add_compile_options("/wd4101")
# warning in winbase.h??
add_compile_options("/wd5039")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# disable warnings about using non _s variants like strncpy
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
@@ -124,8 +143,8 @@ else()
# https://github.com/msys2/MINGW-packages/pull/6880
# some games crash if some DLLS load above 4GB VA, so manually set base address to standard 32-bit VA,
# and might as well double make sure ASLR is disabled here
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
# set visibility to hidden
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden")
@@ -160,8 +179,8 @@ else()
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections")
# linker fix to load below 4GB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
# set visibility to hidden
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden")
@@ -184,8 +203,8 @@ else()
set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf")
# linker fix to load below 4GB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
# enable debug symbols on level 3 and keep frame pointers
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer")
@@ -211,14 +230,12 @@ add_compile_definitions(
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACIOEMU_LOG")
# add project directory to include path so we can comfortably import
include_directories(${spicetools_SOURCE_DIR})
include_directories(${spicetools_SOURCE_DIR} ${spicetools_SOURCE_DIR}/external/imgui)
# add external libraries
add_subdirectory(external/fmt EXCLUDE_FROM_ALL)
add_subdirectory(external/discord-rpc EXCLUDE_FROM_ALL)
add_subdirectory(external/hash-library EXCLUDE_FROM_ALL)
add_compile_definitions(IMGUI_DISABLE_DEMO_WINDOWS)
add_compile_definitions(IMGUI_DISABLE_DEBUG_TOOLS)
add_subdirectory(external/imgui EXCLUDE_FROM_ALL)
add_subdirectory(external/minhook EXCLUDE_FROM_ALL)
add_subdirectory(external/cpu_features EXCLUDE_FROM_ALL)
@@ -445,6 +462,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/mfg/mfg.cpp
games/mfg/io.cpp
games/mfg/bi2a_hook.cpp
games/pc/pc.cpp
games/pc/io.cpp
games/pc/bi2x_hook.cpp
# hooks
hooks/audio/audio.cpp
@@ -516,7 +536,6 @@ set(SOURCE_FILES ${SOURCE_FILES}
overlay/overlay.cpp
overlay/window.cpp
overlay/imgui/extensions.cpp
overlay/imgui/impl_dx9.cpp
overlay/imgui/impl_spice.cpp
overlay/imgui/impl_sw.cpp
overlay/windows/acio_status_buffers.cpp
@@ -610,6 +629,22 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
endif()
# spice_laa.exe
###########
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_spice_laa
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "")
set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa")
target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1)
IF(NOT MSVC)
set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware")
endif()
# spice64.exe
#############
@@ -629,7 +664,6 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
# spicecfg.exe
##############
@@ -726,6 +760,16 @@ if(NOT MSVC)
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
# cpusbxpkm.dll (32 bit)
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} stubs/cpusbxpkm.def)
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES PREFIX "")
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
if(NOT MSVC)
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
endif()
# output directories
####################
@@ -735,7 +779,7 @@ set_target_properties(spicetools_cfg
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
# output 32bit
set_target_properties(spicetools_spice spicetools_stubs_kbt spicetools_stubs_kld
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
+4 -5
View File
@@ -1,8 +1,7 @@
FROM spicetools/deps
WORKDIR /src
RUN chown user:user /src
USER user
COPY --chown=user:user --from=gitroot . /src/.git
COPY --chown=user:user . /src/src/spice2x
COPY --from=gitroot . /src/.git
COPY . /src/src/spice2x
WORKDIR /src/src/spice2x
CMD ["./build_all.sh"]
ENTRYPOINT ["./build_all.sh"]
+19
View File
@@ -247,6 +247,25 @@ which also means that your hex edits are applicable directly.
- sets the contents of the 16 segment display and disables writes from game
- ticker_reset()
- re-enables writes from game
- tapeled_get(name: str, ...)
- returns a list containing a dict of the current tape LED states. The dict keys are:
- `Stage Left`
- `Stage Right`
- `Cabinet Left`
- `Cabinet Right`
- `Control Panel Under`
- `Ceiling Left`
- `Title Left`
- `Title Right`
- `Ceiling Right`
- `Touch Panel Left`
- `Touch Panel Right`
- `Side Panel Left Inner`
- `Side Panel Left Outer`
- `Side Panel Left`
- `Side Panel Right Outer`
- `Side Panel Right Inner`
- `Side Panel Right`
#### LCD
- info()
+4
View File
@@ -41,6 +41,7 @@
namespace acio {
HINSTANCE DLL_INSTANCE = nullptr;
std::vector<acio::ACIOModule *> MODULES;
std::atomic<bool> IO_INIT_IN_PROGRESS = false;
}
/*
@@ -58,6 +59,7 @@ static inline acio::HookMode get_hookmode() {
void acio::attach() {
log_info("acio", "SpiceTools ACIO");
IO_INIT_IN_PROGRESS = true;
// load settings and instance
acio::DLL_INSTANCE = LoadLibraryA("libacio.dll");
@@ -111,6 +113,8 @@ void acio::attach() {
for (auto &module : MODULES) {
module->attach();
}
IO_INIT_IN_PROGRESS = false;
}
void acio::attach_icca() {
+2
View File
@@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <vector>
#include <windows.h>
@@ -11,6 +12,7 @@ namespace acio {
// globals
extern HINSTANCE DLL_INSTANCE;
extern std::vector<acio::ACIOModule *> MODULES;
extern std::atomic<bool> IO_INIT_IN_PROGRESS;
void attach();
void attach_icca();
+20
View File
@@ -33,6 +33,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_off() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 0.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 0.f);
}
return 1;
@@ -44,6 +49,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_on() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 1.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 1.f);
}
return 1;
@@ -55,6 +65,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_off() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 0.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 0.f);
}
return 1;
@@ -66,6 +81,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_on() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 1.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 1.f);
}
return 1;
+1 -1
View File
@@ -31,7 +31,7 @@ namespace acio2emu {
}
};
IOBHandle::IOBHandle(std::wstring device) : device_(device) {
IOBHandle::IOBHandle(LPCWSTR device) : device_(device) {
nodes_[0] = std::make_unique<MasterNode>(this);
}
+1 -1
View File
@@ -26,7 +26,7 @@ namespace acio2emu {
void forward_packet_(const Packet &packet);
public:
IOBHandle(std::wstring device);
IOBHandle(LPCWSTR device);
bool register_node(std::unique_ptr<Node> node);
int number_of_nodes() const;
+3 -3
View File
@@ -102,7 +102,7 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
// send version data
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
if (
avs::game::is_model({"LDJ", "TBS", "UJK"}) ||
avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
// SDVX Valkyrie cabinet mode
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
) {
@@ -373,7 +373,7 @@ void ICCADevice::update_card(int unit) {
bool kb_insert_press = false;
// eamio keypress
kb_insert_press |= eamuse_get_keypad_state((size_t) unit) & (1 << EAM_IO_INSERT);
kb_insert_press |= static_cast<bool>(eamuse_get_keypad_state((size_t) unit) & (1 << EAM_IO_INSERT));
// check for card
if (this->cards[unit] == nullptr && (eamuse_card_insert_consume(this->node_count, unit) || kb_insert_press)) {
@@ -521,7 +521,7 @@ void ICCADevice::update_status(int unit) {
buffer[10] = felica ? 0x01 : 0x00;
} else if (
avs::game::is_model({"LDJ", "TBS"}) ||
avs::game::is_model({"LDJ", "TBS", "XIF"}) ||
// SDVX Valkyrie cabinet mode
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))) {
+15 -3
View File
@@ -9,6 +9,9 @@ using namespace rapidjson;
namespace api::modules {
std::optional<uint32_t> CAPTURE_QUALITY;
std::optional<uint32_t> CAPTURE_DIVIDE;
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
Capture::Capture() : Module("capture") {
@@ -44,12 +47,21 @@ namespace api::modules {
int screen = 0;
int quality = 70;
int divide = 1;
if (req.params.Size() > 0 && req.params[0].IsUint())
if (req.params.Size() > 0 && req.params[0].IsUint()) {
screen = req.params[0].GetUint();
if (req.params.Size() > 1 && req.params[1].IsUint())
}
if (CAPTURE_QUALITY.has_value()) {
quality = CAPTURE_QUALITY.value();
} else if (req.params.Size() > 1 && req.params[1].IsUint()) {
quality = req.params[1].GetUint();
if (req.params.Size() > 2 && req.params[2].IsUint())
}
if (CAPTURE_DIVIDE.has_value()) {
divide = CAPTURE_DIVIDE.value();
} else if (req.params.Size() > 2 && req.params[2].IsUint()) {
divide = req.params[2].GetUint();
}
// receive JPEG data
uint64_t timestamp = 0;
+5
View File
@@ -1,10 +1,15 @@
#pragma once
#include <optional>
#include "api/module.h"
#include "api/request.h"
namespace api::modules {
extern std::optional<uint32_t> CAPTURE_QUALITY;
extern std::optional<uint32_t> CAPTURE_DIVIDE;
class Capture : public Module {
public:
Capture();
+52 -4
View File
@@ -1,13 +1,9 @@
#include "iidx.h"
#include <functional>
#include <vector>
#include "games/iidx/iidx.h"
#include "external/rapidjson/document.h"
using namespace std::placeholders;
using namespace rapidjson;
namespace api::modules {
// settings
@@ -17,6 +13,11 @@ namespace api::modules {
functions["ticker_get"] = std::bind(&IIDX::ticker_get, this, _1, _2);
functions["ticker_set"] = std::bind(&IIDX::ticker_set, this, _1, _2);
functions["ticker_reset"] = std::bind(&IIDX::ticker_reset, this, _1, _2);
functions["tapeled_get"] = std::bind(&IIDX::tapeled_get, this, _1, _2);
for (auto &light : games::iidx::TAPELED_MAPPING) {
this->lights_by_names.emplace(light.lightName, light);
}
}
/**
@@ -69,4 +70,51 @@ namespace api::modules {
// disable read only
games::iidx::IIDXIO_LED_TICKER_READONLY = false;
}
/**
* tapeled_get()
* tapeled_get(name: str, ...)
*/
void IIDX::tapeled_get(Request &req, Response &res) {
Value response_object(kObjectType);
// all tape leds
if (req.params.Size() == 0) {
// Iterate through each device and dump its lights data into the response
for (const auto &mapping : games::iidx::TAPELED_MAPPING) {
copy_tapeled_data(res, response_object, mapping);
}
} else {
// specified light names
for (Value &param : req.params.GetArray()) {
// check params
if (!param.IsString()) {
error_type(res, "name", "string");
return;
}
const auto name = param.GetString();
if (const auto &it = lights_by_names.find(name); it != lights_by_names.end()) {
const auto mapping = it->second.get();
copy_tapeled_data(res, response_object, mapping);
}
}
}
res.add_data(response_object);
}
void IIDX::copy_tapeled_data(Response &res, Value &response_object, const tapeledutils::tape_led &mapping) {
// Create an array for the light state
Value light_state(kArrayType);
light_state.Reserve(mapping.data.capacity() * 3, res.doc()->GetAllocator());
for (const auto [r, g, b] : mapping.data) {
light_state.PushBack(r, res.doc()->GetAllocator());
light_state.PushBack(g, res.doc()->GetAllocator());
light_state.PushBack(b, res.doc()->GetAllocator());
}
// Can't use StringRef here, turns some strings partially into null bytes for some reason
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
}
}
+7
View File
@@ -2,6 +2,7 @@
#include "api/module.h"
#include "api/request.h"
#include "games/iidx/iidx.h"
namespace api::modules {
@@ -10,10 +11,16 @@ namespace api::modules {
IIDX();
private:
// state
robin_hood::unordered_map<std::string, std::reference_wrapper<tapeledutils::tape_led>> lights_by_names;
// function definitions
void ticker_get(Request &req, Response &res);
void ticker_set(Request &req, Response &res);
void ticker_reset(Request &req, Response &res);
void tapeled_get(Request &req, Response &res);
// helper
void copy_tapeled_data(Response &res, rapidjson::Value &response_object, const tapeledutils::tape_led &mapping);
};
}
@@ -16,3 +16,13 @@ def iidx_ticker_set(con: Connection, text: str):
def iidx_ticker_reset(con: Connection):
req = Request("iidx", "ticker_reset")
con.request(req)
def iidx_tapeled_get(con: Connection, *light_names):
req = Request("iidx", "tapeled_get")
for light_name in light_names:
req.add_param(light_name)
res = con.request(req)
return res.get_data()
+13 -3
View File
@@ -8,6 +8,7 @@
#include "external/robin_hood.h"
#include "launcher/logger.h"
#include "launcher/signal.h"
#include "util/detour.h"
#include "util/fileutils.h"
#include "util/libutils.h"
@@ -1736,7 +1737,13 @@ namespace avs {
return;
}
static void create_dir(
static void avs_dir_err(const std::filesystem::path &src_path)
{
launcher::signal::AVS_DIR_CREATION_FAILURE = TRUE;
launcher::signal::AVS_SRC_PATH = src_path.string();
}
static void create_avs_dir(
const std::string_view &avs_path,
const std::string_view &src_path)
{
@@ -1745,6 +1752,7 @@ namespace avs {
auto real_path = std::filesystem::absolute(src_path, err);
if (err) {
avs_dir_err(real_path);
log_warning("avs-core", "failed to resolve '{}' path: {}", avs_path, err.message());
return;
}
@@ -1754,7 +1762,9 @@ namespace avs {
if (created) {
log_info("avs-core", "created '{}' at '{}'", avs_path, real_path.string());
}
if (err) {
avs_dir_err(real_path);
log_warning("avs-core", "failed to create '{}' folder at '{}': {}",
avs_path,
real_path.string(),
@@ -1815,7 +1825,7 @@ namespace avs {
return;
}
create_dir(avs_path, device_path);
create_avs_dir(avs_path, device_path);
}
static void create_avs_config_fs_table(
@@ -1870,7 +1880,7 @@ namespace avs {
continue;
}
create_dir(dst_path, src_path);
create_avs_dir(dst_path, src_path);
} while ((vfs_node = property_node_traversal(vfs_node, TRAVERSE_NEXT_SEARCH_RESULT)));
}
+1
View File
@@ -4,6 +4,7 @@
#include <cstddef>
#include <string>
#include <windows.h>
#include <sys/stat.h>
namespace avs {
namespace core {
+13 -1
View File
@@ -41,6 +41,7 @@ namespace avs {
int HTTP11 = -1;
int URL_SLASH = -1;
int PCB_TYPE = -1;
bool EA3_DEBUG_VERBOSE = false;
// handle
std::string VERSION_STR = "unknown";
@@ -356,6 +357,12 @@ namespace avs {
}
}
// fall back to default PCBID if node is not found
if (!EA3_PCBID[0] && PCBID_CUSTOM.empty()) {
log_warning("avs-ea3", "no PCBID set, falling back to default PCBID value (04040000000000000000)");
PCBID_CUSTOM = "04040000000000000000";
}
// custom PCBID
if (!PCBID_CUSTOM.empty()) {
@@ -399,13 +406,18 @@ namespace avs {
avs::core::NODE_TYPE_str, "/ea3/id/accountid", &EA3_ACCOUNTID);
}
// check if PCBID is defined
// check if PCBID is defined - should never hit, left for sanity check only
if (avs::core::property_search(ea3_config, nullptr, "/ea3/id/pcbid") == nullptr) {
log_fatal("avs-ea3", "node not found: /ea3/id/pcbid (try using -p to specify PCBID)");
} else if (strlen(EA3_PCBID) == 0) {
log_fatal("avs-ea3", "no PCBID set (try using -p to specify PCBID)");
}
// remove <debug> since it can expose pcbid in logged requests
if (!EA3_DEBUG_VERBOSE) {
avs::core::property_search_remove_safe(ea3_config, nullptr, "/ea3/debug/verbose");
}
// remember IDs
char pcbid_buffer[256] { 0 };
char accountid_buffer[256] { 0 };
+1
View File
@@ -28,6 +28,7 @@ namespace avs {
extern int HTTP11;
extern int URL_SLASH;
extern int PCB_TYPE;
extern bool EA3_DEBUG_VERBOSE;
// handle
extern std::string VERSION_STR;
+29 -8
View File
@@ -76,24 +76,45 @@ namespace avs {
const auto dll_path_s = dll_path.string();
log_info("avs-game", "DLL path: {}", dll_path_s.c_str());
if (MAX_PATH <= (dll_path_s.length() + 64)) {
// MAX_PATH is 260
if (130 <= dll_path_s.length()) {
log_warning(
"avs-game",
"PATH TOO LONG WARNING\n\n\n"
"PATH TOO LONG WARNING\n\n"
"-------------------------------------------------------------------\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
" PATH TOO LONG \n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"'{}' has a length of {}\n"
"Most of these games will behave unexpectedly when the path is too long,\n"
"often resulting in random crashes. Move the game contents to a directory\n"
"with shorter path.\n"
"-------------------------------------------------------------------\n\n\n",
"The path '{}'\n"
" has a length of {}\n"
"Most of these games may behave unexpectedly when the path is too\n"
"long, often resulting in random crashes. Move the game contents to\n"
"a directory with shorter path.\n"
"-------------------------------------------------------------------\n\n",
dll_path_s, dll_path_s.length());
}
// ddr gamemdx.dll user error
if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") {
log_fatal(
"ddr",
"BAD GAME DLL ERROR\n\n"
"!!! !!!\n"
"!!! -exec gamemdx.dll was specified !!!\n"
"!!! this is the wrong DLL; the game will not load !!!\n"
"!!! remove -exec argument and try again. !!!\n"
"!!! !!!\n"
);
}
// file not found on disk
if (!fileutils::file_exists(dll_path)) {
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path.string().c_str());
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
}
if (fileutils::verify_header_pe(dll_path)) {
DLL_INSTANCE = libutils::load_library(dll_path);
}
@@ -101,7 +122,7 @@ namespace avs {
// load entry points
dll_entry_init = (ENTRY_INIT_T) libutils::get_proc(DLL_INSTANCE, ENTRY_INIT_NAME);
dll_entry_main = (ENTRY_MAIN_T) libutils::get_proc(DLL_INSTANCE, ENTRY_MAIN_NAME);
log_info("avs-game", "loaded successfully ({})", fmt::ptr(DLL_INSTANCE));
log_info("avs-game", "loaded {} successfully ({})", DLL_NAME, fmt::ptr(DLL_INSTANCE));
}
bool entry_init(char *sid_code, void *app_param) {
Regular → Executable
+61 -27
View File
@@ -19,6 +19,25 @@ function trap_error_exit {
trap trap_error_dbg DEBUG
trap trap_error_exit EXIT
IGNORE_CACHE=0
# Parse options
while getopts "ih" opt; do
case $opt in
i) IGNORE_CACHE=1 ;;
h)
echo "Usage: $0 [-i]"
echo " -i: Ignore build cache"
echo " -h: Show this help"
exit 0
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
# settings
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
GIT_HEAD=$(git rev-parse HEAD || echo "none")
@@ -41,7 +60,7 @@ DIST_ENABLE=1
DIST_FOLDER="./dist"
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
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"
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice spicetools_spice_laa spicetools_stubs_cpusbxpkm"
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64"
# determine build type
@@ -57,7 +76,7 @@ then
fi
# determine number of cores
CORES=$(awk '/^processor\t/ {cores[$NF]++} END {print length(cores)}' /proc/cpuinfo)
CORES=$(nproc)
# print information
echo ""
@@ -74,34 +93,45 @@ echo "Build Type: $BUILD_TYPE"
echo "Cores: $CORES"
echo ""
# 32 bit
echo "Building 32bit targets..."
echo "========================="
if ((CLEAN_BUILD > 0))
if ((IGNORE_CACHE > 0))
then
rm -rf ${BUILDDIR_32}
echo "Ignoring build cache..."
else
export CCACHE_DIR="$(pwd)/.ccache"
export CMAKE_CXX_COMPILER_LAUNCHER=ccache
export CMAKE_C_COMPILER_LAUNCHER=ccache
fi
mkdir -p ${BUILDDIR_32}
pushd ${BUILDDIR_32} > /dev/null
cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_32}
popd > /dev/null
# 64 bit
echo ""
echo "Building 64bit targets..."
echo "========================="
if ((CLEAN_BUILD > 0))
then
rm -rf ${BUILDDIR_64}
fi
mkdir -p ${BUILDDIR_64}
pushd ${BUILDDIR_64} > /dev/null
cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_64}
popd > /dev/null
time (
# 32 bit
echo "Building 32bit targets..."
echo "========================="
if ((CLEAN_BUILD > 0))
then
rm -rf ${BUILDDIR_32}
fi
mkdir -p ${BUILDDIR_32}
pushd ${BUILDDIR_32} > /dev/null
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
popd > /dev/null
echo ""
echo "Compilation process done :)"
echo "==========================="
# 64 bit
echo ""
echo "Building 64bit targets..."
echo "========================="
if ((CLEAN_BUILD > 0))
then
rm -rf ${BUILDDIR_64}
fi
mkdir -p ${BUILDDIR_64}
pushd ${BUILDDIR_64} > /dev/null
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
popd > /dev/null
echo ""
echo "Compilation process done :)"
echo "==========================="
)
# generate PDBs
if false # ((DEBUG > 0))
@@ -151,8 +181,9 @@ fi
echo "Copy files to output directory..."
rm -rf ${OUTDIR}
mkdir -p ${OUTDIR}
#mkdir -p ${OUTDIR}/stubs/32
mkdir -p ${OUTDIR}/stubs/32
mkdir -p ${OUTDIR}/stubs/64
mkdir -p ${OUTDIR}/extras/largeaddressaware
if false # ((DEBUG > 0))
then
# debug files
@@ -169,10 +200,12 @@ then
#cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
else
# release files
cp ${BUILDDIR_32}/spicetools/spicecfg.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/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null
#cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null
cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null
@@ -181,6 +214,7 @@ else
cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
fi
# pack source files to output directory
+2 -2
View File
@@ -1,6 +1,6 @@
docker build --pull external/docker -t spicetools/deps
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice --no-cache
docker run --rm -it -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin spicetools/spice
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice
docker run --rm -it -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin -v %cd%/.ccache:/src/src/spice2x/.ccache spicetools/spice %*
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
Regular → Executable
+2 -4
View File
@@ -1,6 +1,4 @@
#!/bin/bash
export DOCKER_BUILDKIT=0
docker build --pull $PWD/external/docker -t spicetools/deps --platform linux/x86_64
docker build --build-context gitroot=$PWD/../../.git . -t spicetools/spice:latest --no-cache
docker run --rm -it -v $PWD/dist:/src/src/spice2x/dist -v $PWD/bin:/src/src/spice2x/bin spicetools/spice
docker build --build-context gitroot=$PWD/../../.git . -t spicetools/spice:latest
docker run --rm -v $PWD/dist:/src/src/spice2x/dist -v $PWD/bin:/src/src/spice2x/bin -v $PWD/.ccache:/src/src/spice2x/.ccache spicetools/spice "$@"
+2
View File
@@ -0,0 +1,2 @@
del /s /q .ccache
call build_docker.bat
+34 -4
View File
@@ -1,5 +1,8 @@
#include "api.h"
#include <optional>
#include "launcher/superexit.h"
#include "rawinput/rawinput.h"
#include "rawinput/piuio.h"
#include "util/time.h"
@@ -64,18 +67,29 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
auto current_button = &_button;
auto alternatives = check_alts ? &current_button->getAlternatives() : nullptr;
unsigned int button_count = 0;
std::optional<bool> window_has_focus;
while (true) {
// naive behavior
if (current_button->isNaive()) {
GameAPI::Buttons::State state;
auto vkey = current_button->getVKey();
// check for focus
if (vkey != INVALID_VKEY && rawinput::NAIVE_REQUIRE_FOCUS) {
if (!window_has_focus.has_value()) {
window_has_focus = superexit::has_focus();
}
if (!window_has_focus.value()) {
vkey = INVALID_VKEY;
}
}
// read
auto vkey = current_button->getVKey();
GameAPI::Buttons::State state;
if (vkey == 0xFF) {
if (vkey == INVALID_VKEY) {
state = BUTTON_NOT_PRESSED;
} else {
state = (GetAsyncKeyState(current_button->getVKey()) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
state = (GetAsyncKeyState(vkey) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
// invert
@@ -106,6 +120,16 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
auto &devid = current_button->getDeviceIdentifier();
auto device = manager->devices_get(devid, false); // TODO: fix to update only
// check for focus
if (device && rawinput::RAWINPUT_REQUIRE_FOCUS) {
if (!window_has_focus.has_value()) {
window_has_focus = superexit::has_focus();
}
if (!window_has_focus.value()) {
device = nullptr;
}
}
// get state if device was marked as updated
GameAPI::Buttons::State state = current_button->getLastState();
double *last_up = nullptr;
@@ -571,6 +595,12 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
switch (device->type) {
case rawinput::MOUSE: {
// check for focus
if (rawinput::NAIVE_REQUIRE_FOCUS && !superexit::has_focus()) {
value = analog.getLastState();
break;
}
// get mouse position
auto mouse = device->mouseInfo;
long pos;
+1 -1
View File
@@ -284,7 +284,7 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
std::string vKeyString = fmt::format("{:#x}", vKey);
// device must be existing
if (this->device_identifier.empty() && vKey == 0xFF) {
if (this->device_identifier.empty() && vKey == INVALID_VKEY) {
return "";
}
+6 -4
View File
@@ -33,12 +33,14 @@ enum ButtonAnalogType {
extern const char *ButtonAnalogTypeStr[];
constexpr unsigned short INVALID_VKEY = UINT16_C(0xFF);
class Button {
private:
std::vector<Button> alternatives;
std::string name;
std::string device_identifier = "";
unsigned short vKey = 0xFF;
unsigned short vKey = INVALID_VKEY;
ButtonAnalogType analog_type = BAT_NONE;
double debounce_up = 0.0;
double debounce_down = 0.0;
@@ -68,12 +70,12 @@ public:
if (this->override_enabled) {
return true;
}
if (this->vKey != 0xFF) {
if (this->vKey != INVALID_VKEY) {
return true;
}
for (auto &alternative : this->alternatives) {
if (alternative.vKey != 0xFF) {
if (alternative.vKey != INVALID_VKEY) {
return true;
}
}
@@ -82,7 +84,7 @@ public:
}
inline void clearBindings() {
vKey = 0xFF;
vKey = INVALID_VKEY;
alternatives.clear();
device_identifier = "";
analog_type = BAT_NONE;
+1
View File
@@ -43,6 +43,7 @@ public:
std::string value;
std::vector<Option> alternatives;
bool disabled = false;
bool conflicting = false;
explicit Option(OptionDefinition definition, std::string value = "") :
definition(std::move(definition)), value(std::move(value)) {
+12 -22
View File
@@ -15,12 +15,18 @@ namespace cfg {
std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
ScreenResize::ScreenResize() {
bool file_exists = false;
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
if (fileutils::file_exists(this->config_path)) {
log_info("ScreenResize", "loading config from: {}", this->config_path.string());
file_exists = true;
}
} else {
this->config_path = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools_screen_resize.json";
this->config_path =
fileutils::get_config_file_path("ScreenResize", "spicetools_screen_resize.json", &file_exists);
}
if (fileutils::file_exists(this->config_path)) {
if (file_exists) {
this->config_load();
}
}
@@ -29,12 +35,6 @@ namespace cfg {
}
void ScreenResize::config_load() {
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
log_info("ScreenResize", "loading custom config: {}", this->config_path.string());
} else {
log_info("ScreenResize", "loading global config from APPDATA");
}
std::string config = fileutils::text_read(this->config_path);
if (config.empty()) {
log_info("ScreenResize", "config is empty");
@@ -81,16 +81,12 @@ namespace cfg {
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
auto& scene = this->scene_settings[i];
std::string prefix = "";
if (0 < i) {
prefix += fmt::format("scenes/{}/", i-1);
}
const std::string prefix = fmt::format("scenes/{}/", i);
load_int_value(doc, root + prefix + "offset_x", scene.offset_x);
load_int_value(doc, root + prefix + "offset_y", scene.offset_y);
load_float_value(doc, root + prefix + "scale_x", scene.scale_x);
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 + "centered", scene.centered);
}
// windowed settings are always under game settings
@@ -174,8 +170,6 @@ namespace cfg {
}
void ScreenResize::config_save() {
log_info("ScreenResize", "saving config: {}", this->config_path.string());
rapidjson::Document doc;
std::string config = fileutils::text_read(this->config_path);
if (!config.empty()) {
@@ -201,16 +195,12 @@ namespace cfg {
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
auto& scene = this->scene_settings[i];
std::string prefix = "";
if (0 < i) {
prefix += fmt::format("scenes/{}/", i-1);
}
const std::string prefix = fmt::format("scenes/{}/", i);
rapidjson::Pointer(root + prefix + "offset_x").Set(doc, scene.offset_x);
rapidjson::Pointer(root + prefix + "offset_y").Set(doc, scene.offset_y);
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 + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio);
rapidjson::Pointer(root + prefix + "centered").Set(doc, scene.centered);
}
// windowed mode settings
@@ -229,10 +219,10 @@ namespace cfg {
doc.Accept(writer);
// save to file
if (fileutils::text_write(this->config_path, buffer.GetString())) {
if (fileutils::write_config_file("ScreenResize", this->config_path, buffer.GetString())) {
// this->config_dirty = false;
} else {
log_warning("ScreenResize", "unable to save config file to {}", this->config_path.string());
log_warning("ScreenResize", "unable to save config file");
}
}
}
-1
View File
@@ -20,7 +20,6 @@ namespace cfg {
float scale_x = 1.0;
float scale_y = 1.0;
bool keep_aspect_ratio = true;
bool centered = true;
};
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
+77
View File
@@ -1,3 +1,80 @@
09/30/2025 [spice2x]
Prevent verbose debug EA3 logging by default
Options UI tweaks
09/28/2025 [spice2x]
IIDX: auto apply ASIO compatibility fix for IIDX32+
Misc diagnosability updates
09/22/2025 [spice2x]
Add Polaris Chord lights
Add boot.dll to Gitadora patches target
Misc fixes
09/20/2025 [spice2x]
Polaris Chord support for knobs/turntables/mouse as fader input
Misc fixes
09/18/2025 [spice2x]
Polaris Chord windowed mode fixes
UI tweaks
09/17/2025 [spice2x]
Polaris Chord I/O fixes, language shim
09/16/2025 [spice2x]
Add Polaris Chord support
Tweak ImGUI theme
08/21/2025 [spice2x]
DDR: check for common DLL errors
Supply default PCBID if absent
Rearrange options
08/17/2025 [spice2x]
DDR: fix codec registration failure when path contains spaces
08/11/2025 [spice2x]
SDVX: fix camera hook, remove -sdvxdisablecams
DDR: automatically register codecs in com folder on boot
06/03/2025 [spice2x]
Add libshare-pj.dll to Gitadora patch import
Detect ACIO init failures
05/31/2025 [spice2x]
IIDX: fix windowed subscreen not accepting mouse clicks when
overlay is active
IIDX: tape LED over API
Option to make command line args take precedence
UI tweaks
05/09/2025 [spice2x]
Check for window focus when processing input (-inputfocus)
More options for IIDX/SDVX windowed subscreen
SpiceCompanion screen mirroring quality options
Various LovePlus improvements and fixes:
Allow camera access
Full screen mode
Add printer options
Add stub for printer DLL (cpusbxpkm.dll)
Logging improvements for better troubleshooting
04/25/2025 [spice2x]
LargeAddressAware variant of spice.exe for Metal Gear
Save new JSON config files to %appdata%/spice2x
Various bug fixes
04/08/2025 [spice2x]
Ensure rawinput touch is default for all games (-wintouch to override)
Various overlay UI fixes and improvements, update ImGui library
Add missing lights in Museca
03/29/2025 [spice2x]
Add SDVX landscape mode, -forceresswap option
Fix bugs in image scaler
Configurator UI tweaks
03/25/2025 [spice2x]
Add Mahjong Fight Girl support
Add option for custom full screen resolution (-forceres)
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.2.0)
cmake_minimum_required (VERSION 3.12)
project (DiscordRPC)
include(GNUInstallDirs)
+6 -14
View File
@@ -1,25 +1,17 @@
FROM archlinux:base
FROM ghcr.io/archlinux/archlinux:base-devel
RUN pacman --noconfirm -Syu gettext \
base-devel \
git \
RUN pacman --noconfirm -Syu git \
bash \
zip \
upx \
sudo \
binutils \
file \
make \
gcc \
fakeroot \
diffutils \
ninja \
cmake \
awk \
unzip \
mingw-w64-crt \
mingw-w64-winpthreads \
mingw-w64-gcc \
mingw-w64-headers \
mingw-w64-binutils
mingw-w64-binutils \
ccache
RUN useradd user -m && echo "ALL ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
RUN su user -c "cd /tmp/ && git clone https://aur.archlinux.org/yay.git --depth=1 && cd yay && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
RUN su user -c "cd /tmp/ && git clone https://aur.archlinux.org/yay-bin.git --depth=1 && cd yay-bin && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
+4
View File
@@ -1527,8 +1527,10 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
return arg;
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
// The type template parameter is there to avoid an ODR violation when using
// a fallback formatter in one translation unit and an implicit conversion in
@@ -1544,7 +1546,9 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> {
return arg;
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(!IS_PACKED)>
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9)
cmake_minimum_required(VERSION 3.12)
project(imgui CXX)
set(HASH_LIBRARY_HEADERS
+5 -3
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9)
cmake_minimum_required(VERSION 3.12)
project(imgui CXX)
# dear imgui uses the C++ virtual interfaces for DirectX
@@ -8,7 +8,8 @@ set(IMGUI_HEADERS
imconfig.h
imgui.h
imgui_internal.h
imgui_stdlib.h
backends/imgui_impl_dx9.h
misc/cpp/imgui_stdlib.h
)
set(IMGUI_SOURCES
imgui.cpp
@@ -16,7 +17,8 @@ set(IMGUI_SOURCES
imgui_tables.cpp
imgui_widgets.cpp
imgui_demo.cpp
imgui_stdlib.cpp
backends/imgui_impl_dx9.cpp
misc/cpp/imgui_stdlib.cpp
)
add_library(imgui STATIC ${IMGUI_HEADERS} ${IMGUI_SOURCES})
@@ -22,14 +22,14 @@
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
#include "impl_dx9.h"
#include "imgui_impl_dx9.h"
#include <algorithm>
// DirectX
#include <d3d9.h>
#include "external/imgui/imgui.h"
#include "imgui.h"
// allow std::min use
#ifdef min
@@ -327,7 +327,7 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() {
if (g_FontTexture) {
g_FontTexture->Release();
g_FontTexture = NULL;
ImGui::GetIO().Fonts->TexID = NULL;
ImGui::GetIO().Fonts->TexID = 0;
} // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
}
@@ -11,7 +11,7 @@
#pragma once
#include "external/imgui/imgui.h"
#include "imgui.h"
struct IDirect3DDevice9;
+33 -16
View File
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// DEAR IMGUI COMPILE-TIME OPTIONS
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
@@ -9,7 +9,7 @@
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
@@ -21,27 +21,29 @@
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
@@ -49,8 +51,13 @@
#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Enable Test Engine / Automation features.
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
//---- Include imgui_user.h at the end of imgui.h as a convenience
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
//#define IMGUI_INCLUDE_IMGUI_USER_H
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
#define IMGUI_USE_BGRA_PACKED_COLOR
@@ -58,16 +65,17 @@
//---- Performance optimization for DX9
#define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
@@ -78,6 +86,15 @@
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
// Only works in combination with IMGUI_ENABLE_FREETYPE.
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
@@ -108,7 +125,7 @@
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
@@ -116,10 +133,10 @@
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
void MyFunction(const char* name, MyMatrix44* mtx);
}
*/
+4651 -2301
View File
File diff suppressed because it is too large Load Diff
+1497 -902
View File
File diff suppressed because it is too large Load Diff
+3015 -663
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+139 -107
View File
@@ -2,8 +2,11 @@
// This is a slightly modified version of stb_textedit.h 1.14.
// Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// - Added name to struct or it may be forward declared in our code.
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
// Grep for [DEAR IMGUI] to find the changes.
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
// stb_textedit.h - v1.14 - public domain - Sean Barrett
// Development of this library was sponsored by RAD Game Tools
@@ -30,7 +33,7 @@
// DEPENDENCIES
//
// Uses the C runtime function 'memmove', which you can override
// by defining STB_TEXTEDIT_memmove before the implementation.
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
// Uses no other functions. Performs no runtime allocations.
//
//
@@ -40,7 +43,7 @@
// 1.13 (2019-02-07) fix bug in undo size management
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
// 1.9 (2016-08-27) customizable move-by-word
// 1.8 (2016-04-02) better keyboard handling when mouse button is down
// 1.7 (2015-09-13) change y range handling in case baseline is non-0
@@ -208,6 +211,7 @@
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
//
// Each of these functions potentially updates the string and updates the
// state.
@@ -242,7 +246,12 @@
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
// anything other type you wante before including.
// anything other type you want before including.
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: [DEAR IMGUI] added 2024-09
// call this to text inputs sent to the textfield.
//
//
// When rendering, you can read the cursor position and selection state from
@@ -274,8 +283,8 @@
////
////
#ifndef INCLUDE_STB_TEXTEDIT_H
#define INCLUDE_STB_TEXTEDIT_H
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
#define INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////
//
@@ -286,38 +295,38 @@
// and undo state.
//
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
#endif
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
#endif
#ifndef STB_TEXTEDIT_CHARTYPE
#define STB_TEXTEDIT_CHARTYPE int
#ifndef IMSTB_TEXTEDIT_CHARTYPE
#define IMSTB_TEXTEDIT_CHARTYPE int
#endif
#ifndef STB_TEXTEDIT_POSITIONTYPE
#define STB_TEXTEDIT_POSITIONTYPE int
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
#define IMSTB_TEXTEDIT_POSITIONTYPE int
#endif
typedef struct
{
// private data
STB_TEXTEDIT_POSITIONTYPE where;
STB_TEXTEDIT_POSITIONTYPE insert_length;
STB_TEXTEDIT_POSITIONTYPE delete_length;
IMSTB_TEXTEDIT_POSITIONTYPE where;
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
int char_storage;
} StbUndoRecord;
typedef struct
{
// private data
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
short undo_point, redo_point;
int undo_char_point, redo_char_point;
} StbUndoState;
typedef struct
typedef struct STB_TexteditState
{
/////////////////////
//
@@ -371,7 +380,7 @@ typedef struct
float ymin,ymax; // height of row above and below baseline
int num_chars;
} StbTexteditRow;
#endif //INCLUDE_STB_TEXTEDIT_H
#endif //INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////////
@@ -384,11 +393,11 @@ typedef struct
// implementation isn't include-guarded, since it might have indirectly
// included just the "header" portion
#ifdef STB_TEXTEDIT_IMPLEMENTATION
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
#ifndef STB_TEXTEDIT_memmove
#ifndef IMSTB_TEXTEDIT_memmove
#include <string.h>
#define STB_TEXTEDIT_memmove memmove
#define IMSTB_TEXTEDIT_memmove memmove
#endif
@@ -398,7 +407,7 @@ typedef struct
//
// traverse the layout to locate the nearest character to a display position
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
{
StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str);
@@ -437,13 +446,13 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
if (x < r.x1) {
// search characters in row for one that straddles 'x'
prev_x = r.x0;
for (k=0; k < r.num_chars; ++k) {
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
if (x < prev_x+w) {
if (x < prev_x+w/2)
return k+i;
else
return k+i+1;
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
}
prev_x += w;
}
@@ -458,7 +467,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
}
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text
@@ -476,7 +485,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
int p = 0;
@@ -502,11 +511,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
//
// forward declarations
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
typedef struct
{
@@ -518,7 +527,7 @@ typedef struct
// find the x/y location of a character, and remember info about the previous row in
// case we get a move-up event (for page up, we'll have to rescan)
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
{
StbTexteditRow r;
int prev_start = 0;
@@ -549,7 +558,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
i += r.num_chars;
find->y += r.baseline_y_delta;
if (i == z) // [DEAR IMGUI]
{
r.num_chars = 0; // [DEAR IMGUI]
break; // [DEAR IMGUI]
}
}
find->first_char = first = i;
@@ -559,14 +571,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
// now scan to find xpos
find->x = r.x0;
for (i=0; first+i < n; ++i)
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
}
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
// make the selection/cursor state valid if client altered the string
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
int n = STB_TEXTEDIT_STRINGLEN(str);
if (STB_TEXT_HAS_SELECTION(state)) {
@@ -580,7 +592,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// delete characters while updating undo
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
{
stb_text_makeundo_delete(str, state, where, len);
STB_TEXTEDIT_DELETECHARS(str, where, len);
@@ -588,7 +600,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
}
// delete the section
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
stb_textedit_clamp(str, state);
if (STB_TEXT_HAS_SELECTION(state)) {
@@ -625,7 +637,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
}
// move cursor to last character of selection
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_sortselection(state);
@@ -636,14 +648,25 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
}
}
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
#endif
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
}
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
@@ -658,7 +681,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
#endif
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
@@ -685,7 +708,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
}
// API cut: delete selection
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_delete_selection(str,state); // implicitly clamps
@@ -696,7 +719,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
}
// API paste: replace existing selection with passed-in text
static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
{
// if there's a selection, the paste should delete it
stb_textedit_clamp(str, state);
@@ -716,36 +739,44 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
#define STB_TEXTEDIT_KEYTYPE int
#endif
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
// can't add newline in single-line mode
if (text[0] == '\n' && state->single_line)
return;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
else {
stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
stb_text_makeundo_insert(state, state->cursor, text_len);
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
}
// API key: process a keyboard input
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{
retry:
switch (key) {
default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
// can't add newline in single-line mode
if (c == '\n' && state->single_line)
break;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
++state->cursor;
state->has_preferred_x = 0;
}
} else {
stb_textedit_delete_selection(str,state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
stb_text_makeundo_insert(state, state->cursor, 1);
++state->cursor;
state->has_preferred_x = 0;
}
}
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
stb_textedit_text(str, state, &ch, 1);
}
#endif
break;
}
@@ -771,7 +802,7 @@ retry:
stb_textedit_move_to_first(state);
else
if (state->cursor > 0)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
@@ -780,7 +811,7 @@ retry:
if (STB_TEXT_HAS_SELECTION(state))
stb_textedit_move_to_last(str, state);
else
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
stb_textedit_clamp(str, state);
state->has_preferred_x = 0;
break;
@@ -790,7 +821,7 @@ retry:
stb_textedit_prep_selection_at_cursor(state);
// move selection left
if (state->select_end > 0)
--state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
state->cursor = state->select_end;
state->has_preferred_x = 0;
break;
@@ -840,7 +871,7 @@ retry:
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
stb_textedit_prep_selection_at_cursor(state);
// move selection right
++state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
stb_textedit_clamp(str, state);
state->cursor = state->select_end;
state->has_preferred_x = 0;
@@ -889,14 +920,14 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
if (x > goal_x)
break;
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
}
stb_textedit_clamp(str, state);
@@ -951,14 +982,14 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
if (x > goal_x)
break;
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
}
stb_textedit_clamp(str, state);
@@ -986,7 +1017,7 @@ retry:
else {
int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->cursor < n)
stb_textedit_delete(str, state, state->cursor, 1);
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
}
state->has_preferred_x = 0;
break;
@@ -998,8 +1029,9 @@ retry:
else {
stb_textedit_clamp(str, state);
if (state->cursor > 0) {
stb_textedit_delete(str, state, state->cursor-1, 1);
--state->cursor;
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
stb_textedit_delete(str, state, prev, state->cursor - prev);
state->cursor = prev;
}
}
state->has_preferred_x = 0;
@@ -1109,8 +1141,8 @@ retry:
static void stb_textedit_flush_redo(StbUndoState *state)
{
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
}
// discard the oldest entry in the undo list
@@ -1122,13 +1154,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
int n = state->undo_rec[0].insert_length, i;
// delete n characters from all other records
state->undo_char_point -= n;
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
for (i=0; i < state->undo_point; ++i)
if (state->undo_rec[i].char_storage >= 0)
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
}
--state->undo_point;
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
}
}
@@ -1138,7 +1170,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
// fill up even though the undo buffer didn't
static void stb_textedit_discard_redo(StbUndoState *state)
{
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
if (state->redo_point <= k) {
// if the k'th undo state has characters, clean those up
@@ -1146,7 +1178,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
int n = state->undo_rec[k].insert_length, i;
// move the remaining redo character data to the end of the buffer
state->redo_char_point += n;
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
// adjust the position of all the other records to account for above memmove
for (i=state->redo_point; i < k; ++i)
if (state->undo_rec[i].char_storage >= 0)
@@ -1154,12 +1186,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
}
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
// [DEAR IMGUI]
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
// now move redo_point to point to the new one
++state->redo_point;
@@ -1173,32 +1205,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
// if we have no free records, we have to make room, by sliding the
// existing records down
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
stb_textedit_discard_undo(state);
// if the characters to store won't possibly fit in the buffer, we can't undo
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
state->undo_point = 0;
state->undo_char_point = 0;
return NULL;
}
// if we don't have enough free characters in the buffer, we have to make room
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
stb_textedit_discard_undo(state);
return &state->undo_rec[state->undo_point++];
}
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
{
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
if (r == NULL)
return NULL;
r->where = pos;
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
if (insert_len == 0) {
r->char_storage = -1;
@@ -1210,7 +1242,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
}
}
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord u, *r;
@@ -1237,7 +1269,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// characters stored for *undoing* don't leave room for redo
// if the last is true, we have to bail
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
// the undo records take up too much character space; there's no space to store the redo characters
r->insert_length = 0;
} else {
@@ -1246,7 +1278,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// there's definitely room to store the characters eventually
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
// should never happen:
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// there's currently not enough room, so discard a redo record
stb_textedit_discard_redo(s);
@@ -1278,11 +1310,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
s->redo_point--;
}
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord *u, r;
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// we need to do two things: apply the redo record, and create an undo record
@@ -1334,20 +1366,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
stb_text_createundo(&state->undostate, where, 0, length);
}
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
{
int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
if (p) {
for (i=0; i < length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
}
}
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
{
int i;
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
if (p) {
for (i=0; i < old_length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
@@ -1359,8 +1391,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
{
state->undostate.undo_point = 0;
state->undostate.undo_char_point = 0;
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
state->select_end = state->select_start = 0;
state->cursor = 0;
state->has_preferred_x = 0;
@@ -1383,16 +1415,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
{
return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
}
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif//STB_TEXTEDIT_IMPLEMENTATION
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
/*
------------------------------------------------------------------------------
+1 -1
View File
@@ -656,7 +656,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
// If skip != 0, this tells stb_truetype to skip any codepoints for which
// there is no corresponding glyph. If skip=0, which is the default, then
// codepoints without a glyph recived the font's "missing character" glyph,
// codepoints without a glyph received the font's "missing character" glyph,
// typically an empty box by convention.
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9)
cmake_minimum_required(VERSION 3.12)
cmake_policy(SET CMP0069 NEW)
project(minhook)
+1 -1
View File
@@ -14,7 +14,7 @@ namespace games::ccj {
bool MOUSE_TRACKBALL_USE_TOGGLE = false;
uint8_t TRACKBALL_SENSITIVITY = 10;
static HANDLE fakeHandle = (HANDLE)0xDEADBEEF;
static HANDLE fakeHandle = (HANDLE)0xDEADBEEFull;
static HWND hWnd = nullptr;
static WNDPROC wndProc = nullptr;
static std::thread *tbThread = nullptr;
+82
View File
@@ -27,6 +27,7 @@ namespace games::ddr {
// settings
bool SDMODE = false;
bool NO_CODEC_REGISTRATION = false;
uint8_t DDR_TAPELEDS[TAPELED_DEVICE_COUNT][50][3] {};
@@ -49,9 +50,79 @@ namespace games::ddr {
return SendMessage_real(hWnd, Msg, wParam, lParam);
}
bool contains_only_ascii(const std::string& str) {
for (auto c: str) {
if (static_cast<unsigned char>(c) > 127) {
return false;
}
}
return true;
}
DDRGame::DDRGame() : Game("Dance Dance Revolution") {
}
void DDRGame::register_codecs() {
// find where spice.exe / spice64.exe is located
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
// find the com directory
std::filesystem::path dir = "";
if (MODULE_PATH == spice_bin_path) {
// try: \com
dir = spice_bin_path / "com";
} else {
// try: modules\..\com
dir = MODULE_PATH / ".." / "com";
}
if (fileutils::dir_exists(dir)) {
log_info("ddr", "looking for codecs in this directory: {}", dir.string());
} else {
log_info("ddr", "codecs directory not found: {}", dir.string());
return;
}
for (const auto &file : std::filesystem::directory_iterator(dir)) {
const auto &filename = file.path().filename();
const auto extension = strtolower(filename.extension().string());
if (extension != ".dll") {
continue;
}
log_info("ddr", "found DLL: {}, size: {} bytes", filename.string(), file.file_size());
if (filename == "k-clvsd.dll" || filename.string().find("xactengine") == 0) {
const std::wstring wcmd = L"regsvr32.exe /s \"" + file.path().wstring() + L"\"";
const std::string cmd = "regsvr32.exe /s \"" + file.path().string() + "\"";
int result = 0;
std::thread t([wcmd, &result]() {
result = _wsystem(wcmd.c_str());
});
t.join();
if (result == 0) {
log_info("ddr", "`{}` returned {}", cmd, result);
} else {
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
}
if (!contains_only_ascii(file.path().string())) {
log_warning(
"ddr",
"BAD PATH ERROR\n\n\n"
"!!! !!!\n"
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
"!!! this may cause the game to crash! !!!\n"
"!!! !!!\n"
);
}
}
}
}
void DDRGame::pre_attach() {
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) {
log_fatal(
@@ -87,6 +158,17 @@ namespace games::ddr {
"!!! !!!\n\n\n"
);
}
if (!cfg::CONFIGURATOR_STANDALONE) {
if (!NO_CODEC_REGISTRATION) {
this->register_codecs();
} else {
log_warning(
"ddr",
"skipping codec registration (-ddrnocodec), "
"game may crash if you didn't register codecs before launching the game");
}
}
}
void DDRGame::attach() {
+4
View File
@@ -7,6 +7,7 @@ namespace games::ddr {
// settings
extern bool SDMODE;
extern bool NO_CODEC_REGISTRATION;
// Buffers to store RGB data for tape LEDs on gold cabinets
const size_t TAPELED_DEVICE_COUNT = 11;
@@ -18,5 +19,8 @@ namespace games::ddr {
virtual void pre_attach() override;
virtual void attach() override;
virtual void detach() override;
private:
void register_codecs();
};
}
+10 -33
View File
@@ -409,50 +409,27 @@ namespace games::iidx {
* 16 - side panel right - 183 bytes - 61 colors
*
* data is stored in RGB order, 3 bytes per color
*
* TODO: expose this data via API
*/
// data mapping
static struct TapeLedMapping {
size_t data_size;
int index_r, index_g, index_b;
TapeLedMapping(size_t data_size, int index_r, int index_g, int index_b)
: data_size(data_size), index_r(index_r), index_g(index_g), index_b(index_b) {}
} mapping[] = {
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB },
{ 19, Lights::StageRightAvgR, Lights::StageRightAvgG, Lights::StageRightAvgB },
{ 45, Lights::CabinetLeftAvgR, Lights::CabinetLeftAvgG, Lights::CabinetLeftAvgB },
{ 45, Lights::CabinetRightAvgR, Lights::CabinetRightAvgG, Lights::CabinetRightAvgB },
{ 21, Lights::ControlPanelUnderAvgR, Lights::ControlPanelUnderAvgG, Lights::ControlPanelUnderAvgB },
{ 54, Lights::CeilingLeftAvgR, Lights::CeilingLeftAvgG, Lights::CeilingLeftAvgB },
{ 11, Lights::TitleLeftAvgR, Lights::TitleLeftAvgG, Lights::TitleLeftAvgB },
{ 11, Lights::TitleRightAvgR, Lights::TitleRightAvgG, Lights::TitleRightAvgB },
{ 54, Lights::CeilingRightAvgR, Lights::CeilingRightAvgG, Lights::CeilingRightAvgB },
{ 17, Lights::TouchPanelLeftAvgR, Lights::TouchPanelLeftAvgG, Lights::TouchPanelLeftAvgB },
{ 17, Lights::TouchPanelRightAvgR, Lights::TouchPanelRightAvgG, Lights::TouchPanelRightAvgB },
{ 68, Lights::SidePanelLeftInnerAvgR, Lights::SidePanelLeftInnerAvgG, Lights::SidePanelLeftInnerAvgB },
{ 68, Lights::SidePanelLeftOuterAvgR, Lights::SidePanelLeftOuterAvgG, Lights::SidePanelLeftOuterAvgB },
{ 61, Lights::SidePanelLeftAvgR, Lights::SidePanelLeftAvgG, Lights::SidePanelLeftAvgB },
{ 68, Lights::SidePanelRightOuterAvgR, Lights::SidePanelRightOuterAvgG, Lights::SidePanelRightOuterAvgB },
{ 68, Lights::SidePanelRightInnerAvgR, Lights::SidePanelRightInnerAvgG, Lights::SidePanelRightInnerAvgB },
{ 61, Lights::SidePanelRightAvgR, Lights::SidePanelRightAvgG, Lights::SidePanelRightAvgB },
};
// check index bounds
if (tapeledutils::is_enabled() && index < std::size(mapping)) {
auto &map = mapping[index];
if (tapeledutils::is_enabled() && index < std::size(TAPELED_MAPPING)) {
auto &map = TAPELED_MAPPING[index];
const auto data_size = map.data.capacity();
// pick a color to use
const auto rgb = tapeledutils::pick_color_from_led_tape(data, map.data_size);
const auto rgb = tapeledutils::pick_color_from_led_tape(data, data_size);
// program the lights into API
auto &lights = get_lights();
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_r], rgb.r);
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
for (unsigned int i = 0; i < data_size; ++i) {
map.data[i].r = data[i * 3];
map.data[i].g = data[i * 3 + 1];
map.data[i].b = data[i * 3 + 2];
}
}
if (This != custom_node) {
+5 -4
View File
@@ -60,7 +60,7 @@ namespace games::iidx {
static IIDXLocalCamera *front_camera = nullptr; // camera id #1
std::vector<IIDXLocalCamera*> LOCAL_CAMERA_LIST = {};
static IDirect3DDeviceManager9 *s_pD3DManager = nullptr;
std::filesystem::path CAMERA_CONFIG_PATH = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools_camera_control.json";
std::filesystem::path CAMERA_CONFIG_PATH;
bool CAMERA_READY = false;
bool parse_cmd_params() {
@@ -493,7 +493,8 @@ namespace games::iidx {
}
bool camera_config_load() {
log_info("iidx:camhook", "loading config");
CAMERA_CONFIG_PATH =
fileutils::get_config_file_path("iidx::camhook", "spicetools_camera_control.json");
try {
// read config file
@@ -651,9 +652,9 @@ namespace games::iidx {
doc.Accept(writer);
// save to file
if (fileutils::text_write(CAMERA_CONFIG_PATH, buffer.GetString())) {
if (fileutils::write_config_file("iidx::camhook", CAMERA_CONFIG_PATH, buffer.GetString())) {
} else {
log_warning("iidx:camhook", "unable to save config file to {}", CAMERA_CONFIG_PATH.string());
log_warning("iidx:camhook", "unable to save config file");
}
return true;
+72 -2
View File
@@ -8,6 +8,7 @@
#include "games/iidx/legacy_camera.h"
#include "hooks/audio/audio.h"
#include "hooks/avshook.h"
#include "hooks/cfgmgr32hook.h"
#include "hooks/devicehook.h"
@@ -27,6 +28,9 @@
#include "util/memutils.h"
#include "util/sigscan.h"
#include "util/utils.h"
#include "launcher/signal.h"
#include "external/robin_hood.h"
#include "bi2a.h"
#include "bi2x_hook.h"
@@ -74,6 +78,26 @@ namespace games::iidx {
bool IIDXIO_LED_TICKER_READONLY = false;
std::mutex IIDX_LED_TICKER_LOCK;
tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL] = {
{ 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" },
{ 19, Lights::StageRightAvgR, Lights::StageRightAvgG, Lights::StageRightAvgB, "Stage Right" },
{ 45, Lights::CabinetLeftAvgR, Lights::CabinetLeftAvgG, Lights::CabinetLeftAvgB, "Cabinet Left" },
{ 45, Lights::CabinetRightAvgR, Lights::CabinetRightAvgG, Lights::CabinetRightAvgB, "Cabinet Right" },
{ 21, Lights::ControlPanelUnderAvgR, Lights::ControlPanelUnderAvgG, Lights::ControlPanelUnderAvgB, "Control Panel Under" },
{ 54, Lights::CeilingLeftAvgR, Lights::CeilingLeftAvgG, Lights::CeilingLeftAvgB, "Ceiling Left" },
{ 11, Lights::TitleLeftAvgR, Lights::TitleLeftAvgG, Lights::TitleLeftAvgB, "Title Left" },
{ 11, Lights::TitleRightAvgR, Lights::TitleRightAvgG, Lights::TitleRightAvgB, "Title Right" },
{ 54, Lights::CeilingRightAvgR, Lights::CeilingRightAvgG, Lights::CeilingRightAvgB, "Ceiling Right" },
{ 17, Lights::TouchPanelLeftAvgR, Lights::TouchPanelLeftAvgG, Lights::TouchPanelLeftAvgB, "Touch Panel Left" },
{ 17, Lights::TouchPanelRightAvgR, Lights::TouchPanelRightAvgG, Lights::TouchPanelRightAvgB, "Touch Panel Right" },
{ 68, Lights::SidePanelLeftInnerAvgR, Lights::SidePanelLeftInnerAvgG, Lights::SidePanelLeftInnerAvgB, "Side Panel Left Inner" },
{ 68, Lights::SidePanelLeftOuterAvgR, Lights::SidePanelLeftOuterAvgG, Lights::SidePanelLeftOuterAvgB, "Side Panel Left Outer" },
{ 61, Lights::SidePanelLeftAvgR, Lights::SidePanelLeftAvgG, Lights::SidePanelLeftAvgB, "Side Panel Left" },
{ 68, Lights::SidePanelRightOuterAvgR, Lights::SidePanelRightOuterAvgG, Lights::SidePanelRightOuterAvgB, "Side Panel Right Outer" },
{ 68, Lights::SidePanelRightInnerAvgR, Lights::SidePanelRightInnerAvgG, Lights::SidePanelRightInnerAvgB, "Side Panel Right Inner" },
{ 61, Lights::SidePanelRightAvgR, Lights::SidePanelRightAvgG, Lights::SidePanelRightAvgB, "Side Panel Right" },
};
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
if (lpSubKey != nullptr && phkResult != nullptr) {
if (hKey == HKEY_LOCAL_MACHINE &&
@@ -210,6 +234,9 @@ namespace games::iidx {
} else if (data.find(" W:touch: missing trigger:") != std::string::npos) {
out.clear();
return true;
} else if (data.find("SuperstepSound: Audio device is not available") != std::string::npos) {
launcher::signal::SUPERSTEP_SOUND_ERROR = TRUE;
return false;
} else {
return false;
}
@@ -350,7 +377,11 @@ namespace games::iidx {
}
}
apply_audio_hacks();
if (hooks::audio::ENABLED) {
apply_audio_hacks();
} else {
log_warning("iidx", "skipping audio hooks; disabled by user");
}
// ASIO device hook
RegCloseKey_orig = detour::iat_try(
@@ -740,26 +771,30 @@ namespace games::iidx {
0);
// attempt to detect ASIO support
// <=24 : 32-bit only
// 25-26: has neither (no patch needed - WASAPI Exclusive by default)
// 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)
if (!has_SOUND_OUTPUT_DEVICE && !has_XONAR_SOUND_CARD) {
// iidx 25-26
log_info("iidx", "This game only uses WASAPI audio engine");
return;
}
if (has_SOUND_OUTPUT_DEVICE && has_XONAR_SOUND_CARD) {
// iidx 27-30
log_info("iidx", "This game accepts SOUND_OUTPUT_DEVICE environment variable");
return;
}
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(
avs::game::DLL_INSTANCE,
"FF5008E8????ECFF83780803740D",
"FF5008E8??????FF83780803740D",
"??????BB00000000EB169090????",
0, 0);
@@ -774,6 +809,41 @@ namespace games::iidx {
"Successfully forced WASAPI as audio engine using signature matching @ 0x{:x}.",
result);
}
} else {
log_info("iidx", "Not applying force wasapi patch; game will use ASIO");
}
// patch iidx32+ for asio compatibility
// only do this if NOT wasapi (as opposed to checking if it's asio)
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
if (avs::game::is_ext(2024090100, MAXINT) &&
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() && SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
// in iidx32 final:
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
// 48 8b 01 mov rax,QWORD PTR [rcx]
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
intptr_t result = replace_pattern(
avs::game::DLL_INSTANCE,
"FF50????????????????FF50??4533C94533C0418D51",
"????????????????????909090??????????????????",
0, 0);
if (result == 0) {
log_warning(
"iidx",
"Failed to apply ASIO compatibility fix for iidx32+. "
"Unless patches are applied, your ASIO device ({}) may hang or fail to work",
ASIO_DRIVER->c_str());
} else {
log_info(
"iidx",
"Successfully applied ASIO compatibility fix for iidx32+ using signature matching @ 0x{:x}.",
result);
}
}
#endif
+8
View File
@@ -2,9 +2,13 @@
#include <mutex>
#include <optional>
#include <vector>
#include "games/game.h"
#include "external/robin_hood.h"
#include "util/tapeled.h"
namespace games::iidx {
// settings
@@ -30,6 +34,10 @@ namespace games::iidx {
extern bool IIDXIO_LED_TICKER_READONLY;
extern std::mutex IIDX_LED_TICKER_LOCK;
constexpr int IIDX_TAPELED_TOTAL = 17;
// data mapping
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
class IIDXGame : public games::Game {
public:
IIDXGame();
+38 -15
View File
@@ -85,12 +85,19 @@ namespace games::iidx {
&m_pwszSymbolicLink,
&m_cchSymbolicLink
);
if (FAILED(hr)) { goto done; }
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] GetAllocatedString failed with {:#x}", m_name, (ULONG)hr);
goto done;
}
log_misc("iidx:camhook", "[{}] Symlink: {}", m_name, GetSymLink());
// Create the media source object.
hr = pActivate->ActivateObject(IID_PPV_ARGS(&m_pSource));
if (FAILED(hr)) { goto done; }
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] ActivateObject failed with {:#x}", m_name, (ULONG)hr);
goto done;
}
// Retain reference to the camera
m_pSource->AddRef();
@@ -98,13 +105,22 @@ namespace games::iidx {
// Create an attribute store to hold initialization settings.
hr = WrappedMFCreateAttributes(&pAttributes, 2);
if (FAILED(hr)) { goto done; }
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] MFCreateAttributes failed with {:#x}", m_name, (ULONG)hr);
goto done;
}
hr = pAttributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, pD3DManager);
if (FAILED(hr)) { goto done; }
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] SetUnknown(MF_SOURCE_READER_D3D_MANAGER) failed with {:#x}", m_name, (ULONG)hr);
goto done;
}
hr = pAttributes->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE);
if (FAILED(hr)) { goto done; }
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] SetUINT32(MF_SOURCE_READER_DISABLE_DXVA) failed with {:#x}", m_name, (ULONG)hr);
goto done;
}
// TODO: Color space conversion
// if (SUCCEEDED(hr)) {
@@ -120,11 +136,18 @@ namespace games::iidx {
pAttributes,
&m_pSourceReader
);
if (FAILED(hr)) { goto done; }
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] MFCreateSourceReaderFromMediaSource failed with {:#x}", m_name, (ULONG)hr);
goto done;
}
log_misc("iidx:camhook", "[{}] Created source reader", m_name);
hr = InitTargetTexture();
if (FAILED(hr)) { goto done; }
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] InitTargetTexture failed with {:#x}", m_name, (ULONG)hr);
goto done;
}
// Camera should be still usable even if camera control is not supported
InitCameraControl();
@@ -134,7 +157,7 @@ namespace games::iidx {
m_initialized = true;
log_misc("iidx:camhook", "[{}] Initialized", m_name);
} else {
log_warning("iidx:camhook", "[{}] Failed to create camera: {}", m_name, hr);
log_warning("iidx:camhook", "[{}] Failed to create camera: {:#x}", m_name, (ULONG)hr);
}
SafeRelease(&pAttributes);
LeaveCriticalSection(&m_critsec);
@@ -165,7 +188,7 @@ namespace games::iidx {
if (FAILED(hr)) {
if (hr != MF_E_NO_MORE_TYPES) {
log_warning("iidx:camhook", "[{}] Cannot get media type {} {}", m_name, i, hr);
log_warning("iidx:camhook", "[{}] Cannot get media type {} {:#x}", m_name, i, (ULONG)hr);
}
break;
}
@@ -584,7 +607,7 @@ namespace games::iidx {
* Return values:
* S_OK: this is a "better" media type than the existing one
* S_FALSE: valid media type, but not "better"
* E_*: invalid meia type
* E_*: invalid media type
*/
HRESULT IIDXLocalCamera::TryMediaType(IMFMediaType *pType, UINT32 *pBestWidth, double *pBestFrameRate) {
HRESULT hr = S_OK;
@@ -595,13 +618,13 @@ namespace games::iidx {
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] Failed to get subtype: {}", m_name, hr);
log_warning("iidx:camhook", "[{}] Failed to get subtype: {:#x}", m_name, (ULONG)hr);
return hr;
}
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] Failed to get frame size: {}", m_name, hr);
log_warning("iidx:camhook", "[{}] Failed to get frame size: {:#x}", m_name, (ULONG)hr);
return hr;
}
@@ -619,7 +642,7 @@ namespace games::iidx {
(UINT32*)&frameRate.Denominator
);
if (FAILED(hr)) {
log_warning("iidx:camhook", "[{}] Failed to get frame rate: {}", m_name, hr);
log_warning("iidx:camhook", "[{}] Failed to get frame rate: {:#x}", m_name, (ULONG)hr);
return hr;
}
double frameRateValue = frameRate.Numerator / frameRate.Denominator;
@@ -696,7 +719,7 @@ namespace games::iidx {
if (SUCCEEDED(hr)) {
log_misc("iidx:camhook", "[{}] Created texture", m_name);
} else {
log_warning("iidx:camhook", "[{}] Failed to create texture: {}", m_name, hr);
log_warning("iidx:camhook", "[{}] Failed to create texture: {:#x}", m_name, (ULONG)hr);
}
return hr;
}
@@ -785,7 +808,7 @@ namespace games::iidx {
FlushDrawCommands();
if (FAILED(hr)) {
log_warning("iidx:camhook", "Error in DrawSample {}", hr);
log_warning("iidx:camhook", "Error in DrawSample {:#x}", (ULONG)hr);
}
SafeRelease(&pCameraSurf);
LeaveCriticalSection(&m_critsec);
+2 -2
View File
@@ -266,8 +266,8 @@ namespace games::iidx::poke {
y *= rawinput::TOUCHSCREEN_RANGE_Y;
} else if (GRAPHICS_IIDX_WSUB) {
// Scale to windowed subscreen
x *= GRAPHICS_IIDX_WSUB_WIDTH;
y *= GRAPHICS_IIDX_WSUB_HEIGHT;
x *= GRAPHICS_WSUB_WIDTH;
y *= GRAPHICS_WSUB_HEIGHT;
} else if (GENERIC_SUB_WINDOW_FULLSIZE || !overlay::OVERLAY->get_active()) {
// Overlay is not present, scale to main screen
if (GRAPHICS_WINDOWED) {
+10 -12
View File
@@ -36,6 +36,7 @@
#include "ccj/io.h"
#include "qks/io.h"
#include "mfg/io.h"
#include "pc/io.h"
namespace games {
@@ -313,6 +314,15 @@ namespace games {
buttons.insert({ mfg, mfg::get_buttons() });
buttons_help.insert({ mfg, mfg::get_buttons_help() });
file_hints[mfg].emplace_back("game/MFGClient_Data");
// Polaris Chord
const std::string pc("Polaris Chord");
games.push_back(pc);
buttons.insert({ pc, pc::get_buttons() });
buttons_help.insert({ pc, pc::get_buttons_help() });
analogs.insert({ pc, pc::get_analogs() });
lights.insert({ pc, pc::get_lights() });
file_hints[pc].emplace_back("game/svm_Data");
}
const std::vector<std::string> &get_games() {
@@ -461,18 +471,6 @@ namespace games {
vkey_defaults.push_back(0xFF);
names.emplace_back("Force Exit Game");
vkey_defaults.push_back(0xFF);
names.emplace_back("Navigator Activate");
vkey_defaults.push_back(0xFF);
names.emplace_back("Navigator Cancel");
vkey_defaults.push_back(0xFF);
names.emplace_back("Navigator Up");
vkey_defaults.push_back(0xFF);
names.emplace_back("Navigator Down");
vkey_defaults.push_back(0xFF);
names.emplace_back("Navigator Left");
vkey_defaults.push_back(0xFF);
names.emplace_back("Navigator Right");
vkey_defaults.push_back(0xFF);
names.emplace_back("Hotkey Enable 1");
vkey_defaults.push_back(0xFF);
names.emplace_back("Hotkey Enable 2");
-6
View File
@@ -30,12 +30,6 @@ namespace games {
ScreenResizeScene3,
ScreenResizeScene4,
SuperExit,
NavigatorActivate,
NavigatorCancel,
NavigatorUp,
NavigatorDown,
NavigatorLeft,
NavigatorRight,
HotkeyEnable1,
HotkeyEnable2,
HotkeyToggle,
+34 -9
View File
@@ -13,10 +13,14 @@
namespace games::loveplus {
bool CAMERA_ENABLE = false;
// touch stuff
static bool TOUCH_ENABLE = false;
static bool TOUCH_ATTACHED = false;
static std::string lp_args = "-noWatchDog -noIOError -noIrda -notarget";
void touch_update() {
// check if touch enabled
@@ -28,16 +32,15 @@ namespace games::loveplus {
if (!TOUCH_ATTACHED) {
// Find the game window.
// We check the foreground window first, then fall back to searching for the window title
// All game versions seem to have their model first in the window title
HWND wnd = GetForegroundWindow();
if (!string_begins_with(GetActiveWindowTitle(), "LovePlus")) {
wnd = FindWindowBeginsWith(avs::game::MODEL);
}
HWND wnd = FindProcessWindowBeginsWith("LovePlusAC");
// attach touch hook
if (wnd) {
log_info("loveplus", "using window handle for touch: {}", fmt::ptr(wnd));
log_info(
"loveplus",
"using window handle for touch: {} ({})",
fmt::ptr(wnd),
get_window_title(wnd));
touch_create_wnd(wnd);
} else {
log_info("loveplus", "falling back to the DirectX window handle for touch");
@@ -117,12 +120,26 @@ namespace games::loveplus {
}
static LPSTR __stdcall GetCommandLineA_hook() {
static std::string lp_args = "-win -noCamera -noWatchDog -noIOError -noIrda -notarget";
return lp_args.data();
}
static bool lp_spam_remover(void *user, const std::string &data, logger::Style style, std::string &out) {
if (data.empty() || data[0] != '[') {
return false;
}
if (data.find("> [INFO] err_code: 0, err_title: , err_str:") != std::string::npos) {
out = "";
return true;
}
return false;
}
LovePlusGame::LovePlusGame() : Game("LovePlus") {
logger::hook_add(lp_spam_remover, nullptr);
}
LovePlusGame::~LovePlusGame() {
logger::hook_remove(lp_spam_remover, this);
}
void LovePlusGame::attach() {
@@ -157,9 +174,17 @@ namespace games::loveplus {
HMODULE seteq = libutils::try_library("ad_hd_seteq_dll.dll");
detour::inline_hook((void *) SetEqualizer, libutils::try_proc(seteq, "SetEqualizer"));
// set up command line args
if (!CAMERA_ENABLE) {
lp_args += " -noCamera";
}
if (GRAPHICS_WINDOWED) {
lp_args += " -win";
}
// get command line hook
HMODULE lpac = libutils::try_library("lpac.dll");
detour::iat("GetCommandLineA", GetCommandLineA_hook, lpac);
}
void LovePlusGame::detach() {
+3
View File
@@ -4,12 +4,15 @@
namespace games::loveplus {
extern bool CAMERA_ENABLE;
// touch stuff
void touch_update();
class LovePlusGame : public games::Game {
public:
LovePlusGame();
virtual ~LovePlusGame() override;
virtual void attach() override;
virtual void detach() override;
};
+1 -1
View File
@@ -28,7 +28,7 @@ namespace games::mfg {
SetEnvironmentVariableA("VFG_CABINET_TYPE", MFG_CABINET_TYPE.c_str());
// add card reader
portName = MFG_CABINET_TYPE == "UKS" ? L"\\\\.\\COM1" : L"\\\\.\\COM3";
portName = (MFG_CABINET_TYPE == "UKS") ? std::wstring(L"\\\\.\\COM1") : std::wstring(L"\\\\.\\COM3");
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
devicehook_init_trampoline();
devicehook_add(acioHandle);
+2
View File
@@ -71,6 +71,8 @@ std::vector<Light> &games::museca::get_lights() {
GameAPI::Lights::sortLights(
&lights,
"Start",
"Keypad",
"Title R",
"Title G",
"Title B",
+2
View File
@@ -46,6 +46,8 @@ namespace games::museca {
// all lights in correct order
namespace Lights {
enum {
Start,
Keypad,
TitleR,
TitleG,
TitleB,
+1 -1
View File
@@ -94,7 +94,7 @@ namespace games::otoca {
bool kb_insert_press = false;
// eamio keypress
kb_insert_press |= eamuse_get_keypad_state(0) & (1 << EAM_IO_INSERT);
kb_insert_press |= static_cast<bool>(eamuse_get_keypad_state(0) & (1 << EAM_IO_INSERT));
// check for card
if (CARD_DATA == nullptr && (eamuse_card_insert_consume(1, 0) || kb_insert_press)) {
+739
View File
@@ -0,0 +1,739 @@
#include "bi2x_hook.h"
#include "util/execexe.h"
#include "util/logging.h"
#include "rawinput/rawinput.h"
#include "misc/eamuse.h"
#include "games/io.h"
#include "io.h"
#include "util/tapeled.h"
#include "pc.h"
#include "util/utils.h"
namespace games::pc {
bool PC_KNOB_MODE = false;
constexpr float RELATIVE_DECAY = 0.02f;
struct FADER_RELATIVE {
// ranges from 0.f to 1.f
float previous_raw = 0.5f;
// ranges from -1.f to +1.f
float effective_value = 0.f;
};
FADER_RELATIVE fader_l_relative;
FADER_RELATIVE fader_r_relative;
float calculate_analog_raw_delta(float old_value, float new_value) {
// assumed that values are between 0.f and 1.f
float delta = 0.f;
if ((old_value < 0.25f) && (0.75f < new_value)) {
// wrapped around: going left
delta = -((1.f - new_value) + old_value);
} else if ((new_value < 0.25f) && (0.75f < old_value)) {
// wrapped around: going right
delta = (1.f - old_value) + new_value;
} else {
delta = new_value - old_value;
}
return delta;
}
float apply_decay(float val) {
// return to center over time
if (RELATIVE_DECAY < val) {
return val - RELATIVE_DECAY;
} else if (val < -RELATIVE_DECAY) {
return val + RELATIVE_DECAY;
} else {
return 0.f;
}
}
float get_fader_knob_mode_value(float raw_analog, FADER_RELATIVE &fader) {
// add new value...
const float delta = calculate_analog_raw_delta(fader.previous_raw, raw_analog);
fader.effective_value += (delta * 20.f);
fader.previous_raw = raw_analog;
// capture the result after adding new input value
fader.effective_value = CLAMP(fader.effective_value, -1.f, 1.f);
const float result = fader.effective_value;
// post-process for next iteration: apply decay (return to center over time)
fader.effective_value = apply_decay(fader.effective_value);
return result;
}
/*
* class definitions
*/
struct AIO_SCI_COMM {
};
struct AIO_NMGR_IOB2 {
};
struct AIO_IOB2_BI2X_AC1 {
};
struct AIO_IOB2_BI2X_AC1__SETTING {
};
struct AIO_IOB2_BI2X_WRFIRM {
};
struct AIO_IOB2_BI2X_AC1__INPUT
{
uint8_t DevIoCounter;
uint8_t bExIoAErr;
uint8_t bExIoBErr;
uint8_t bPcPowerOn;
uint8_t bPcPowerCheck;
uint8_t bCoin1Jam;
uint8_t bCoin2Jam;
uint8_t bCoin3Jam;
uint8_t bCoin4Jam;
uint8_t Coin1Count;
uint8_t Coin2Count;
uint8_t Coin3Count;
uint8_t Coin4Count;
uint16_t AnalogCh1;
uint16_t AnalogCh2;
uint16_t AnalogCh3;
uint16_t AnalogCh4;
uint8_t CN8_8;
uint8_t CN8_9;
uint8_t CN8_10;
uint8_t CN9_8;
uint8_t CN9_9;
uint8_t CN9_10;
uint8_t CN11_11;
uint8_t CN11_12;
uint8_t CN11_13;
uint8_t CN11_14;
uint8_t CN11_15;
uint8_t CN11_16;
uint8_t CN11_17;
uint8_t CN11_18;
uint8_t CN11_19;
uint8_t CN11_20;
uint8_t CN12_11;
uint8_t CN12_12;
uint8_t CN12_13;
uint8_t CN12_14;
uint8_t CN12_15;
uint8_t CN12_16;
uint8_t CN12_17;
uint8_t CN12_18;
uint8_t CN12_19;
uint8_t CN12_20;
uint8_t CN12_21;
uint8_t CN12_22;
uint8_t CN12_23;
uint8_t CN12_24;
uint8_t CN15_3;
uint8_t CN15_4;
uint8_t CN15_5;
uint8_t CN15_6;
uint8_t CN15_7;
uint8_t CN15_8;
uint8_t CN15_9;
uint8_t CN15_10;
uint8_t CN15_11;
uint8_t CN15_12;
uint8_t CN15_13;
uint8_t CN15_14;
uint8_t CN15_15;
uint8_t CN15_16;
uint8_t CN15_17;
uint8_t CN15_18;
uint8_t CN15_19;
uint8_t CN15_20;
uint8_t CN19_8;
uint8_t CN19_9;
uint8_t CN19_10;
uint8_t CN19_11;
uint8_t CN19_12;
uint8_t CN19_13;
uint8_t CN19_14;
uint8_t CN19_15;
};
struct AIO_IOB2_BI2X_AC1__INPUTDATA {
uint8_t data[247];
};
struct AIO_IOB2_BI2X_AC1__OUTPUTDATA {
uint8_t data[48];
};
struct AIO_IOB2_BI2X_AC1__ICNPIN {
uint16_t Ain[4];
uint64_t CnPin;
};
struct AIO_IOB2_BI2X_AC1__DEVSTATUS {
uint8_t InputCounter;
uint8_t OutputCounter;
uint8_t IoResetCounter;
uint8_t TapeLedCounter;
uint8_t TapeLedRate[8];
AIO_IOB2_BI2X_AC1__INPUT Input;
AIO_IOB2_BI2X_AC1__INPUTDATA InputData;
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20];
};
/*
* typedefs
*/
// libaio-iob2_video.dll
typedef AIO_IOB2_BI2X_AC1* (__fastcall *aioIob2Bi2xAC1_Create_t)(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId,
AIO_IOB2_BI2X_AC1__SETTING *i_Setting);
typedef void (__fastcall *aioIob2Bi2xAC1_GetDeviceStatus_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl,
AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus);
typedef void (__fastcall *aioIob2Bi2xAC1_SetWatchDogTimer_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count);
typedef void (__fastcall *aioIob2Bi2xAC1_AddCounter_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter,
uint32_t i_Count);
typedef void (__fastcall *aioIob2Bi2xAC1_SetOutputData_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin,
uint8_t i_Data);
typedef void (__fastcall *aioIob2Bi2xAC1_SetTapeLedDataPart_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
uint32_t i_Offset, uint8_t *i_pData,
uint32_t i_cntTapeLed, bool i_bReverse);
typedef void (__fastcall *aioIob2Bi2x_SetTapeLedDataGroup_t)(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_bfGroup);
typedef void (__fastcall *aioIob2Bi2x_SetTapeLedDataLimit_t)(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_Channel,
uint8_t i_Scale, uint8_t i_Limit);
typedef AIO_IOB2_BI2X_WRFIRM *(__fastcall *aioIob2Bi2x_CreateWriteFirmContext_t)(uint32_t i_SerialNumber,
uint32_t i_bfIob);
typedef AIO_SCI_COMM* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint32_t i_SerialNumber);
typedef void (__fastcall *aioIob2Bi2x_DestroyWriteFirmContext_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm);
typedef int32_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm);
typedef bool (__fastcall *aioIob2Bi2x_WriteFirmIsCompleted_t)(int32_t i_State);
typedef bool (__fastcall *aioIob2Bi2x_WriteFirmIsError_t)(int32_t i_State);
// libaio-iob.dll
typedef AIO_NMGR_IOB2 *(__fastcall *aioNMgrIob2_Create_t)(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode);
typedef void (__fastcall *aioNMgrIob_BeginManage_t)(AIO_NMGR_IOB2 *i_pNodeMgr);
// libaio.dll
typedef void (__fastcall *aioSci_Destroy_t)(AIO_SCI_COMM *i_pNodeMgr);
typedef void (__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB2 *i_pNodeMgr);
typedef int32_t (__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB2 *i_pNodeMgr);
typedef bool (__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State);
typedef bool (__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State);
typedef void (__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl);
typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl);
typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State);
typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State);
/*
* function pointers
*/
// libaio-iob2_video.dll
static aioIob2Bi2xAC1_Create_t aioIob2Bi2xAC1_Create_orig = nullptr;
static aioIob2Bi2xAC1_GetDeviceStatus_t aioIob2Bi2xAC1_GetDeviceStatus_orig = nullptr;
static aioIob2Bi2xAC1_SetWatchDogTimer_t aioIob2Bi2xAC1_SetWatchDogTimer_orig = nullptr;
static aioIob2Bi2xAC1_AddCounter_t aioIob2Bi2xAC1_AddCounter_orig = nullptr;
static aioIob2Bi2xAC1_SetOutputData_t aioIob2Bi2xAC1_SetOutputData_orig = nullptr;
static aioIob2Bi2xAC1_SetTapeLedDataPart_t aioIob2Bi2xAC1_SetTapeLedDataPart_orig = nullptr;
static aioIob2Bi2x_SetTapeLedDataGroup_t aioIob2Bi2x_SetTapeLedDataGroup_orig = nullptr;
static aioIob2Bi2x_SetTapeLedDataLimit_t aioIob2Bi2x_SetTapeLedDataLimit_orig = nullptr;
static aioIob2Bi2x_OpenSciUsbCdc_t aioIob2Bi2x_OpenSciUsbCdc_orig = nullptr;
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
static aioIob2Bi2x_DestroyWriteFirmContext_t aioIob2Bi2x_DestroyWriteFirmContext_orig = nullptr;
static aioIob2Bi2x_WriteFirmGetState_t aioIob2Bi2x_WriteFirmGetState_orig = nullptr;
static aioIob2Bi2x_WriteFirmIsCompleted_t aioIob2Bi2x_WriteFirmIsCompleted_orig = nullptr;
static aioIob2Bi2x_WriteFirmIsError_t aioIob2Bi2x_WriteFirmIsError_orig = nullptr;
// libaio-iob.dll
static aioNMgrIob2_Create_t aioNMgrIob2_Create_orig = nullptr;
static aioNMgrIob_BeginManage_t aioNMgrIob_BeginManage_orig = nullptr;
// libaio.dll
static aioSci_Destroy_t aioSci_Destroy_orig = nullptr;
static aioNodeMgr_Destroy_t aioNodeMgr_Destroy_orig = nullptr;
static aioNodeMgr_GetState_t aioNodeMgr_GetState_orig = nullptr;
static aioNodeMgr_IsReady_t aioNodeMgr_IsReady_orig = nullptr;
static aioNodeMgr_IsError_t aioNodeMgr_IsError_orig = nullptr;
static aioNodeCtl_Destroy_t aioNodeCtl_Destroy_orig = nullptr;
static aioNodeCtl_GetState_t aioNodeCtl_GetState_orig = nullptr;
static aioNodeCtl_IsReady_t aioNodeCtl_IsReady_orig = nullptr;
static aioNodeCtl_IsError_t aioNodeCtl_IsError_orig = nullptr;
/*
* variables
*/
static AIO_IOB2_BI2X_AC1 *aioIob2Bi2xAc1;
static AIO_SCI_COMM *aioSciComm;
static AIO_NMGR_IOB2 *aioNmgrIob2;
static AIO_IOB2_BI2X_WRFIRM *aioIob2Bi2xWrfirm;
/*
* implementations
*/
// libaio-iob2_video.dll
static AIO_IOB2_BI2X_AC1* __fastcall aioIob2Bi2xAC1_Create(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId,
AIO_IOB2_BI2X_AC1__SETTING *i_Setting) {
if (i_pNodeMgr == aioNmgrIob2) {
log_info("bi2x_hook", "aioIob2Bi2xAC1_Create hook hit");
aioIob2Bi2xAc1 = new AIO_IOB2_BI2X_AC1;
return aioIob2Bi2xAc1;
} else {
return aioIob2Bi2xAC1_Create_orig(i_pNodeMgr, i_DevId, i_Setting);
}
}
void __fastcall aioIob2Bi2xAC1_GetDeviceStatus(AIO_IOB2_BI2X_AC1 *i_pNodeCtl,
AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus) {
RI_MGR->devices_flush_output();
if (i_pNodeCtl != aioIob2Bi2xAc1) {
return aioIob2Bi2xAC1_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus);
}
memset(o_DevStatus, 0x00, sizeof(AIO_IOB2_BI2X_AC1__DEVSTATUS));
auto &buttons = get_buttons();
o_DevStatus->Input.CN8_8 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]) ? 0 : 0xFF;
o_DevStatus->Input.CN8_9 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]) ? 0 : 0xFF;
o_DevStatus->Input.CN8_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]) ? 0 : 0xFF;
o_DevStatus->Input.CN15_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]) ? 0 : 0xFF;
o_DevStatus->Input.CN15_12 = 0xFF; // Recorder off; present in I/O test menu but unused in game
o_DevStatus->Input.CN12_11 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button1]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_12 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button2]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_13 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button3]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_14 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button4]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_15 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button5]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_16 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button6]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_17 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button7]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_18 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button8]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_19 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button9]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_20 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button10]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_21 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button11]) ? 0 : 0xFF;
o_DevStatus->Input.CN12_22 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button12]) ? 0 : 0xFF;
auto &analogs = get_analogs();
// FADER-L
float val = 0.f;
if (analogs[Analogs::FaderL].isSet()) {
if (PC_KNOB_MODE) {
val = get_fader_knob_mode_value(
GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]),
fader_l_relative);
} else {
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2;
}
}
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]) &&
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
val = 0.f;
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left])) {
val = -1.0f;
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
val = +1.0f;
}
o_DevStatus->Input.CN15_7 = (val < 0.2f) ? 0xFF : 0;
o_DevStatus->Input.CN15_6 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
o_DevStatus->Input.CN15_5 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
o_DevStatus->Input.CN15_4 = (val > -0.35f && val < 0.85f) ? 0xFF : 0;
o_DevStatus->Input.CN15_3 = (val > -0.2f) ? 0xFF : 0;
// FADER-R
val = 0.f;
if (analogs[Analogs::FaderR].isSet()) {
if (PC_KNOB_MODE) {
val = get_fader_knob_mode_value(
GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]),
fader_r_relative);
} else {
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2;
}
}
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]) &&
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
val = 0.f;
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left])) {
val = -1.0f;
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
val = +1.0f;
}
o_DevStatus->Input.CN11_20 = (val < 0.2f) ? 0xFF : 0;
o_DevStatus->Input.CN11_19 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
o_DevStatus->Input.CN9_10 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
o_DevStatus->Input.CN9_9 = (val > -0.35f && val < 0.85f) ? 0xFF : 0;
o_DevStatus->Input.CN9_8 = (val > -0.2f) ? 0xFF : 0;
// coin
o_DevStatus->Input.Coin1Count = eamuse_coin_get_stock();
}
void __fastcall aioIob2Bi2xAC1_SetWatchDogTimer(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count) {
if (i_pNodeCtl != aioIob2Bi2xAc1) {
return aioIob2Bi2xAC1_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
}
}
void __fastcall aioIob2Bi2xAC1_AddCounter(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) {
if (i_pNodeCtl == aioIob2Bi2xAc1 && i_Count == 0) {
eamuse_coin_set_stock((uint16_t) i_Count);
} else {
return aioIob2Bi2xAC1_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count);
}
}
void __fastcall aioIob2Bi2xAC1_SetOutputData(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data) {
if (i_pNodeCtl != aioIob2Bi2xAc1) {
return aioIob2Bi2xAC1_SetOutputData_orig(i_pNodeCtl, i_CnPin, i_Data);
}
if (i_CnPin == 0x1) {
eamuse_coin_set_block(i_Data == 0xFF);
return;
}
auto &lights = get_lights();
if (i_CnPin == 0x11) {
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_R), i_Data / 255.0f);
} else if (i_CnPin == 0x12) {
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_G), i_Data / 255.0f);
} else if (i_CnPin == 0x13) {
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_B), i_Data / 255.0f);
}
}
struct PolarisChordLight {
int data_index;
games::pc::Lights::pc_lights_t light;
PolarisChordLight(
int data_index, games::pc::Lights::pc_lights_t light) :
data_index(data_index), light(light) {}
};
static void set_led_value(games::pc::Lights::pc_lights_t light, uint8_t value) {
auto &lights = games::pc::get_lights();
GameAPI::Lights::writeLight(RI_MGR, lights.at(light), value / 255.f);
}
void __fastcall aioIob2Bi2xAC1_SetTapeLedDataPart(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
uint32_t i_Offset, uint8_t *i_pData,
uint32_t i_cntTapeLed, bool i_bReverse) {
if (i_pNodeCtl != aioIob2Bi2xAc1) {
return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse);
}
// log_info(
// "pc",
// "lamp [{}] [{}] = {},{},{}, cnt={}",
// i_TapeLedCh,
// i_Offset,
// i_pData[0], i_pData[1], i_pData[2],
// i_cntTapeLed);
// [channel 0]
// these are button lamps; there are 12 buttons (columns) with this layout:
//
// 0 5 8 11 14 16 | 18 20 22 25 28 31
// 1 6 9 12 15 17 | 19 21 23 26 29 32
// 2 7 10 13 | 24 27 30 33
// 3 | 34
// 4 | 35
//
// 36 RGB lamps * 3 = 108 LEDs
// each value ranges from [0, 255]
// (we only care about the top row)
static PolarisChordLight button_lights_mapping[] = {
{(0 * 3) + 0, games::pc::Lights::pc_lights_t::Button1_R},
{(0 * 3) + 1, games::pc::Lights::pc_lights_t::Button1_G},
{(0 * 3) + 2, games::pc::Lights::pc_lights_t::Button1_B},
{(5 * 3) + 0, games::pc::Lights::pc_lights_t::Button2_R},
{(5 * 3) + 1, games::pc::Lights::pc_lights_t::Button2_G},
{(5 * 3) + 2, games::pc::Lights::pc_lights_t::Button2_B},
{(8 * 3) + 0, games::pc::Lights::pc_lights_t::Button3_R},
{(8 * 3) + 1, games::pc::Lights::pc_lights_t::Button3_G},
{(8 * 3) + 2, games::pc::Lights::pc_lights_t::Button3_B},
{(11 * 3) + 0, games::pc::Lights::pc_lights_t::Button4_R},
{(11 * 3) + 1, games::pc::Lights::pc_lights_t::Button4_G},
{(11 * 3) + 2, games::pc::Lights::pc_lights_t::Button4_B},
{(14 * 3) + 0, games::pc::Lights::pc_lights_t::Button5_R},
{(14 * 3) + 1, games::pc::Lights::pc_lights_t::Button5_G},
{(14 * 3) + 2, games::pc::Lights::pc_lights_t::Button5_B},
{(16 * 3) + 0, games::pc::Lights::pc_lights_t::Button6_R},
{(16 * 3) + 1, games::pc::Lights::pc_lights_t::Button6_G},
{(16 * 3) + 2, games::pc::Lights::pc_lights_t::Button6_B},
{(18 * 3) + 0, games::pc::Lights::pc_lights_t::Button7_R},
{(18 * 3) + 1, games::pc::Lights::pc_lights_t::Button7_G},
{(18 * 3) + 2, games::pc::Lights::pc_lights_t::Button7_B},
{(20 * 3) + 0, games::pc::Lights::pc_lights_t::Button8_R},
{(20 * 3) + 1, games::pc::Lights::pc_lights_t::Button8_G},
{(20 * 3) + 2, games::pc::Lights::pc_lights_t::Button8_B},
{(22 * 3) + 0, games::pc::Lights::pc_lights_t::Button9_R},
{(22 * 3) + 1, games::pc::Lights::pc_lights_t::Button9_G},
{(22 * 3) + 2, games::pc::Lights::pc_lights_t::Button9_B},
{(25 * 3) + 0, games::pc::Lights::pc_lights_t::Button10_R},
{(25 * 3) + 1, games::pc::Lights::pc_lights_t::Button10_G},
{(25 * 3) + 2, games::pc::Lights::pc_lights_t::Button10_B},
{(28 * 3) + 0, games::pc::Lights::pc_lights_t::Button11_R},
{(28 * 3) + 1, games::pc::Lights::pc_lights_t::Button11_G},
{(28 * 3) + 2, games::pc::Lights::pc_lights_t::Button11_B},
{(31 * 3) + 0, games::pc::Lights::pc_lights_t::Button12_R},
{(31 * 3) + 1, games::pc::Lights::pc_lights_t::Button12_G},
{(31 * 3) + 2, games::pc::Lights::pc_lights_t::Button12_B},
};
if (i_TapeLedCh == 0 && i_Offset == 0 && i_cntTapeLed == 108) {
for (const auto &map : button_lights_mapping) {
set_led_value(map.light, i_pData[map.data_index]);
}
}
}
void __fastcall aioIob2Bi2x_SetTapeLedDataGroup(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_bfGroup) {
if (i_pNodeCtl != aioIob2Bi2xAc1) {
return aioIob2Bi2x_SetTapeLedDataGroup_orig(i_pNodeCtl, i_bfGroup);
}
}
void __fastcall aioIob2Bi2x_SetTapeLedDataLimit(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_Channel,
uint8_t i_Scale, uint8_t i_Limit) {
if (i_pNodeCtl != aioIob2Bi2xAc1) {
return aioIob2Bi2x_SetTapeLedDataLimit_orig(i_pNodeCtl, i_Channel, i_Scale, i_Limit);
}
}
static AIO_SCI_COMM *__fastcall aioIob2Bi2x_OpenSciUsbCdc(uint32_t i_SerialNumber) {
aioSciComm = new AIO_SCI_COMM;
return aioSciComm;
}
static AIO_IOB2_BI2X_WRFIRM *__fastcall aioIob2Bi2x_CreateWriteFirmContext(
uint32_t i_SerialNumber, uint32_t i_bfIob) {
aioIob2Bi2xWrfirm = new AIO_IOB2_BI2X_WRFIRM;
return aioIob2Bi2xWrfirm;
}
static void __fastcall aioIob2Bi2x_DestroyWriteFirmContext(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) {
if (i_pWrFirm == aioIob2Bi2xWrfirm) {
delete aioIob2Bi2xWrfirm;
aioIob2Bi2xWrfirm = nullptr;
} else {
return aioIob2Bi2x_DestroyWriteFirmContext_orig(i_pWrFirm);
}
}
static int32_t __fastcall aioIob2Bi2x_WriteFirmGetState(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) {
if (i_pWrFirm == aioIob2Bi2xWrfirm) {
return 8;
} else {
return aioIob2Bi2x_WriteFirmGetState_orig(i_pWrFirm);
}
}
static bool __fastcall aioIob2Bi2x_WriteFirmIsCompleted(int32_t i_State) {
if (aioIob2Bi2xWrfirm != nullptr) {
return true;
} else {
return aioIob2Bi2x_WriteFirmIsCompleted_orig(i_State);
}
}
static bool __fastcall aioIob2Bi2x_WriteFirmIsError(int32_t i_State) {
if (aioIob2Bi2xWrfirm != nullptr) {
return false;
} else {
return aioIob2Bi2x_WriteFirmIsError_orig(i_State);
}
}
// libaio-iob.dll
static AIO_NMGR_IOB2 *__fastcall aioNMgrIob2_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
if (i_pSci == aioSciComm) {
aioNmgrIob2 = new AIO_NMGR_IOB2;
return aioNmgrIob2;
} else {
return aioNMgrIob2_Create_orig(i_pSci, i_bfMode);
}
}
static void __fastcall aioNMgrIob_BeginManage(AIO_NMGR_IOB2 *i_pNodeMgr) {
if (i_pNodeMgr != aioNmgrIob2) {
return aioNMgrIob_BeginManage_orig(i_pNodeMgr);
}
}
// libaio.dll
static void __fastcall aioSci_Destroy(AIO_SCI_COMM *i_pNodeMgr) {
if (i_pNodeMgr == aioSciComm) {
delete aioSciComm;
} else {
return aioSci_Destroy_orig(i_pNodeMgr);
}
}
static void __fastcall aioNodeMgr_Destroy(AIO_NMGR_IOB2 *i_pNodeMgr) {
if (i_pNodeMgr == aioNmgrIob2) {
delete aioNmgrIob2;
aioNmgrIob2 = nullptr;
} else {
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
}
}
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB2 *i_pNodeMgr) {
if (i_pNodeMgr == aioNmgrIob2) {
return 1;
} else {
return aioNodeMgr_GetState_orig(i_pNodeMgr);
}
}
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State) {
if (i_pNodeMgr == aioNmgrIob2) {
return true;
} else {
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
}
}
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State) {
if (i_pNodeMgr == aioNmgrIob2) {
return false;
} else {
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
}
}
static void __fastcall aioNodeCtl_Destroy(AIO_IOB2_BI2X_AC1 *i_pNodeCtl) {
if (i_pNodeCtl == aioIob2Bi2xAc1) {
delete aioIob2Bi2xAc1;
aioIob2Bi2xAc1 = nullptr;
} else {
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
}
}
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB2_BI2X_AC1 *i_pNodeCtl) {
if (i_pNodeCtl == aioIob2Bi2xAc1) {
return 1;
} else {
return aioNodeCtl_GetState_orig(i_pNodeCtl);
}
}
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State) {
if (i_pNodeCtl == aioIob2Bi2xAc1) {
return true;
} else {
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
}
}
static bool __fastcall aioNodeCtl_IsError(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State) {
if (i_pNodeCtl == aioIob2Bi2xAc1) {
return false;
} else {
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
}
}
void bi2x_hook_init() {
// avoid double init
static bool initialized = false;
if (initialized) {
return;
} else {
initialized = true;
}
// announce
log_info("bi2x_hook", "init");
// libaio-iob2_video.dll
const auto libaioIob2VideoDll = "libaio-iob2_video.dll";
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_Create",
aioIob2Bi2xAC1_Create, &aioIob2Bi2xAC1_Create_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_GetDeviceStatus",
aioIob2Bi2xAC1_GetDeviceStatus, &aioIob2Bi2xAC1_GetDeviceStatus_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetWatchDogTimer",
aioIob2Bi2xAC1_SetWatchDogTimer, &aioIob2Bi2xAC1_SetWatchDogTimer_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_AddCounter",
aioIob2Bi2xAC1_AddCounter, &aioIob2Bi2xAC1_AddCounter_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetOutputData",
aioIob2Bi2xAC1_SetOutputData, &aioIob2Bi2xAC1_SetOutputData_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetTapeLedDataPart",
aioIob2Bi2xAC1_SetTapeLedDataPart, &aioIob2Bi2xAC1_SetTapeLedDataPart_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataGroup",
aioIob2Bi2x_SetTapeLedDataGroup, &aioIob2Bi2x_SetTapeLedDataGroup_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataLimit",
aioIob2Bi2x_SetTapeLedDataLimit, &aioIob2Bi2x_SetTapeLedDataLimit_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_OpenSciUsbCdc",
aioIob2Bi2x_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_CreateWriteFirmContext",
aioIob2Bi2x_CreateWriteFirmContext, &aioIob2Bi2x_CreateWriteFirmContext_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_DestroyWriteFirmContext",
aioIob2Bi2x_DestroyWriteFirmContext, &aioIob2Bi2x_DestroyWriteFirmContext_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmGetState",
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsCompleted",
aioIob2Bi2x_WriteFirmIsCompleted, &aioIob2Bi2x_WriteFirmIsCompleted_orig);
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsError",
aioIob2Bi2x_WriteFirmIsError, &aioIob2Bi2x_WriteFirmIsError_orig);
// libaio-iob.dll
const auto libaioIobDll = "libaio-iob.dll";
execexe::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
execexe::trampoline_try(libaioIobDll, "aioNMgrIob_BeginManage",
aioNMgrIob_BeginManage, &aioNMgrIob_BeginManage_orig);
// libaio.dll
const auto libaioDll = "libaio.dll";
execexe::trampoline_try(libaioDll, "aioSci_Destroy",
aioSci_Destroy, &aioSci_Destroy_orig);
execexe::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
execexe::trampoline_try(libaioDll, "aioNodeMgr_GetState",
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsError",
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
execexe::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
execexe::trampoline_try(libaioDll, "aioNodeCtl_GetState",
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsError",
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
}
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
namespace games::pc {
void bi2x_hook_init();
}
+127
View File
@@ -0,0 +1,127 @@
#include "io.h"
std::vector<Button> &games::pc::get_buttons() {
static std::vector<Button> buttons;
if (buttons.empty()) {
buttons = GameAPI::Buttons::getButtons("Polaris Chord");
GameAPI::Buttons::sortButtons(
&buttons,
"Service",
"Test",
"Coin Mech",
"Button 1",
"Button 2",
"Button 3",
"Button 4",
"Button 5",
"Button 6",
"Button 7",
"Button 8",
"Button 9",
"Button 10",
"Button 11",
"Button 12",
"Fader-L Left",
"Fader-L Right",
"Fader-R Left",
"Fader-R Right",
"Headphone"
);
}
return buttons;
}
std::string games::pc::get_buttons_help() {
// keep to max 100 characters wide
return
" FADER-L FADER-R \n"
"\n"
" B1 B2 B3 ... B11 B12\n"
"\n"
" Most menu interactions are on the touch screen; you can use your mouse.\n"
;
}
std::vector<Analog> &games::pc::get_analogs() {
static std::vector<Analog> analogs;
if (analogs.empty()) {
analogs = GameAPI::Analogs::getAnalogs("Polaris Chord");
GameAPI::Analogs::sortAnalogs(
&analogs,
"Fader-L",
"Fader-R"
);
}
return analogs;
}
std::vector<Light> &games::pc::get_lights() {
static std::vector<Light> lights;
if (lights.empty()) {
lights = GameAPI::Lights::getLights("Polaris Chord");
GameAPI::Lights::sortLights(
&lights,
"IC Reader R",
"IC Reader G",
"IC Reader B",
"Button 1 R",
"Button 1 G",
"Button 1 B",
"Button 2 R",
"Button 2 G",
"Button 2 B",
"Button 3 R",
"Button 3 G",
"Button 3 B",
"Button 4 R",
"Button 4 G",
"Button 4 B",
"Button 5 R",
"Button 5 G",
"Button 5 B",
"Button 6 R",
"Button 6 G",
"Button 6 B",
"Button 7 R",
"Button 7 G",
"Button 7 B",
"Button 8 R",
"Button 8 G",
"Button 8 B",
"Button 9 R",
"Button 9 G",
"Button 9 B",
"Button 10 R",
"Button 10 G",
"Button 10 B",
"Button 11 R",
"Button 11 G",
"Button 11 B",
"Button 12 R",
"Button 12 G",
"Button 12 B"
);
}
return lights;
}
+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include <vector>
#include "cfg/api.h"
namespace games::pc {
namespace Buttons {
enum {
Service,
Test,
CoinMech,
Button1,
Button2,
Button3,
Button4,
Button5,
Button6,
Button7,
Button8,
Button9,
Button10,
Button11,
Button12,
FaderL_Left,
FaderL_Right,
FaderR_Left,
FaderR_Right,
Headphone
};
}
namespace Analogs {
enum {
FaderL,
FaderR,
};
}
namespace Lights {
typedef enum {
IC_READER_R,
IC_READER_G,
IC_READER_B,
Button1_R,
Button1_G,
Button1_B,
Button2_R,
Button2_G,
Button2_B,
Button3_R,
Button3_G,
Button3_B,
Button4_R,
Button4_G,
Button4_B,
Button5_R,
Button5_G,
Button5_B,
Button6_R,
Button6_G,
Button6_B,
Button7_R,
Button7_G,
Button7_B,
Button8_R,
Button8_G,
Button8_B,
Button9_R,
Button9_G,
Button9_B,
Button10_R,
Button10_G,
Button10_B,
Button11_R,
Button11_G,
Button11_B,
Button12_R,
Button12_G,
Button12_B
} pc_lights_t;
}
// getters
std::vector<Button> &get_buttons();
std::string get_buttons_help();
std::vector<Analog> &get_analogs();
std::vector<Light> &get_lights();
}
+110
View File
@@ -0,0 +1,110 @@
#include "pc.h"
#include <format>
#include "bi2x_hook.h"
#include "util/detour.h"
#include "util/logging.h"
#include "util/fileutils.h"
#include "util/unity_player.h"
#include "util/execexe.h"
#include "acioemu/handle.h"
#include "misc/wintouchemu.h"
#include "hooks/graphics/graphics.h"
#include "rawinput/rawinput.h"
namespace games::pc {
std::string PC_INJECT_ARGS = "";
bool PC_NO_IO = false;
static acioemu::ACIOHandle *acioHandle = nullptr;
static std::wstring portName = L"COM1";
static decltype(RegisterRawInputDevices) *RegisterRawInputDevices_orig = nullptr;
static decltype(EnumDisplaySettingsW) *EnumDisplaySettingsW_orig = nullptr;
static decltype(QueryDisplayConfig) *QueryDisplayConfig_orig = nullptr;
static BOOL WINAPI RegisterRawInputDevices_hook(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize) {
// if the caller is spice itself, then pass through.
if (pRawInputDevices &&
(uiNumDevices > 0) &&
(pRawInputDevices[0].hwndTarget == RI_MGR->input_hwnd)) {
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
}
// otherwise, it must be the game; prevent the game from registering for raw input
// and hijacking WM_INPUT messages.
SetLastError(0xDEADBEEF);
return FALSE;
}
static BOOL WINAPI EnumDisplaySettingsW_hook(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode) {
auto result = EnumDisplaySettingsW_orig(lpszDeviceName, iModeNum, lpDevMode);
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Screen-currentResolution.html
// "If the player is running in windowed mode, this returns the current dimensions of the game window in pixels and the display refresh rate."
// Override it to 1920x1080 to avoid broken UI under Windowed mode
if (result && iModeNum == ENUM_CURRENT_SETTINGS) {
// Actually make it returns size of the game window, Can I?
lpDevMode->dmPelsWidth = 1920;
lpDevMode->dmPelsHeight = 1080;
}
return result;
}
LONG WINAPI QueryDisplayConfig_hook(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*, DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*) {
// make unity fallback to EnumDisplaySettingsW as I don't
// want to deal with this api which is way more complex
return ERROR_NOT_SUPPORTED;
}
void PCGame::attach() {
Game::attach();
// add card reader
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
devicehook_init_trampoline();
devicehook_add(acioHandle);
execexe::init();
execexe::init_deferred([] () {
execexe::init_port_hook(portName, acioHandle);
execexe::load_library("libaio.dll");
execexe::load_library("libaio-iob.dll");
execexe::load_library("libaio-iob_video.dll");
execexe::load_library("libaio-iob2_video.dll");
execexe::load_library("win10actlog.dll");
if (!PC_NO_IO) {
bi2x_hook_init();
}
});
const auto user32Dll = "user32.dll";
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
QueryDisplayConfig_hook, &QueryDisplayConfig_orig);
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
EnumDisplaySettingsW_hook, &EnumDisplaySettingsW_orig);
if (GRAPHICS_SHOW_CURSOR) {
unity_utils::force_show_cursor(true);
}
unity_utils::set_args(
std::format("{} {}{}",
GetCommandLineA(),
PC_INJECT_ARGS,
unity_utils::get_unity_player_args()));
}
void PCGame::detach() {
Game::detach();
devicehook_dispose();
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "games/game.h"
namespace games::pc {
extern std::string PC_INJECT_ARGS;
extern bool PC_NO_IO;
extern bool PC_KNOB_MODE;
class PCGame : public games::Game {
public:
PCGame() : Game("Polaris Chord") {}
virtual void attach() override;
virtual void detach() override;
};
}
+1 -1
View File
@@ -63,7 +63,7 @@ namespace games::qma {
touch_attach_dx_hook();
// cursor
if (!is_touch_available()) {
if (!is_touch_available("QMATouchDevice::open")) {
ShowCursor(true);
}
}
+1 -1
View File
@@ -86,7 +86,7 @@ bool games::rb::ReflecBeatTouchDeviceHandle::open(LPCWSTR lpFileName) {
}
// show cursor on window if mouse is used
if (!is_touch_available()) {
if (!is_touch_available("ReflecBeatTouchDeviceHandle::open")) {
ShowCursor(TRUE);
}
+1 -1
View File
@@ -72,7 +72,7 @@ public:
touch_attach_dx_hook();
// cursor
if (!is_touch_available()) {
if (!is_touch_available("TwTouchDevice::open")) {
ShowCursor(true);
}
}
+6 -116
View File
@@ -14,134 +14,24 @@
#include <mfidl.h>
#include "avs/game.h"
#include "hooks/cfgmgr32hook.h"
#include "util/detour.h"
#include "util/memutils.h"
#include "util/utils.h"
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
namespace games::sdvx {
static std::wstring CAMERA0_ID;
static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue,
UINT32 *pcchLength) {
// call the original
HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength);
// try first camera
wchar_t *pwc = nullptr;
if (CAMERA0_ID.length() == 23)
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
// check if camera could be identified
if (pwc) {
// fake the USB IDs
pwc[4] = L'2';
pwc[5] = L'8';
pwc[6] = L'8';
pwc[7] = L'c';
pwc[13] = L'0';
pwc[14] = L'0';
pwc[15] = L'0';
pwc[16] = L'2';
pwc[21] = L'0';
pwc[22] = L'0';
}
// return original result
return result;
}
static void hook_camera(IMFActivate* camera, size_t no, std::wstring camera_id, std::string camera_instance) {
// don't hook if camera 0 is already hooked
if (CAMERA0_ID.length() > 0)
return;
// save the camera ID
CAMERA0_ID = camera_id;
// cfgmgr hook
CFGMGR32_HOOK_SETTING camera_setting;
camera_setting.device_instance = 0xDEADBEEF;
camera_setting.parent_instance = ~camera_setting.device_instance;
camera_setting.device_id = "USB\\VEN_1022&DEV_7908";
camera_setting.device_node_id = "USB\\VID_288C&PID_0002&MI_00\\?&????????&?&????";
if (camera_instance.length() == 17) {
for (int i = 0; i < 17; i++) {
camera_setting.device_node_id[28 + i] = camera_instance[i];
}
}
cfgmgr32hook_add(camera_setting);
// save original method for later use
if (GetAllocatedString_orig == nullptr) {
GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString;
}
// hook allocated string method for camera identification
memutils::VProtectGuard camera_guard(camera->lpVtbl);
camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook;
}
static HRESULT WINAPI MFEnumDeviceSources_hook(IMFAttributes *pAttributes, IMFActivate ***pppSourceActivate,
UINT32 *pcSourceActivate) {
// call original function
HRESULT result_orig = MFEnumDeviceSources_orig(pAttributes, pppSourceActivate, pcSourceActivate);
// check for capture devices
if (FAILED(result_orig) || !*pcSourceActivate) {
return result_orig;
}
// iterate cameras
size_t cam_hook_num = 0;
for (size_t cam_num = 0; cam_num < *pcSourceActivate && cam_hook_num < 1; cam_num++) {
// flip
size_t cam_num_flipped = cam_num;
// get camera link
IMFActivate *camera = (*pppSourceActivate)[cam_num_flipped];
LPWSTR camera_link_lpwstr;
UINT32 camera_link_length;
if (SUCCEEDED(camera->lpVtbl->GetAllocatedString(
camera,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
&camera_link_lpwstr,
&camera_link_length))) {
// cut name to make ID
std::wstring camera_link_ws = std::wstring(camera_link_lpwstr);
std::wstring camera_id = camera_link_ws.substr(8, 23);
// get camera instance
std::string camera_link = ws2s(camera_link_ws);
std::string camera_instance = camera_link.substr(32, 17);
// hook the camera
hook_camera(camera, cam_hook_num, camera_id, camera_instance);
// increase camera hook number
cam_hook_num++;
}
}
// return result
return result_orig;
*pppSourceActivate = nullptr;
*pcSourceActivate = 0;
log_misc("sdvx", "MFEnumDeviceSources_hook called, returning 0 cameras");
return S_OK;
}
void camera_init() {
// camera media framework hook
MFEnumDeviceSources_orig = detour::iat_try(
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
log_info("sdvx", "installing camera hooks...");
detour::iat_try("MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
}
}
+11 -7
View File
@@ -24,6 +24,7 @@
#include "io.h"
#include "acioemu/handle.h"
#include "cfg/configurator.h"
#include "launcher/signal.h"
static decltype(RegCloseKey) *RegCloseKey_orig = nullptr;
static decltype(RegEnumKeyA) *RegEnumKeyA_orig = nullptr;
@@ -39,7 +40,6 @@ namespace games::sdvx {
const char *ORIGINAL_ASIO_DEVICE_NAME = "XONAR SOUND CARD(64)";
// settings
bool DISABLECAMS = false;
bool NATIVETOUCH = false;
uint8_t DIGITAL_KNOB_SENS = 16;
SdvxOverlayPosition OVERLAY_POS = SDVX_OVERLAY_BOTTOM;
@@ -210,6 +210,10 @@ namespace games::sdvx {
out = "";
return true;
}
if (data.find("SuperstepSound: Audio device is not available") != std::string::npos) {
launcher::signal::SUPERSTEP_SOUND_ERROR = TRUE;
return false;
}
return false;
}
@@ -369,17 +373,17 @@ namespace games::sdvx {
winuser_hook_init(avs::game::DLL_INSTANCE);
// hook camera
if (!DISABLECAMS) {
camera_init();
}
camera_init();
// RGB CAMERA error ignore
if (!replace_pattern(
// RGB CAMERA error ignore for SDVX5
// SDVX5: boot sequence triggers camera error if camera is not detected
// SDVX6: boots fine, but game title screen in attract loop will have camera error (cosmetic only)
if (replace_pattern(
avs::game::DLL_INSTANCE,
"418D480484C074218D51FD",
"????????????9090??????",
0, 0)) {
log_info("sdvx", "did not find matching signature for camera error patch");
log_info("sdvx", "applied camera error patch (sdvx5)");
}
// remove log spam
+3 -2
View File
@@ -11,11 +11,12 @@ namespace games::sdvx {
enum SdvxOverlayPosition {
SDVX_OVERLAY_TOP,
SDVX_OVERLAY_MIDDLE,
SDVX_OVERLAY_BOTTOM
SDVX_OVERLAY_BOTTOM,
SDVX_OVERLAY_BOTTOM_LEFT,
SDVX_OVERLAY_BOTTOM_RIGHT
};
// settings
extern bool DISABLECAMS;
extern bool NATIVETOUCH;
extern uint8_t DIGITAL_KNOB_SENS;
extern std::optional<std::string> ASIO_DRIVER;
+37 -17
View File
@@ -294,7 +294,7 @@ namespace games::shared {
// error
if (!clear) {
log_fatal("sdvx", "could not find path");
log_fatal("printer", "could not find path");
}
return "DUMMY";
@@ -326,7 +326,7 @@ namespace games::shared {
memcpy(image_data, pBandImage->baseAddr, (size_t) (image_width * image_height * 3));
// convert BGR to RGB
log_info("printer", "converting BGR to RGB...");
log_misc("printer", "converting BGR to RGB...");
for (int pixel = 0; pixel < image_width * image_height; pixel++) {
int index = pixel * 3;
uint8_t tmp = image_data[index];
@@ -334,25 +334,45 @@ namespace games::shared {
image_data[index + 2] = tmp;
}
// flip horizontally
for (int x = 0; x < image_width / 2; x++) {
for (int y = 0; y < image_height; y++) {
int index1 = (y * image_width + x) * 3;
int index2 = (y * image_width + image_width - x - 1) * 3;
uint8_t r = image_data[index1 + 0];
uint8_t g = image_data[index1 + 1];
uint8_t b = image_data[index1 + 2];
image_data[index1 + 0] = image_data[index2 + 0];
image_data[index1 + 1] = image_data[index2 + 1];
image_data[index1 + 2] = image_data[index2 + 2];
image_data[index2 + 0] = r;
image_data[index2 + 1] = g;
image_data[index2 + 2] = b;
if (avs::game::is_model({"KLP", "KFC"})) {
// rotate image clockwise
log_misc("printer", "rotate clockwise...");
auto rotated = new uint8_t[image_width * image_height * 3];
for (int x = 0; x < image_width; x++) {
for (int y = 0; y < image_height; y++) {
int index1 = (y * image_width + x) * 3;
int index2 = (x * image_height + y) * 3;
rotated[index2 + 0] = image_data[index1 + 0];
rotated[index2 + 1] = image_data[index1 + 1];
rotated[index2 + 2] = image_data[index1 + 2];
}
}
delete[] image_data;
image_data = rotated;
std::swap(image_width, image_height);
} else {
// flip horizontally
log_misc("printer", "flip horizontally...");
for (int x = 0; x < image_width / 2; x++) {
for (int y = 0; y < image_height; y++) {
int index1 = (y * image_width + x) * 3;
int index2 = (y * image_width + image_width - x - 1) * 3;
uint8_t r = image_data[index1 + 0];
uint8_t g = image_data[index1 + 1];
uint8_t b = image_data[index1 + 2];
image_data[index1 + 0] = image_data[index2 + 0];
image_data[index1 + 1] = image_data[index2 + 1];
image_data[index1 + 2] = image_data[index2 + 2];
image_data[index2 + 0] = r;
image_data[index2 + 1] = g;
image_data[index2 + 2] = b;
}
}
}
// iterate folders
log_info("printer", "writing files...");
log_misc("printer", "writing files...");
for (const auto &path : PRINTER_PATH) {
for (const auto &format : PRINTER_FORMAT) {
@@ -681,10 +681,27 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
// dump presentation parameters
for (size_t i = 0; i < num_adapters; i++) {
auto *params = &pPresentationParameters[i];
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
if (!GRAPHICS_WINDOWED && i == 0) {
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
log_misc(
"graphics::d3d9",
"use custom resolution {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
log_misc(
"graphics::d3d9",
"swap full orientation {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight,
params->BackBufferHeight, params->BackBufferWidth);
std::swap(params->BackBufferWidth, params->BackBufferHeight);
}
}
log_info("graphics::d3d9",
@@ -852,10 +869,27 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
for (size_t i = 0; i < num_adapters; i++) {
auto *params = &pPresentationParameters[i];
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
if (!GRAPHICS_WINDOWED && i == 0) {
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
log_misc(
"graphics::d3d9",
"use custom resolution {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
log_misc(
"graphics::d3d9",
"swap full orientation {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight,
params->BackBufferHeight, params->BackBufferWidth);
std::swap(params->BackBufferWidth, params->BackBufferHeight);
}
}
log_info("graphics::d3d9",
@@ -882,9 +916,13 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
for (size_t i = 0; i < num_adapters; i++) {
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
if (!GRAPHICS_WINDOWED && i == 0 && GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
if (!GRAPHICS_WINDOWED && i == 0) {
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
}
}
log_info("graphics::d3d9",
@@ -7,6 +7,7 @@
#include "hooks/graphics/graphics.h"
#include "overlay/overlay.h"
#include "util/flags_helper.h"
#include "util/utils.h"
#include "cfg/screen_resize.h"
#include "d3d9_backend.h"
@@ -692,89 +693,181 @@ static IDirect3DSurface9 *backbuffer = nullptr;
static LPDIRECT3DSWAPCHAIN9 mSwapChain = nullptr;
static IDirect3DTexture9* tex;
static UINT topSurface_width = 0;
static UINT topSurface_height = 0;
static float topSurface_aspect_ratio = 1.f;
void SurfaceHook(IDirect3DDevice9 *pReal) {
// log_misc("graphics::d3d9", "SurfaceHook called");
D3DPRESENT_PARAMETERS param {};
pReal->GetSwapChain(0, &mSwapChain);
mSwapChain->GetPresentParameters(&param);
// phase 0 - create a surface that has the same aspect ratio as the original back buffer
// but larger in size. this is needed to ensure that when the user zooms out, we have
// sufficient buffer space (black border) around the original image
//
// (only done once)
if (!topSurface) {
if (pReal->CreateTexture(4096, 4096, 1,
topSurface_width = param.BackBufferWidth * 3;
topSurface_height = param.BackBufferHeight * 3;
topSurface_aspect_ratio = (float)topSurface_width / (float)topSurface_height;
if (pReal->CreateTexture(topSurface_width, topSurface_height, 1,
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, &tex, NULL) != D3D_OK) {
log_misc("graphics::d3d9", "create texture failed");
log_warning("graphics::d3d9", "SurfaceHook - create texture failed");
}
log_misc("graphics::d3d9", "Backbuffer: {} {} {}",
param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount);
tex->GetSurfaceLevel(0, &topSurface);
log_misc(
"graphics::d3d9", "SurfaceHook - Backbuffer: {} {} {}",
param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount);
tex->GetSurfaceLevel(0, &topSurface);
if (mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backbuffer) != D3D_OK) {
log_misc("graphics::d3d9", "GetBackBuffer failed");
log_warning("graphics::d3d9", "SurfaceHook - GetBackBuffer failed");
}
}
const int rectLeft = 1024;
const int rectTop = 576;
// pre-calculate dimensions used for phase 1
const int rectLeft = param.BackBufferWidth;
const int rectTop = param.BackBufferHeight;
const int w = param.BackBufferWidth;
const int h = param.BackBufferHeight;
D3DLOCKED_RECT rect;
HRESULT hr;
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
// phase 1 - copy the original back buffer onto the new surface.
// we draw it 1:1 in the center of the surface.
// if orientation is swapped, fix up the rect and draw it in the center.
//
// for this phase we always render with the same rect so that it's always overwritten
// by a new frame on the same spot.
RECT originRect {
rectLeft,
rectTop,
(LONG)(rectLeft + w),
(LONG)(rectTop + h),
};
if (GRAPHICS_FS_ORIENTATION_SWAP) {
originRect.left = (topSurface_width / 2) - (GRAPHICS_FS_ORIGINAL_WIDTH / 2);
originRect.top = (topSurface_height / 2) - (GRAPHICS_FS_ORIGINAL_HEIGHT / 2);
originRect.right = originRect.left + GRAPHICS_FS_ORIGINAL_WIDTH;
originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT;
}
// log_misc(
// "graphics::d3d9", "originRect: {} {} {} {}",
// originRect.left, originRect.top, originRect.right, originRect.bottom);
hr = pReal->StretchRect(
backbuffer, nullptr,
topSurface, &originRect,
D3DTEXF_LINEAR);
if (hr != D3D_OK) {
log_warning(
"graphics::d3d9",
"SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}",
originRect.left, originRect.top, originRect.right, originRect.bottom);
}
topSurface->UnlockRect();
// phase 2 - viewport calculation - do the actual zoom / offset math
// figure out what region of the surface to copy back to the back buffer.
RECT targetRect {
rectLeft,
rectTop,
(LONG)(rectLeft + w),
(LONG)(rectTop + h),
};
if (cfg::SCREENRESIZE->enable_screen_resize) {
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
auto w_new = w / scene.scale_x;
auto h_new = h / scene.scale_y;
// stretch to add top/left offset to avoid going negative
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
auto hr = pReal->StretchRect(
backbuffer, nullptr,
topSurface, &targetRect,
D3DTEXF_LINEAR);
// make sure the scaling is within bounds
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
if (w_new > topSurface_width || h_new > topSurface_height) {
w_new = topSurface_width;
h_new = topSurface_height;
}
} else {
w_new = MIN(w_new, topSurface_width);
h_new = MIN(h_new, topSurface_height);
}
if (hr != D3D_OK) {
log_misc("graphics::d3d9", "StretchRect backbuffer failed");
}
topSurface->UnlockRect();
targetRect.left = (topSurface_width / 2) - (w_new / 2) - scene.offset_x;
targetRect.top = (topSurface_height / 2) - (h_new / 2) - scene.offset_y;
targetRect.right = targetRect.left + w_new;
targetRect.bottom = targetRect.top + h_new;
// do the actual zoom / offset math
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
if (scene.centered) {
targetRect.right = (w + rectLeft) / scene.scale_x;
targetRect.bottom = (h + rectTop) / scene.scale_y;
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
targetRect.top -= deltaH;
targetRect.bottom -= deltaH;
targetRect.left -= deltaW;
targetRect.right -= deltaW;
} else {
targetRect.left -= scene.offset_x;
targetRect.top += scene.offset_y;
targetRect.right = -scene.offset_x;
targetRect.right += (w + rectLeft) / scene.scale_x;
targetRect.bottom = scene.offset_y;
targetRect.bottom += (h + rectTop) / scene.scale_y;
// boundary check to prevent StretchRect failures
if (targetRect.left < 0) {
targetRect.left = 0;
targetRect.right = w_new;
} else if (targetRect.right > (LONG)topSurface_width) {
targetRect.left = topSurface_width - w_new;
targetRect.right = topSurface_width;
}
if (targetRect.top < 0) {
targetRect.top = 0;
targetRect.bottom = h_new;
} else if (targetRect.bottom > (LONG)topSurface_height) {
targetRect.top = topSurface_height - h_new;
targetRect.bottom = topSurface_height;
}
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
// increase the viewport to fit the image on screen (effectively, zoom out a little)
if (GRAPHICS_FS_ORIGINAL_WIDTH < GRAPHICS_FS_ORIGINAL_HEIGHT) {
// portrait game on landscape display
// height should match the original image
targetRect.top = originRect.top;
targetRect.bottom = originRect.bottom;
// width of viewport should be wider, effectively shrinking the image on x-axis when rendered
const auto w_new = GRAPHICS_FS_ORIGINAL_HEIGHT * topSurface_aspect_ratio;
targetRect.left = (topSurface_width / 2) - (w_new / 2);
targetRect.right = targetRect.left + w_new;
} else {
// landscape game on portrait display
targetRect.left = originRect.left;
targetRect.right = originRect.right;
// height of viewport should be taller, effectively shrinking the image on y-axis when rendered
const auto h_new = GRAPHICS_FS_ORIGINAL_WIDTH / topSurface_aspect_ratio;
targetRect.top = (topSurface_height / 2) - (h_new / 2);
targetRect.bottom = targetRect.top + h_new;
}
}
// draw to back buffer
// log_misc("graphics::d3d9", "targetRect: {} {} {} {}",
// targetRect.left, targetRect.top, targetRect.right, targetRect.bottom);
// phase 3 - draw the surface to back buffer
backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
bool use_linear_filter = true;
if (cfg::SCREENRESIZE->enable_screen_resize) {
use_linear_filter = cfg::SCREENRESIZE->enable_linear_filter;
}
hr = pReal->StretchRect(
topSurface, &targetRect,
backbuffer, nullptr,
cfg::SCREENRESIZE->enable_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
backbuffer->UnlockRect();
if (hr != D3D_OK) {
log_misc("graphics::d3d9", "StretchRect targetRect failed");
log_warning(
"graphics::d3d9",
"SurfaceHook - StretchRect targetRect failed, you must reset image scaling to default values! rect: {} {} {} {}",
targetRect.left, targetRect.top, targetRect.right, targetRect.bottom);
}
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EndScene() {
WRAP_DEBUG;
if (cfg::SCREENRESIZE->enable_screen_resize) {
if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_ORIENTATION_SWAP) {
SurfaceHook(pReal);
}
+30 -3
View File
@@ -68,6 +68,9 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
bool SUBSCREEN_FORCE_REDRAW = false;
bool D3D9_DEVICE_HOOK_DISABLE = false;
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
// settings
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
@@ -371,6 +374,27 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window) {
dwStyle &= ~(WS_MAXIMIZEBOX);
}
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
}
if (is_sdvx_sub_window) {
graphics_load_windowed_subscreen_parameters();
if (GRAPHICS_WSUB_SIZE.has_value()) {
nWidth = GRAPHICS_WSUB_WIDTH;
nHeight = GRAPHICS_WSUB_HEIGHT;
} else {
GRAPHICS_WSUB_WIDTH = nWidth;
GRAPHICS_WSUB_HEIGHT = nHeight;
}
if (GRAPHICS_WSUB_POS.has_value()) {
x = GRAPHICS_WSUB_X;
y = GRAPHICS_WSUB_Y;
} else {
GRAPHICS_WSUB_X = x;
GRAPHICS_WSUB_Y = y;
}
}
// call original
HWND result = CreateWindowExA_orig(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
@@ -526,11 +550,11 @@ static BOOL WINAPI MoveWindow_hook(HWND hWnd, int X, int Y, int nWidth, int nHei
dwStyle = GetWindowLongA(hWnd, GWL_STYLE);
SetRect(&rect, 0, 0, GRAPHICS_IIDX_WSUB_WIDTH, GRAPHICS_IIDX_WSUB_HEIGHT);
SetRect(&rect, 0, 0, GRAPHICS_WSUB_WIDTH, GRAPHICS_WSUB_HEIGHT);
AdjustWindowRect(&rect, dwStyle, 0);
X = GRAPHICS_IIDX_WSUB_X;
Y = GRAPHICS_IIDX_WSUB_Y;
X = GRAPHICS_WSUB_X;
Y = GRAPHICS_WSUB_Y;
nWidth = rect.right - rect.left;
nHeight = rect.bottom - rect.top;
@@ -862,6 +886,9 @@ void graphics_hook_subscreen_window(HWND hWnd) {
WSUB_WNDPROC_ORIG = reinterpret_cast<WNDPROC>(GetWindowLongPtrA(hWnd, GWLP_WNDPROC));
SetWindowLongPtrA(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WsubWindowProc));
}
if (GRAPHICS_WSUB_ALWAYS_ON_TOP) {
graphics_update_z_order(hWnd, true);
}
}
void graphics_screens_register(int screen) {
+12 -7
View File
@@ -36,6 +36,9 @@ extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE;
extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME;
@@ -47,12 +50,14 @@ extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
extern bool GRAPHICS_IIDX_WSUB;
extern std::optional<std::string> GRAPHICS_IIDX_WSUB_SIZE;
extern std::optional<std::string> GRAPHICS_IIDX_WSUB_POS;
extern int GRAPHICS_IIDX_WSUB_WIDTH;
extern int GRAPHICS_IIDX_WSUB_HEIGHT;
extern int GRAPHICS_IIDX_WSUB_X;
extern int GRAPHICS_IIDX_WSUB_Y;
extern std::optional<std::string> GRAPHICS_WSUB_SIZE;
extern std::optional<std::string> GRAPHICS_WSUB_POS;
extern int GRAPHICS_WSUB_WIDTH;
extern int GRAPHICS_WSUB_HEIGHT;
extern int GRAPHICS_WSUB_X;
extern int GRAPHICS_WSUB_Y;
extern bool GRAPHICS_WSUB_BORDERLESS;
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
extern HWND TDJ_SUBSCREEN_WINDOW;
extern HWND SDVX_SUBSCREEN_WINDOW;
@@ -91,7 +96,7 @@ std::string graphics_screenshot_genpath();
void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void graphics_capture_initial_window(HWND hWnd);
void graphics_update_window_style(HWND hWnd);
void graphics_update_z_order(HWND hWnd);
void graphics_update_z_order(HWND hWnd, bool always_on_top);
void graphics_move_resize_window(HWND hWnd);
bool graphics_window_change_crashes_game();
void graphics_load_windowed_subscreen_parameters();
@@ -26,12 +26,14 @@ bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
bool GRAPHICS_IIDX_WSUB = false;
std::optional<std::string> GRAPHICS_IIDX_WSUB_SIZE;
std::optional<std::string> GRAPHICS_IIDX_WSUB_POS;
int GRAPHICS_IIDX_WSUB_WIDTH = 1280;
int GRAPHICS_IIDX_WSUB_HEIGHT = 720;
int GRAPHICS_IIDX_WSUB_X = 0;
int GRAPHICS_IIDX_WSUB_Y = 0;
std::optional<std::string> GRAPHICS_WSUB_SIZE;
std::optional<std::string> GRAPHICS_WSUB_POS;
int GRAPHICS_WSUB_WIDTH = 1280;
int GRAPHICS_WSUB_HEIGHT = 720;
int GRAPHICS_WSUB_X = 0;
int GRAPHICS_WSUB_Y = 0;
bool GRAPHICS_WSUB_BORDERLESS = false;
bool GRAPHICS_WSUB_ALWAYS_ON_TOP = false;
// these flags are carefully constructed to ensure maximum compatibility
// (e.g., DDR likes to hang when SetWindowPos is called with certain params)
@@ -108,7 +110,7 @@ void graphics_capture_initial_window(HWND hWnd) {
}
if (cfg::SCREENRESIZE->window_always_on_top) {
log_info("graphics-windowed", "change window z-order - always on top");
graphics_update_z_order(hWnd);
graphics_update_z_order(hWnd, true);
}
// ensure spicetouch coordinates are initialized
@@ -169,29 +171,29 @@ void graphics_load_windowed_subscreen_parameters() {
log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called");
if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) {
if (GRAPHICS_WSUB_SIZE.has_value()) {
log_debug(
"graphics-windowed",
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE");
"graphics_load_windowed_parameters - load GRAPHICS_WSUB_SIZE");
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(GRAPHICS_IIDX_WSUB_SIZE.value(), result)) {
GRAPHICS_IIDX_WSUB_WIDTH = result.first;
GRAPHICS_IIDX_WSUB_HEIGHT = result.second;
if (parse_width_height(GRAPHICS_WSUB_SIZE.value(), result)) {
GRAPHICS_WSUB_WIDTH = result.first;
GRAPHICS_WSUB_HEIGHT = result.second;
} else {
log_warning("graphics-windowed", "failed to parse -wsubsize");
}
}
if (GRAPHICS_IIDX_WSUB_POS.has_value()) {
if (GRAPHICS_WSUB_POS.has_value()) {
log_debug(
"graphics-windowed",
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS");
"graphics_load_windowed_parameters - load GRAPHICS_WSUB_POS");
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(GRAPHICS_IIDX_WSUB_POS.value(), result)) {
GRAPHICS_IIDX_WSUB_X = result.first;
GRAPHICS_IIDX_WSUB_Y = result.second;
if (parse_width_height(GRAPHICS_WSUB_POS.value(), result)) {
GRAPHICS_WSUB_X = result.first;
GRAPHICS_WSUB_Y = result.second;
} else {
log_warning("graphics-windowed", "failed to parse -wsubpos");
}
@@ -398,7 +400,7 @@ void graphics_update_window_style(HWND hWnd) {
log_debug("graphics-windowed", "graphics_update_window_style returned");
}
void graphics_update_z_order(HWND hWnd) {
void graphics_update_z_order(HWND hWnd, bool always_on_top) {
if (!GRAPHICS_WINDOWED) {
return;
}
@@ -406,7 +408,7 @@ void graphics_update_z_order(HWND hWnd) {
log_debug("graphics-windowed", "graphics_update_z_order called");
HWND insert_after = nullptr;
if (cfg::SCREENRESIZE->window_always_on_top) {
if (always_on_top) {
insert_after = HWND_TOPMOST;
} else {
insert_after = HWND_NOTOPMOST;
+51
View File
@@ -18,6 +18,7 @@ constexpr UINT CODEPAGE_SHIFT_JIS = 932;
static decltype(GetACP) *GetACP_orig = nullptr;
static decltype(GetOEMCP) *GetOEMCP_orig = nullptr;
static decltype(MultiByteToWideChar) *MultiByteToWideChar_orig = nullptr;
static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr;
#ifdef SPICE64
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
@@ -119,6 +120,48 @@ static int WINAPI MultiByteToWideChar_hook(
cchWideChar);
}
static int WINAPI GetLocaleInfoEx_hook (
LPCWSTR lpLocaleName,
LCTYPE LCType,
LPWSTR lpLCData,
int cchData)
{
#if 0
if (lpLocaleName == LOCALE_NAME_INVARIANT) {
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit (LOCALE_NAME_INVARIANT), {}, {}", LCType, cchData);
} else if (lpLocaleName == LOCALE_NAME_SYSTEM_DEFAULT) {
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit (LOCALE_NAME_SYSTEM_DEFAULT), {}, {}", LCType, cchData);
} else if (lpLocaleName == LOCALE_NAME_USER_DEFAULT) {
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit (LOCALE_NAME_USER_DEFAULT), {}, {}", LCType, cchData);
} else {
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit ({}), {}, {}", ws2s(lpLocaleName), LCType, cchData);
}
#endif
if (lpLocaleName == LOCALE_NAME_USER_DEFAULT &&
LCType == LOCALE_SISO639LANGNAME &&
lpLCData != NULL &&
cchData >= 3) {
log_misc("hooks::lang",
"GetLocaleInfoEx_hook hit (LOCALE_NAME_USER_DEFAULT / LOCALE_SISO639LANGNAME), return `ja`");
wcscpy(lpLCData, L"ja");
return 3;
}
if (lpLocaleName == LOCALE_NAME_USER_DEFAULT &&
LCType == LOCALE_SISO3166CTRYNAME &&
lpLCData != NULL &&
cchData >= 3) {
log_misc("hooks::lang",
"GetLocaleInfoEx_hook hit (LOCALE_NAME_USER_DEFAULT / LOCALE_SISO3166CTRYNAME), return `JP`");
wcscpy(lpLCData, L"JP");
return 3;
}
return GetLocaleInfoEx_orig(lpLocaleName, LCType, lpLCData, cchData);
}
void hooks::lang::early_init() {
log_info("hooks::lang", "early initialization");
@@ -138,6 +181,14 @@ void hooks::lang::early_init() {
}
#endif
if (avs::game::is_model("XIF")) {
log_info("hooks::lang", "hooking GetLocaleInfoEx");
detour::trampoline_try(
"kernel32.dll",
"GetLocaleInfoEx",
GetLocaleInfoEx_hook,
&GetLocaleInfoEx_orig);
}
}
void hooks::lang::init() {
+5 -1
View File
@@ -141,13 +141,17 @@ static ULONG WINAPI GetAdaptersInfo_hook(PIP_ADAPTER_INFO pAdapterInfo, PULONG p
static int WINAPI bind_hook(SOCKET s, const struct sockaddr *name, int namelen) {
#ifdef __clang__
#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCDFAInspection"
#endif
// cast to sockaddr_in
struct sockaddr_in *in_name = (struct sockaddr_in *) name;
#ifdef __clang__
#pragma clang diagnostic pop
#endif
// override bind to allow all hosts
in_name->sin_addr.s_addr = inet_addr("0.0.0.0");
@@ -179,7 +183,7 @@ void networkhook_init() {
strncpy(s_prefix, inet_ntoa(prefix), 16);
// log preferences
log_info("network", "Network preferences: {}", s_network, s_subnet, s_prefix);
// log_info("network", "Network preferences: {}, {}, {}", s_network, s_subnet, s_prefix);
// GetAdaptersInfo hook
auto orig_addr = detour::iat_try(
+309 -66
View File
@@ -6,6 +6,7 @@
#include <shlwapi.h>
#include <cfg/configurator.h>
#include "api/modules/capture.h"
#include "acio/acio.h"
#include "acio/icca/icca.h"
#include "api/controller.h"
@@ -60,6 +61,7 @@
#include "games/ccj/trackball.h"
#include "games/qks/qks.h"
#include "games/mfg/mfg.h"
#include "games/pc/pc.h"
#include "games/museca/museca.h"
#include "hooks/avshook.h"
#include "hooks/audio/audio.h"
@@ -124,6 +126,8 @@ std::string LOG_FILE_PATH = "";
int LAUNCHER_ARGC = 0;
char **LAUNCHER_ARGV = nullptr;
std::unique_ptr<std::vector<Option>> LAUNCHER_OPTIONS;
std::mutex CARD_OVERRIDES_LOCK;
std::string CARD_OVERRIDES[2];
// sub-systems
@@ -147,6 +151,9 @@ static bool check_dll(const std::string &model) {
void update_msvcrt_args(int argc, char *argv[]);
void dump_button_bindings(std::vector<Button> *buttons);
void dump_analog_bindings();
int main_implementation(int argc, char *argv[]) {
// remember argv, argv
@@ -216,7 +223,9 @@ int main_implementation(int argc, char *argv[]) {
bool attach_ccj = false;
bool attach_qks = false;
bool attach_mfg = false;
bool attach_pc = false;
bool attach_museca = false;
bool show_cursor_if_no_touch = false;
// misc settings
size_t user_heap_size = 0;
@@ -240,6 +249,11 @@ int main_implementation(int argc, char *argv[]) {
// parse arguments
LAUNCHER_OPTIONS = launcher::parse_options(argc, argv);
// command line override (must be done before merging options with cfg)
if (LAUNCHER_OPTIONS->at(launcher::Options::OptionConflictResolution).value_bool()) {
launcher::USE_CMD_OVERRIDE = true;
}
// determine config file path - must be done before anything else
const auto &cfg_path = LAUNCHER_OPTIONS->at(launcher::Options::ConfigurationPath);
if (cfg_path.is_active()) {
@@ -281,6 +295,7 @@ int main_implementation(int argc, char *argv[]) {
cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config;
cfg_run = true;
}
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
options[launcher::Options::ServiceURL].is_active() &&
!cfg::CONFIGURATOR_STANDALONE) {
@@ -350,6 +365,9 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::VerboseAVSLogging].value_bool()) {
hooks::avs::config::LOG = true;
}
if (options[launcher::Options::AllowEA3Verbose].value_bool()) {
avs::ea3::EA3_DEBUG_VERBOSE = true;
}
if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
GRAPHICS_ADJUST_ORIENTATION =
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
@@ -367,15 +385,6 @@ int main_implementation(int argc, char *argv[]) {
GRAPHICS_FORCE_SINGLE_ADAPTER = false;
}
if (options[launcher::Options::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
} else {
log_warning("launcher", "failed to parse -forceres");
}
}
if (options[launcher::Options::spice2x_NvapiProfile].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
nvapi::ADD_PROFILE = true;
}
@@ -429,9 +438,6 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::LoadSoundVoltexModule].value_bool()) {
attach_sdvx = true;
}
if (options[launcher::Options::SDVXDisableCameras].value_bool()) {
games::sdvx::DISABLECAMS = true;
}
if (options[launcher::Options::SDVXNativeTouch].value_bool()) {
games::sdvx::NATIVETOUCH = true;
}
@@ -448,7 +454,12 @@ int main_implementation(int argc, char *argv[]) {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_TOP;
} else if (txt == "center") {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_MIDDLE;
} else if (txt == "bottomleft") {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT;
} else if (txt == "bottomright") {
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT;
}
// else - bottom
}
if (options[launcher::Options::spice2x_SDVXSubRedraw].value_bool()) {
SUBSCREEN_FORCE_REDRAW = true;
@@ -617,12 +628,18 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::LoadMFGModule].value_bool()) {
attach_mfg = true;
}
if (options[launcher::Options::LoadPCModule].value_bool()) {
attach_pc = true;
}
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
attach_museca = true;
}
if (options[launcher::Options::DDR43Mode].value_bool()) {
games::ddr::SDMODE = true;
}
if (options[launcher::Options::DDRSkipCodecRegisteration].value_bool()) {
games::ddr::NO_CODEC_REGISTRATION = true;
}
if (options[launcher::Options::LoadSteelChronicleModule].value_bool()) {
attach_sc = true;
}
@@ -662,21 +679,42 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::SDVXPrinterEmulation].value_bool()) {
attach_cpusbxpkm_printer = true;
}
if (options[launcher::Options::SDVXPrinterOutputOverwrite].value_bool()) {
// printer overwrite
if (options[launcher::Options::SDVXPrinterOutputOverwrite].value_bool() ||
options[launcher::Options::LovePlusPrinterOutputOverwrite].value_bool()) {
games::shared::PRINTER_OVERWRITE_FILE = true;
}
// printer output path
for (auto &path : options[launcher::Options::SDVXPrinterOutputPath].values_text()) {
games::shared::PRINTER_PATH.push_back(path);
}
for (auto &path : options[launcher::Options::LovePlusPrinterOutputPath].values_text()) {
games::shared::PRINTER_PATH.push_back(path);
}
// printer file format
for (auto &path : options[launcher::Options::SDVXPrinterOutputFormat].values_text()) {
games::shared::PRINTER_FORMAT.push_back(path);
}
for (auto &path : options[launcher::Options::LovePlusPrinterOutputFormat].values_text()) {
games::shared::PRINTER_FORMAT.push_back(path);
}
// printer JPG quality
if (options[launcher::Options::SDVXPrinterJPGQuality].is_active()) {
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::SDVXPrinterJPGQuality].value_uint32();
} else if (options[launcher::Options::LovePlusPrinterJPGQuality].is_active()) {
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::LovePlusPrinterJPGQuality].value_uint32();
}
if (options[launcher::Options::SDVXPrinterOutputClear].value_bool()) {
// printer output clear
if (options[launcher::Options::SDVXPrinterOutputClear].value_bool() ||
options[launcher::Options::LovePlusPrinterOutputClear].value_bool()) {
games::shared::PRINTER_CLEAR = true;
}
if (options[launcher::Options::HTTP11].is_active()) {
avs::ea3::HTTP11 = options[launcher::Options::HTTP11].value_uint32();
}
@@ -747,9 +785,11 @@ int main_implementation(int argc, char *argv[]) {
SetDllDirectoryW(MODULE_PATH.c_str());
}
if (options[launcher::Options::Player1Card].is_active()) {
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
CARD_OVERRIDES[0] = options[launcher::Options::Player1Card].value_text();
}
if (options[launcher::Options::Player2Card].is_active()) {
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
CARD_OVERRIDES[1] = options[launcher::Options::Player2Card].value_text();
}
if (options[launcher::Options::Player1PinMacro].is_active()) {
@@ -812,6 +852,7 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::AdapterSubnet].is_active()) {
NETWORK_SUBNET = options[launcher::Options::AdapterSubnet].value_text();
}
if (options[launcher::Options::APITCPPort].is_active()) {
api_enable = true;
api_port = options[launcher::Options::APITCPPort].value_uint32();
@@ -834,6 +875,13 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::APIDebugMode].value_bool()) {
api_debug = true;
}
if (options[launcher::Options::APIScreenMirrorQuality].is_active()) {
api::modules::CAPTURE_QUALITY = options[launcher::Options::APIScreenMirrorQuality].value_uint32();
}
if (options[launcher::Options::APIScreenMirrorDivide].is_active()) {
api::modules::CAPTURE_DIVIDE = options[launcher::Options::APIScreenMirrorDivide].value_uint32();
}
if (options[launcher::Options::DisableDebugHooks].value_bool()) {
debughook::DEBUGHOOK_LOGGING = false;
}
@@ -975,12 +1023,24 @@ int main_implementation(int argc, char *argv[]) {
GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool();
GRAPHICS_WINDOW_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].value_bool();
// IIDX Windowed Subscreen
// IIDX/SDVX Windowed Subscreen
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) {
GRAPHICS_IIDX_WSUB_SIZE = options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].value_text();
GRAPHICS_WSUB_SIZE = options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].value_text();
} else if (options[launcher::Options::SDVXWindowedSubscreenSize].is_active()) {
GRAPHICS_WSUB_SIZE = options[launcher::Options::SDVXWindowedSubscreenSize].value_text();
}
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].is_active()) {
GRAPHICS_IIDX_WSUB_POS = options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].value_text();
GRAPHICS_WSUB_POS = options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].value_text();
} else if (options[launcher::Options::SDVXWindowedSubscreenPosition].is_active()) {
GRAPHICS_WSUB_POS = options[launcher::Options::SDVXWindowedSubscreenPosition].value_text();
}
if (options[launcher::Options::IIDXWindowedSubscreenBorderless].value_bool() ||
options[launcher::Options::SDVXWindowedSubscreenBorderless].value_bool()) {
GRAPHICS_WSUB_BORDERLESS = true;
}
if (options[launcher::Options::IIDXWindowedSubscreenAlwaysOnTop].value_bool() ||
options[launcher::Options::SDVXWindowedSubscreenAlwaysOnTop].value_bool()) {
GRAPHICS_WSUB_ALWAYS_ON_TOP = true;
}
if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) {
@@ -1049,12 +1109,32 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::MFGNoIO].is_active()) {
games::mfg::MFG_NO_IO = options[launcher::Options::MFGNoIO].value_bool();
}
if (options[launcher::Options::PCArgs].is_active()) {
games::pc::PC_INJECT_ARGS = options[launcher::Options::PCArgs].value_text();
}
if (options[launcher::Options::PCNoIO].is_active()) {
games::pc::PC_NO_IO = options[launcher::Options::PCNoIO].value_bool();
}
if (options[launcher::Options::PCKnobMode].value_bool()) {
games::pc::PC_KNOB_MODE = true;
}
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
rawinput::ENABLE_SMX_STAGE = true;
}
if (options[launcher::Options::spice2x_EnableSMXDedicab].value_bool()) {
rawinput::ENABLE_SMX_DEDICAB = true;
}
if (options[launcher::Options::InputRequiresFocus].is_active()) {
const auto &text = options[launcher::Options::InputRequiresFocus].value_text();
if (text == "always") {
rawinput::NAIVE_REQUIRE_FOCUS = true;
rawinput::RAWINPUT_REQUIRE_FOCUS = true;
} else if (text == "never") {
rawinput::NAIVE_REQUIRE_FOCUS = false;
rawinput::RAWINPUT_REQUIRE_FOCUS = false;
}
}
if (options[launcher::Options::MidiAlgoVer].is_active()) {
if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") {
midi_algo = rawinput::MidiNoteAlgorithm::LEGACY;
@@ -1076,6 +1156,10 @@ int main_implementation(int argc, char *argv[]) {
rawinput::MIDI_NOTE_SUSTAIN = options[launcher::Options::MidiNoteSustain].value_uint32();
}
if (options[launcher::Options::LovePlusCamEnable].value_bool()) {
games::loveplus::CAMERA_ENABLE = true;
}
// API debugging
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
@@ -1117,6 +1201,8 @@ int main_implementation(int argc, char *argv[]) {
// log
#ifdef SPICE64
log_info("launcher", "SpiceTools Bootstrap (x64) (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
@@ -1160,6 +1246,77 @@ int main_implementation(int argc, char *argv[]) {
}
log_info("launcher", "arguments:\n{}", arguments.str());
// print out conflicts
size_t conflicts = 0;
for (const auto &option : options) {
if (option.conflicting && option.get_definition().type != OptionType::Bool) {
conflicts += 1;
if (launcher::USE_CMD_OVERRIDE) {
log_warning(
"launcher",
"multiple values for -{}, command line args take precedence: {}",
option.get_definition().name,
option.value);
} else {
log_warning(
"launcher",
"multiple values for -{}, spicecfg values take precedence: {}",
option.get_definition().name,
option.value);
}
}
}
if (conflicts) {
if (launcher::USE_CMD_OVERRIDE) {
log_info(
"launcher",
"user specified -cmdoverride, therefore command line args took precedence over spicecfg");
} else {
log_warning(
"launcher",
"spicecfg values take precedence over command line args; to change this behavior, use -cmdoverride");
}
}
if (options[launcher::Options::GameExecutable].is_active() && !cfg::CONFIGURATOR_STANDALONE) {
log_warning(
"launcher",
"WARNING - user specified -exec option\n\n\n"
"!!! !!!\n"
"!!! Using -exec option disables all game-specific hooks! !!!\n"
"!!! Unless you know exactly what you are doing, clear -exec !!!\n"
"!!! and try again. Using -exec by itself will result in weird !!!\n"
"!!! errors and loss of functionality. !!!\n"
"!!! !!!\n"
);
}
if (options[launcher::Options::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
} else {
log_warning("launcher", "failed to parse -forceres");
}
}
// SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
// FullscreenOrientationFlip continues to live on, however.
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool()) {
log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!");
}
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
// #if SPICE64
// GRAPHICS_FS_ORIENTATION_SWAP = true;
// #else
// log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
// #endif
// }
if (options[launcher::Options::FullscreenOrientationFlip].value_bool() && !GRAPHICS_WINDOWED) {
GRAPHICS_FS_ORIENTATION_SWAP = true;
}
// deleted options
if (options[launcher::Options::OpenKFControl].value_bool()) {
log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version.");
@@ -1275,11 +1432,7 @@ int main_implementation(int argc, char *argv[]) {
avs::game::DLL_NAME = "system.dll";
attach_io = true;
attach_shogikai = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1322,12 +1475,7 @@ int main_implementation(int argc, char *argv[]) {
// game crash fix
easrv_maint = false;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1442,12 +1590,7 @@ int main_implementation(int argc, char *argv[]) {
avs::game::DLL_NAME = "nostalgia.dll";
attach_io = true;
attach_nostalgia = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1553,12 +1696,7 @@ int main_implementation(int argc, char *argv[]) {
avs::game::DLL_NAME = "arknck.dll";
attach_io = true;
attach_we = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1591,10 +1729,7 @@ int main_implementation(int argc, char *argv[]) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_ccj = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1603,10 +1738,7 @@ int main_implementation(int argc, char *argv[]) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_qks = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1616,10 +1748,17 @@ int main_implementation(int argc, char *argv[]) {
attach_io = true;
attach_mfg = true;
launcher::signal::USE_VEH_WORKAROUND = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
// Polaris Chord
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/svm_Data")) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_pc = true;
launcher::signal::USE_VEH_WORKAROUND = true;
show_cursor_if_no_touch = true;
break;
}
@@ -1628,10 +1767,7 @@ int main_implementation(int argc, char *argv[]) {
avs::game::DLL_NAME = "kamunity.dll";
attach_io = true;
attach_bc = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1650,7 +1786,14 @@ int main_implementation(int argc, char *argv[]) {
// usage error
if (!cfg::CONFIGURATOR_STANDALONE
&& (!CHECK_DLL_IGNORE_ARCH)) {
log_fatal("launcher", "module auto detection failed.");
log_fatal(
"launcher",
"module auto detection failed!\n\n"
"This usually means one of the following:\n\n"
" 1. You put spice executables in the wrong place, 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"
);
}
break;
@@ -1778,6 +1921,10 @@ int main_implementation(int argc, char *argv[]) {
avs::core::set_default_heap_size("kamunity.dll");
games.push_back(new games::mfg::MFGGame());
}
if (attach_pc) {
avs::core::HEAP_SIZE = 536870912; // 512MB
games.push_back(new games::pc::PCGame());
}
// apply user heap size, if defined
if (user_heap_size > 0) {
@@ -1810,6 +1957,24 @@ int main_implementation(int argc, char *argv[]) {
exit(spicecfg_run(sextet_devices));
}
// log some DLLs found in path (purely for troubleshooting purposes to detect
// dxvk, ForceD3D9On12, ifs_layeredfs, etc)
libutils::warn_if_dll_exists("d3d8.dll");
libutils::warn_if_dll_exists("d3d9.dll");
libutils::warn_if_dll_exists("d3d10core.dll");
libutils::warn_if_dll_exists("d3d11.dll");
libutils::warn_if_dll_exists("d3d12.dll");
libutils::warn_if_dll_exists("dxgi.dll");
libutils::warn_if_dll_exists("opengl32.dll");
libutils::warn_if_dll_exists("nvcuda.dll");
libutils::warn_if_dll_exists("nvcuvid.dll");
libutils::warn_if_dll_exists("nvEncodeAPI64.dll");
libutils::warn_if_dll_exists("msvcr100.dll");
libutils::warn_if_dll_exists("dsound.dll");
// complain loudly & early about dll load ordering issue
libutils::check_duplicate_dlls();
// print cpu features
if (!cfg::CONFIGURATOR_STANDALONE && dump_sysinfo) {
cpuutils::print_cpu_features();
@@ -1827,6 +1992,26 @@ int main_implementation(int argc, char *argv[]) {
// print devices
RI_MGR->devices_print();
auto buttons = games::get_buttons(eamuse_get_game());
log_misc("rawinput", "Button mappings:");
dump_button_bindings(buttons);
log_misc("rawinput", "Keypad button mappings:");
auto keypads = games::get_buttons_keypads(eamuse_get_game());
dump_button_bindings(keypads);
log_misc("rawinput", "Overlay button mappings:");
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
dump_button_bindings(overlay_buttons);
log_misc("rawinput", "Analog mappings:");
dump_analog_bindings();
// for certain games, show cursor if no touch is available (must be called after RI_MGR is available)
if (show_cursor_if_no_touch && !is_touch_available("launcher::main_implementation")) {
GRAPHICS_SHOW_CURSOR = true;
}
// cardio
if (cardio_enabled) {
cardio_runner_start(true);
@@ -1963,15 +2148,6 @@ int main_implementation(int argc, char *argv[]) {
networkhook_init();
}
// layeredfs
if (fileutils::dir_exists("data_mods") &&
!fileutils::file_exists("ifs_hook.dll") &&
!fileutils::file_exists(MODULE_PATH / "ifs_hook.dll")) {
log_warning("launcher", "data_mods directory was found, but ifs_hook.dll is not present; your mods will not load");
log_warning("launcher", "to fix this, download ifs_layeredfs and add it as a DLL hook (-k)");
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
}
update_msvcrt_args(argc, argv);
// load hooks
@@ -1985,6 +2161,14 @@ int main_implementation(int argc, char *argv[]) {
}
}
// layeredfs
if (fileutils::dir_exists("data_mods") &&
GetModuleHandleA("ifs_hook.dll") == nullptr) {
log_warning("launcher", "data_mods directory was found, but ifs_hook.dll is not present; your mods will not load");
log_warning("launcher", "to fix this, download ifs_layeredfs and add it as a DLL hook (-k)");
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
}
// apply patches
{
overlay::windows::PatchManager patch_manager(nullptr, true);
@@ -2207,6 +2391,65 @@ void update_msvcrt_args(int argc, char *argv[]) {
#endif
}
void dump_button_bindings(std::vector<Button> *buttons) {
if (!buttons) {
return;
}
for (auto button = buttons->begin(); button != buttons->end(); ++button) {
if (!button->isSet()) {
continue;
}
if (button->isNaive()) {
log_misc(
"rawinput", " [{}] dev=Naive, vkey={}",
button->getName(),
button->getVKey()
);
} else {
log_misc(
"rawinput", " [{}] dev={}, vkey={}, analogtype={}",
button->getName(),
button->getDeviceIdentifier(),
button->getVKey(),
button->getAnalogType()
);
}
for (auto& alt : button->getAlternatives()) {
if (alt.getVKey() == INVALID_VKEY) {
continue;
}
log_misc(
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
button->getName(),
alt.isNaive() ? "Naive" : alt.getDeviceIdentifier(),
alt.getVKey(),
alt.getAnalogType()
);
}
}
}
void dump_analog_bindings() {
auto analogs = games::get_analogs(eamuse_get_game());
if (!analogs) {
return;
}
for (auto& analog : *analogs) {
if (!analog.isSet()) {
continue;
}
log_misc(
"rawinput", " [{}] dev={}, index={}",
analog.getName(),
analog.getDeviceIdentifier(),
analog.getIndex()
);
}
}
#ifndef SPICETOOLS_SPICECFG_STANDALONE
int main(int argc, char *argv[]) {
return main_implementation(argc, argv);
+362 -77
View File
@@ -17,43 +17,50 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
"Game Options",
"Common",
"Network",
"Overlay",
"Graphics (Common)",
"Graphics (Full Screen)",
"Graphics (Windowed)",
"Audio",
};
static const std::vector<std::string> CATEGORY_ORDER_ADVANCED = {
"Game Options (Advanced)",
"Overlay",
"Network (Advanced)",
"Performance",
"Miscellaneous",
"Paths",
"Touch Parameters",
"I/O Options",
"NFC Card Readers",
};
static const std::vector<std::string> CATEGORY_ORDER_DEV = {
"Network Adapters",
"Path Overrides",
"Network (Development)",
"Audio (Hacks)",
"I/O Modules",
"Development",
"Debug Log",
};
static const std::vector<std::string> CATEGORY_ORDER_NONE = {
""
};
bool launcher::USE_CMD_OVERRIDE = false;
/*
* Option Definitions
* Be aware that the order must be the same as in the enum launcher::Options!
*/
static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
{
.title = "Game Executable",
.title = "Override Game Executable & Disable Hooks",
.name = "exec",
.desc = "Path to the game DLL file",
.desc = "Path to the game DLL file.\n\n"
"This option is typically not needed; leave it empty to allow spice to auto-detect your game.\n\n"
"WARNING: this option also disables all game-specific hooks and turns off most I/O modules!",
.type = OptionType::Text,
.setting_name = "*.dll",
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Open Configurator",
@@ -80,6 +87,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Network",
},
{
// ServiceURL
.title = "EA Service URL",
.name = "url",
.desc = "Sets a custom service URL override",
@@ -141,6 +149,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Graphics (Windowed)",
},
{
// InjectHook
.title = "Inject DLL Hooks",
.name = "k",
.desc = "Multiple files are allowed; use multiple -k flags, or in SpiceCfg, separate by "
@@ -151,6 +160,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Common",
},
{
// EarlyInjectHook
.title = "Inject Early DLL Hooks",
.name = "z",
.desc = "Equivalent to 'Inject DLL Hooks' option, but ensures hooks are injected before "
@@ -185,9 +195,12 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
{
.title = "Monitor",
.name = "monitor",
.desc = "Sets the display that the game will be opened in, for multiple monitors",
.desc = "Sets the display that the game will be opened in, for multiple monitors.\n\n"
"0 is the primary monitor, 1 is the second monitor, and so on.\n\n"
"Not all games will respect this. Does not work at all with multi-monitor games like TDJ/UFC. "
"Disable Full Screen Optimizations for best results",
.type = OptionType::Integer,
.category = "Graphics (Common)",
.category = "Graphics (Full Screen)",
},
{
.title = "Only Use One Monitor",
@@ -195,7 +208,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
.type = OptionType::Bool,
.category = "Graphics (Common)",
.category = "Graphics (Full Screen)",
},
{
.title = "Force Refresh Rate",
@@ -211,10 +224,23 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc =
"For full screen mode, forcibly set a custom resolution.\n\n"
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
"If you are using -forceresswap/-sdvxlandscape, put the TARGET monitor resolution in this field.\n\n"
"This should only be used as last resort if your GPU/monitor can't display the resolution required by the game",
.type = OptionType::Text,
.setting_name = "1280,720",
.category = "Graphics (Common)"
.category = "Graphics (Full Screen)"
},
{
// FullscreenOrientationFlip
.title = "Full Screen Orientation Swap",
.name = "forceresswap",
.desc =
"Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n"
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
"Strongly consider combining this with -forceres option to render at monitor native resolution\n\n"
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out!",
.type = OptionType::Bool,
.category = "Graphics (Full Screen)"
},
{
// Graphics9On12
@@ -246,7 +272,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "nolegacy",
.desc = "Disables legacy key activation in-game.",
.type = OptionType::Bool,
.category = "Miscellaneous",
.category = "I/O Options",
},
{
.title = "Discord Rich Presence",
@@ -288,28 +314,29 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "This is NOT the EA service URL; use -url for that. "
"Force the use of an adapter with the specified network. Must also provide -subnet",
.type = OptionType::Text,
.category = "Network Adapters",
.category = "Network (Development)",
.sensitive = true,
},
{
.title = "Preferred NetAdapter Subnet",
.name = "subnet",
.desc = "Force the use of an adapter with the specified subnet. Must also provide -network",
.type = OptionType::Text,
.category = "Network Adapters",
.category = "Network (Development)",
},
{
.title = "Disable Network Fixes",
.name = "netfixdisable",
.desc = "Force disables network fixes",
.type = OptionType::Bool,
.category = "Network Adapters",
.category = "Network (Development)",
},
{
.title = "HTTP/1.1",
.name = "http11",
.desc = "Sets EA3 http11 value",
.type = OptionType::Enum,
.category = "Network (Advanced)",
.category = "Network (Development)",
.elements = {{"0", "Off"}, {"1", "On"}},
},
{
@@ -317,7 +344,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "ssldisable",
.desc = "Prevents the SSL protocol from being registered",
.type = OptionType::Bool,
.category = "Network (Advanced)",
.category = "Network (Development)",
},
{
.title = "URL Slash",
@@ -332,7 +359,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "r",
.desc = "Set custom SOFTID override",
.type = OptionType::Text,
.category = "Network (Advanced)",
.category = "Development",
.sensitive = true,
},
{
@@ -527,10 +554,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options",
},
{
.title = "IIDX BIO2 Firmware",
.title = "IIDX BIO2 Firmware Update",
.name = "iidxbio2fw",
.desc = "Enables BIO2 firmware updates",
.desc = "Enables BIO2 firmware updates. WARNING - can cause semi-permanent change to your I/O board",
.type = OptionType::Bool,
.hidden = true,
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
},
@@ -650,6 +678,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
// SDVXPrinterOutputPath
.title = "SDVX Printer Output Path",
.name = "printerpath",
.desc = "Path to folder where images will be stored",
@@ -674,9 +703,10 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
// SDVXPrinterOutputFormat
.title = "SDVX Printer Output Format",
.name = "printerformat",
.desc = "Path to folder where images will be stored",
.desc = "File format for printer output",
.type = OptionType::Text,
.setting_name = "(png/bmp/tga/jpg)",
.game_name = "Sound Voltex",
@@ -692,10 +722,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
.title = "SDVX Disable Cameras",
.title = "SDVX Disable Cameras (DEPRECATED - no longer needed)",
.name = "sdvxdisablecams",
.desc = "Disables cameras",
.desc = "This option does nothing.\n\n"
"This option was used in the past to fix cameras in SDVX5 but this is no longer "
"needed as camera check will always be skipped with a built-in patch.",
.type = OptionType::Bool,
.hidden = true,
.game_name = "Sound Voltex",
.category = "Game Options",
},
@@ -745,7 +778,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Enum,
.game_name = "Sound Voltex",
.category = "Overlay",
.elements = {{"top", ""}, {"center", ""}, {"bottom", ""}},
.elements = {
{"top", ""},
{"center", ""},
{"bottom", ""},
{"bottomleft", "for landscape"},
{"bottomright", "for landscape"},
},
},
{
// spice2x_SDVXSubRedraw
@@ -775,6 +814,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.game_name = "Dance Dance Revolution",
.category = "Game Options",
},
{
// DDRSkipCodecRegisteration
.title = "DDR Skip Codec Registration",
.name = "ddrnocodec",
.desc = "Prevent automatic registration of codecs in the com folder",
.type = OptionType::Bool,
.game_name = "Dance Dance Revolution",
.category = "Game Options (Advanced)",
},
{
.title = "Force Load Pop'n Music Module",
.name = "pnm",
@@ -1002,7 +1050,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
{
// LoadCCJModule
.title = "Force Load Chase Chase Jokers",
.title = "Force Load Chase Chase Jokers Module",
.name = "ccj",
.desc = "manually enable Chase Chase Jokers module",
.type = OptionType::Bool,
@@ -1011,7 +1059,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
{
// LoadQKSModule
.title = "Force Load QuizKnock STADIUM",
.title = "Force Load QuizKnock STADIUM Module",
.name = "qks",
.desc = "manually enable QuizKnock STADIUM module",
.type = OptionType::Bool,
@@ -1020,16 +1068,25 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
{
// LoadMFGModule
.title = "Force Load Mahjong Fight Girl",
.title = "Force Load Mahjong Fight Girl Module",
.name = "mfg",
.desc = "manually enable Mahjong Fight Girl module",
.type = OptionType::Bool,
.game_name = "Mahjong Fight Girl",
.category = "Game Options (Advanced)",
},
{
// LoadPCModule
.title = "Force Load Polaris Chord Module",
.name = "pc",
.desc = "manually enable Polaris Chord module",
.type = OptionType::Bool,
.game_name = "Polaris Chord",
.category = "Game Options (Advanced)",
},
{
// LoadMusecaModule
.title = "Force Load Museca",
.title = "Force Load Museca Module",
.name = "museca",
.desc = "manually enable Museca module",
.type = OptionType::Bool,
@@ -1037,88 +1094,88 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
.title = "Modules Folder Path",
.title = "Modules Folder Override",
.name = "modules",
.desc = "Sets a custom path to the modules folder",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Screenshot Folder Path",
.title = "Screenshot Folder Override",
.name = "screenshotpath",
.desc = "Sets a custom path to the screenshots folder",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Configuration Path",
.title = "Configuration Path Override",
.name = "cfgpath",
.desc = "Sets a custom file path for config file. Must be passed via the command line. "
"If left empty, %appdata%\\spicetools.xml will be used",
.type = OptionType::Text,
.setting_name = "(default)",
.category = "Paths",
.category = "Path Overrides",
.disabled = true,
},
{
// ScreenResizeConfigPath
.title = "Screen Resize Config Path",
.title = "Screen Resize Config Path Override",
.name = "resizecfgpath",
.desc = "Sets a custom file path for screen resize config file. "
"If left empty, %appdata%\\spicetools_screen_resize.json will be used",
"If left empty, %appdata%\\spice2x\\spicetools_screen_resize.json will be used",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
// PatchManagerConfigPath
.title = "Patch Manager Config Path",
.title = "Patch Manager Config Path Override",
.name = "patchcfgpath",
.desc = "Sets a custom file path for patch manager config file. Can be used to manage 'profiles' for auto-patches. "
"If left empty, %appdata%\\spicetools_patch_manager.json will be used",
"If left empty, %appdata%\\spice2x\\spicetools_patch_manager.json will be used",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Intel SDE Folder Path",
.title = "Intel SDE",
.name = "sde",
.desc = "Path to Intel SDE kit path for automatic attaching",
.type = OptionType::Text,
.category = "Paths",
.category = "Development",
},
{
.title = "Path to ea3-config.xml",
.title = "ea3-config.xml Override",
.name = "e",
.desc = "Sets a custom path to ea3-config.xml",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Path to app-config.xml",
.title = "app-config.xml Override",
.name = "a",
.desc = "Sets a custom path to app-config.xml",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Path to avs-config.xml",
.title = "avs-config.xml Override",
.name = "v",
.desc = "Sets a custom path to avs-config.xml",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Path to bootstrap.xml",
.title = "bootstrap.xml Override",
.name = "b",
.desc = "Sets a custom path to bootstrap.xml",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "Path to log.txt",
.title = "log.txt Override",
.name = "y",
.desc = "Sets a custom path to log.txt",
.type = OptionType::Text,
.category = "Paths",
.category = "Path Overrides",
},
{
.title = "API TCP Port",
@@ -1170,6 +1227,28 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool,
.category = "API (Dev)",
},
{
// APIScreenMirrorQuality
.title = "API Screen Mirror Quality Override",
.name = "apiscreenq",
.desc = "JPEG compression level of mirrored images, overriding any client request. "
"Value must be between 0 (poor quality) and 100 (best quality), inclusive. Default: 70",
.type = OptionType::Integer,
.setting_name = "(0-100)",
.category = "SpiceCompanion and API",
},
{
// APIScreenMirrorDivide
.title = "API Screen Mirror Divide Override",
.name = "apiscreendiv",
.desc = "Divide value of mirrored images, overriding any client request. "
"Divide of 1 means send every pixel, 2 means every other pixel, 3 means every third pixel, and so on; "
"increasing the value drastically reduces transfer size at the cost of image quality. "
"Value must be 1 or greater. Default: 1",
.type = OptionType::Integer,
.setting_name = "1",
.category = "SpiceCompanion and API",
},
{
.title = "Enable All IO Modules",
.name = "io",
@@ -1265,6 +1344,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Touch Parameters",
},
{
// ICCAReaderPort
.title = "ICCA Reader Port",
.name = "reader",
.desc = "Connects to and uses a ICCA on a given COM port",
@@ -1341,11 +1421,12 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
},
{
// SextetStreamPort
.title = "SextetStream Port",
.name = "sextet",
.desc = "Use a SextetStream device on a given COM port",
.type = OptionType::Text,
.category = "Miscellaneous",
.category = "I/O Options",
},
{
.title = "Enable BemaniTools 5 API",
@@ -1446,7 +1527,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"you use a different backend instead of exclusive WASAPI).\n\n"
"Check this if you want games to natively access your audio device",
.type = OptionType::Bool,
.category = "Audio",
.category = "Audio (Hacks)",
},
{
// spice2x_DisableVolumeHook
@@ -1458,16 +1539,19 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"Default: off (prevent games from changing audio volume by hooking IAudioEndpointVolume).\n\n"
"Check this if you want games to freely change your volume",
.type = OptionType::Bool,
.category = "Audio",
.category = "Audio (Hacks)",
},
{
.title = "Spice Audio Hook Backend",
.name = "audiobackend",
.desc = "Selects the audio backend to use when spice audio hook is enabled, overriding exclusive WASAPI. "
" Does nothing for games that do not output to exclusive WASAPI",
"Does nothing for games that do not output to exclusive WASAPI",
.type = OptionType::Enum,
.category = "Audio",
.elements = {{"asio", "ASIO"}, {"waveout", "waveOut"}},
.elements = {
{"asio", "ASIO"},
{"waveout", "broken, do not use"}
},
},
{
.title = "Spice Audio Hook ASIO Driver ID",
@@ -1483,7 +1567,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"This is automatically enabled when required and not normally needed",
.type = OptionType::Bool,
.hidden = true,
.category = "Audio",
.category = "Audio (Hacks)",
},
{
// DelayBy5Seconds
@@ -1536,11 +1620,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.elements = {{"0", "90 (CW)"}, {"1", "270 (CCW)"}},
},
{
.title = "Log Level",
.title = "AVS Log Level",
.name = "loglevel",
.desc = "Set the level of detail that gets written to the log",
.desc = "Set the level of detail for AVS log messages written to the log. Does not affect logging from spice",
.type = OptionType::Enum,
.category = "Performance",
.category = "Debug Log",
.elements = {{"fatal", ""}, {"warning", ""}, {"info", ""}, {"misc", ""}, {"all", ""}, {"disable", ""}},
},
{
@@ -1548,14 +1632,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "automap",
.desc = "Enable automap in patch configuration",
.type = OptionType::Bool,
.category = "Development",
.category = "Network (Development)",
},
{
.title = "EA Netdump",
.name = "netdump",
.desc = "Enable automap in network dumping configuration",
.type = OptionType::Bool,
.category = "Development",
.category = "Network (Development)",
},
{
.title = "Discord RPC AppID Override",
@@ -1569,35 +1653,45 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "logblock",
.desc = "Slower but safer logging used for debugging",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Debug CreateFile",
.name = "createfiledebug",
.desc = "Outputs CreateFile debug prints",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Verbose Graphics Logging",
.name = "graphicsverbose",
.desc = "Enable the verbose logging of graphics hook code",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Verbose AVS Logging",
.name = "avsverbose",
.desc = "Enable the verbose logging of AVS filesystem functions",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
// AllowEA3Verbose
.title = "Allow EA3 Verbose Debug Logging",
.name = "ea3verbose",
.desc = "This option is only useful for network developers; leave this OFF.\n\n"
"By default, ea3/debug/verbose node is removed by spice to prevent exposing PCBID in the logs. "
"When this is enabled, ea3/debug/verbose node in the XML will be kept and exposed to the game",
.type = OptionType::Bool,
.category = "Debug Log",
},
{
.title = "Disable Colored Output",
.name = "nocolor",
.desc = "Disable terminal colors for log outputs to console",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
.title = "Disable ACP Hook",
@@ -1632,7 +1726,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "pebprint",
.desc = "Prints PEB on startup to console",
.type = OptionType::Bool,
.category = "Development",
.category = "Debug Log",
},
{
// DumpSystemInfo
@@ -1640,7 +1734,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "sysdump",
.desc = "Print system information to the log on startup. Default: basic",
.type = OptionType::Enum,
.category = "Development",
.category = "Debug Log",
.elements = {
{"none", "Nothing"},
{"basic", "OS, CPU, SMBIOS, GPU"},
@@ -1725,6 +1819,35 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.game_name = "Mahjong Fight Girl",
.category = "Game Options (Advanced)"
},
{
// PCArgs
.title = "PC Arguments Override",
.name = "pcargs",
.desc = "Command line arguments passed to the game.",
.type = OptionType::Text,
.setting_name = "",
.game_name = "Polaris Chord",
.category = "Game Options (Advanced)",
},
{
// PCNoIO
.title = "PC Disable IO Emulation",
.name = "pcnoio",
.desc = "Disables BI2X hooks for PC",
.type = OptionType::Bool,
.setting_name = "",
.game_name = "Polaris Chord",
.category = "Game Options (Advanced)"
},
{
// PCKnobMode
.title = "PC Fader Knobs Mode",
.name = "pcknobs",
.desc = "Allows SDVX knobs and IIDX turntables to be bound as Polaris Chord faders",
.type = OptionType::Bool,
.game_name = "Polaris Chord",
.category = "Game Options",
},
{
// spice2x_LightsOverallBrightness
.title = "Lights Brightness Adjustment",
@@ -1819,6 +1942,24 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.game_name = "Beatmania IIDX",
.category = "Graphics (Windowed)",
},
{
// IIDXWindowedSubscreenBorderless
.title = "IIDX Windowed Subscreen Borderless",
.name = "iidxwsubborderless",
.desc = "Remove window decoration from windowed subscreen",
.type = OptionType::Bool,
.game_name = "Beatmania IIDX",
.category = "Graphics (Windowed)",
},
{
// IIDXWindowedSubscreenAlwaysOnTop
.title = "IIDX Windowed Subscreen Always On Top",
.name = "iidxwsubtop",
.desc = "Keep windowed subscreen on top",
.type = OptionType::Bool,
.game_name = "Beatmania IIDX",
.category = "Graphics (Windowed)",
},
{
// spice2x_JubeatLegacyTouch
.title = "JB Legacy Touch Targets",
@@ -1852,7 +1993,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "For certain buggy ASIO drivers, force unload of ASIO driver when audio stream stops. "
"Used for working around ASIO drivers that lock up after force quitting games",
.type = OptionType::Bool,
.category = "Audio",
.category = "Audio (Hacks)",
},
{
// spice2x_IIDXNoESpec
@@ -2025,6 +2166,20 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.game_name = "Sound Voltex",
.category = "Game Options",
},
{
// SDVXFullscreenLandscape
.title = "(DISABLED) SDVX Landscape Mode (SDVX5+)",
.name = "sdvxlandscape",
.desc =
"Option HIDDEN and DISABLED due to it affecting in-game camera angle for displaying lanes.\n\n"
"Allows you to play in landscape by transposing resolution and applying image scaling.\n\n"
"Works only for SDVX5 and above! This is identical to -forceresswap.\n\n"
"Will launch at 1080p by default; strongly consider combining this with -forceres option to render at monitor native resolution",
.type = OptionType::Bool,
.hidden = true,
.game_name = "Sound Voltex",
.category = "Game Options",
},
{
// spice2x_EnableSMXStage
.title = "StepManiaX Stage Lighting Support",
@@ -2092,6 +2247,23 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.setting_name = "20",
.category = "I/O Options",
},
{
// InputRequiresFocus
.title = "Input Requires Focus",
.name = "inputfocus",
.desc =
"Determine input behavior when game window is not in focus.\n\n"
"never: allow input regardless of focus\n"
"naive (default): only naive bindings check for window focus, rawinput binds are always allowed\n"
"always: only process input when window is in focus",
.type = OptionType::Enum,
.category = "I/O Options",
.elements = {
{"never", ""},
{"naive", ""},
{"always", ""},
},
},
{
// NostalgiaPoke
.title = "Nost Screen Poke",
@@ -2116,6 +2288,107 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
{"2", "double buffer"},
{"3", "triple buffer"},
},
},
{
// SDVXWindowedSubscreenSize
.title = "SDVX Windowed Subscreen Size",
.name = "sdvxwsubsize",
.desc = "Size of the subscreen window. Defaults to (1920,1080)",
.type = OptionType::Text,
.setting_name = "1920,1080",
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// SDVXWindowedSubscreenPosition
.title = "SDVX Windowed Subscreen Position",
.name = "sdvxwsubpos",
.desc = "Initial position of the subscreen window. Defaults to (0,0)",
.type = OptionType::Text,
.setting_name = "0,0",
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// SDVXWindowedSubscreenBorderless
.title = "SDVX Windowed Subscreen Borderless",
.name = "sdvxwsubborderless",
.desc = "Remove window decoration from windowed subscreen",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// SDVXWindowedSubscreenAlwaysOnTop
.title = "SDVX Windowed Subscreen Always On Top",
.name = "sdvxwsubtop",
.desc = "Keep windowed subscreen on top",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// LovePlusCamEnable
.title = "LovePlus Camera Enable",
.name = "lovepluscam",
.desc = "Allow game to access camera; camera must be compatible with game",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options",
},
{
// LovePlusPrinterOutputPath
.title = "LovePlus Printer Output Path",
.name = "lpprinterpath",
.desc = "Path to folder where images will be stored",
.type = OptionType::Text,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
.title = "LovePlus Printer Output Clear",
.name = "lpprinterclear",
.desc = "Clean up saved images in the output directory on startup",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
.title = "LovePlus Printer Output Overwrite",
.name = "lpprinteroverwrite",
.desc = "Always overwrite the same file in output directory",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
// LovePlusPrinterOutputFormat
.title = "LovePlus Printer Output Format",
.name = "lpprinterformat",
.desc = "File format for printer output",
.type = OptionType::Text,
.setting_name = "(png/bmp/tga/jpg)",
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
.title = "LovePlus Printer JPG Quality",
.name = "lpprinterjpgquality",
.desc = "Quality setting in percent if JPG format is used",
.type = OptionType::Integer,
.setting_name = "(0-100)",
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
// OptionConflictResolution
.title = "Command Line Args Override",
.name = "cmdoverride",
.desc = "By default, option values in spicecfg take precedence over command-line args, for legacy compat.\n"
"When this is specified in command line, command-line args take precedence instead",
.type = OptionType::Bool,
.category = "Development",
.disabled = true,
}
};
@@ -2197,7 +2470,7 @@ std::unique_ptr<std::vector<Option>> launcher::parse_options(int argc, char *arg
} else {
// validate it is an integer
try {
std::stoull(argv[i], nullptr, 16);
auto _ = std::stoull(argv[i], nullptr, 16);
} catch (const std::exception &ex) {
log_fatal("options", "parameter for -{} is not a hex number: {}", alias, argv[i]);
}
@@ -2298,12 +2571,25 @@ std::vector<Option> launcher::merge_options(
auto &new_option = merged.emplace_back(option.get_definition(), "");
new_option.disabled = true;
for (auto &value : option.values()) {
new_option.value_add(value);
}
for (auto &value : override.values()) {
new_option.value_add(value);
if (USE_CMD_OVERRIDE) {
// command-line arguments take precedence (opt-in)
for (auto &value : override.values()) {
new_option.value_add(value);
}
for (auto &value : option.values()) {
new_option.value_add(value);
}
} else {
// spicecfg options take precedence (default)
// this sucks, but it's the default for legacy spicetools compat
for (auto &value : option.values()) {
new_option.value_add(value);
}
for (auto &value : override.values()) {
new_option.value_add(value);
}
}
new_option.conflicting = true;
} else {
auto &new_option = merged.emplace_back(override.get_definition(), "");
new_option.disabled = true;
@@ -2319,7 +2605,6 @@ std::vector<Option> launcher::merge_options(
}
}
}
return merged;
}
+26
View File
@@ -30,6 +30,7 @@ namespace launcher {
GraphicsForceSingleAdapter,
GraphicsForceRefresh,
FullscreenResolution,
FullscreenOrientationFlip,
Graphics9On12,
spice2x_Dx9On12,
NoLegacy,
@@ -85,6 +86,7 @@ namespace launcher {
spice2x_SDVXSubRedraw,
LoadDDRModule,
DDR43Mode,
DDRSkipCodecRegisteration,
LoadPopnMusicModule,
PopnMusicForceHDMode,
PopnMusicForceSDMode,
@@ -116,6 +118,7 @@ namespace launcher {
LoadCCJModule,
LoadQKSModule,
LoadMFGModule,
LoadPCModule,
LoadMusecaModule,
PathToModules,
ScreenshotFolder,
@@ -135,6 +138,8 @@ namespace launcher {
APISerialBaud,
APIPretty,
APIDebugMode,
APIScreenMirrorQuality,
APIScreenMirrorDivide,
EnableAllIOModules,
EnableACIOModule,
EnableICCAModule,
@@ -183,6 +188,7 @@ namespace launcher {
DebugCreateFile,
VerboseGraphicsLogging,
VerboseAVSLogging,
AllowEA3Verbose,
DisableColoredOutput,
DisableACPHook,
DisableSignalHandling,
@@ -198,6 +204,9 @@ namespace launcher {
MFGArgs,
MFGCabType,
MFGNoIO,
PCArgs,
PCNoIO,
PCKnobMode,
spice2x_LightsOverallBrightness,
spice2x_WindowBorder,
spice2x_WindowSize,
@@ -206,6 +215,8 @@ namespace launcher {
WindowForceScaling,
spice2x_IIDXWindowedSubscreenSize,
spice2x_IIDXWindowedSubscreenPosition,
IIDXWindowedSubscreenBorderless,
IIDXWindowedSubscreenAlwaysOnTop,
spice2x_JubeatLegacyTouch,
spice2x_RBTouchScale,
spice2x_AsioForceUnload,
@@ -222,13 +233,26 @@ namespace launcher {
spice2x_NoNVAPI,
spice2x_NoD3D9DeviceHook,
spice2x_SDVXNoSub,
SDVXFullscreenLandscape,
spice2x_EnableSMXStage,
spice2x_EnableSMXDedicab,
IIDXRecQuality,
MidiAlgoVer,
MidiNoteSustain,
InputRequiresFocus,
NostalgiaPoke,
ForceBackBufferCount,
SDVXWindowedSubscreenSize,
SDVXWindowedSubscreenPosition,
SDVXWindowedSubscreenBorderless,
SDVXWindowedSubscreenAlwaysOnTop,
LovePlusCamEnable,
LovePlusPrinterOutputPath,
LovePlusPrinterOutputClear,
LovePlusPrinterOutputOverwrite,
LovePlusPrinterOutputFormat,
LovePlusPrinterJPGQuality,
OptionConflictResolution,
};
enum class OptionsCategory {
@@ -240,6 +264,8 @@ namespace launcher {
};
}
extern bool USE_CMD_OVERRIDE;
const std::vector<std::string> &get_categories(Options::OptionsCategory category);
const std::vector<OptionDefinition> &get_option_definitions();
std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]);
+2
View File
@@ -40,6 +40,8 @@ namespace richpresence {
{"Busou Shinki: Armored Princess Battle Conductor", "1226739666741366916"},
{"Chase Chase Jokers", "1226739863915593770"},
{"QuizKnock STADIUM", "1226739930328334478"},
{"Mahjong Fight Girl", "1417371188342030468"},
{"Polaris Chord", "1417371592224407552"},
};
// state
+56 -1
View File
@@ -1,11 +1,13 @@
#include "signal.h"
#include <exception>
#include <string>
#include <windows.h>
#include <dbghelp.h>
#include "avs/core.h"
#include "avs/game.h"
#include "acio/acio.h"
#include "external/stackwalker/stackwalker.h"
#include "hooks/libraryhook.h"
#include "launcher/shutdown.h"
@@ -28,6 +30,11 @@ namespace launcher::signal {
// settings
bool DISABLE = false;
bool USE_VEH_WORKAROUND = false;
// states
bool SUPERSTEP_SOUND_ERROR = false;
bool AVS_DIR_CREATION_FAILURE = false;
std::string AVS_SRC_PATH;
}
#define V(variant) case variant: return #variant
@@ -97,6 +104,54 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
// print signal
log_warning("signal", "exception raised: {}", exception_code(ExceptionRecord));
std::string err;
switch (ExceptionRecord->ExceptionCode) {
case EXCEPTION_ILLEGAL_INSTRUCTION:
err = "Illegal instruction: either your CPU is too old (e.g., does not support "
"SSE4.2 or AVX2 but perhaps the game requires it); or, a bad patch was applied.";
break;
default:
break;
}
if (!err.empty()) {
log_warning(
"signal",
"likely cause for your error based on the exception code:\n {}",
err.c_str());
}
// check ACIO init failures
if (acio::IO_INIT_IN_PROGRESS) {
log_warning(
"signal",
"exception raised during ACIO init, this usually happens when a third party application interferes with hooks");
log_warning(
"signal",
" please check for the following, disable them, and try launching the game again:");
log_warning(
"signal",
" RivaTuner Statistics Server (RTSS), MSI Afterburner, kernel mode anti-cheat");
}
if (launcher::signal::SUPERSTEP_SOUND_ERROR) {
log_warning("signal", "audio initialization error was previously detected during boot!");
log_warning("signal", " (W:SuperstepSound: Audio device is not available!!!)");
log_warning("signal", " this crash is most likely related to audio init failure");
log_warning("signal", " fix your audio device, double check your audio options/patches, and try again");
}
if (launcher::signal::AVS_DIR_CREATION_FAILURE) {
log_warning("signal",
"AVS filesystem initialization failure was previously detected during boot!");
log_warning("signal",
" ERROR: directory could not be created: {}",
launcher::signal::AVS_SRC_PATH.c_str());
log_warning("signal",
" this crash may have been caused by bad <mounttable> contents in {}",
avs::core::CFG_PATH.c_str());
log_warning("signal", " fix the XML file and try again");
}
// walk the exception chain
struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord;
while (record_cause != nullptr) {
+6
View File
@@ -1,10 +1,16 @@
#pragma once
#include <string>
namespace launcher::signal {
// settings
extern bool DISABLE;
extern bool USE_VEH_WORKAROUND;
extern bool SUPERSTEP_SOUND_ERROR;
extern bool AVS_DIR_CREATION_FAILURE;
extern std::string AVS_XML_PATH;
extern std::string AVS_SRC_PATH;
//void print_stacktrace();
void attach();

Some files were not shown because too many files have changed in this diff Show More