Compare commits

...

58 Commits
5.31 ... 5.33

Author SHA1 Message Date
icex2
e6766d4a3b Update readme with supported games, iidx27 2020-10-27 20:10:10 +01:00
icex2
843d78bc4b Update changelog for 5.33 release 2020-10-27 19:51:48 +01:00
Will Xyen
0792b29f3d iidxhook9: Add iidxhook support for versions that use dx9ex and some other features 2020-10-24 12:17:55 -07:00
Will Xyen
e1d4810743 mempatch-hook: fix CRLF 2020-10-24 11:15:58 -07:00
Will Xyen
2110abc269 [avs] add avs_is_active import 2020-10-23 17:31:49 -07:00
icex2
fc22f76988 doc: Add missing supported iidx version to iidxhook8 2020-10-20 23:45:19 +02:00
Will Xyen
3d4df4cd64 [memfile] Add support for faking files in memory 2020-10-18 14:46:34 -07:00
Will Xyen
be5a773cb7 [iohook] Fix iohook_init race condition 2020-10-18 14:45:49 -07:00
Will Xyen
3ed6ac6290 [tools] Add x64 mempatch-hook 2020-10-06 20:01:41 -07:00
icex2
e49149b03d Update README.md, add pipeline status 2020-10-03 20:38:05 +00:00
Will Xyen
af507e6dcb vigem-sdvxio: Remove unused header 2020-09-27 18:59:24 -07:00
Will Xyen
a9ebc30ea7 vigem-sdvxio: Add relative mode and address comments 2020-09-26 22:54:12 -07:00
Will Xyen
4f3135439d vigem-sdvxio: Add readme 2020-09-13 20:16:37 -07:00
Will Xyen
2d1c09d4e6 sdvxhook: code-format 2020-09-13 19:46:16 -07:00
Will Xyen
164d56b6eb vigem-sdvxio: Add implementation 2020-09-13 19:45:52 -07:00
Will Xyen
1712f10f66 minor bugfixes 2020-09-12 07:53:00 -07:00
Will T
128496901e Add jbiotest for testing jubeat IO impls 2020-09-12 09:12:35 +00:00
icex2
65a45740d2 launcher: Log OS version on startup 2020-09-02 22:04:55 +02:00
icex2
8bd0f44e24 inject: Log OS version on startup 2020-09-02 22:04:55 +02:00
icex2
7733912ae2 util: Add OS module to get version information of Windows
Data we always need when things go wrong, log it on boot.
2020-09-02 22:04:53 +02:00
icex2
55c9f5a521 hooks for inject: Fix log output in DllMain
With the new inject tool, the output of OutputDebugString is
available on console and file logging.
2020-09-02 22:03:26 +02:00
icex2
917dc88826 util/log: Remove log_error, replace occurances with log_warning
log_error was using the log_writer_fatal impl which conflicts with
how this is used on AVS. Therefore, achieve a visible "error"
message in inject by using log_warning + ERROR string in the message
2020-09-02 19:53:07 +00:00
icex2
475d438ec7 inject/debugger: Avoid code dupe 2020-09-02 19:53:07 +00:00
icex2
f23d3531a2 util/signal: Expose signal_exception_code_to_str
Can be re-used in inject/debugger to avoid code duplication
2020-09-02 19:53:07 +00:00
icex2
32c3428c59 inject/logger: Add timestamps to log messages 2020-09-02 19:53:07 +00:00
icex2
ee034a1f08 inject: Fix windows psapi mess by explicitly defining version
Otherwise, the compiler does not link the correct version of that
API. See the requirements section at the bottom of the page here:
https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmodulebasenamea
2020-09-02 19:53:06 +00:00
icex2
54b321c7d9 inject: Major refactoring
This addresses one major issue and multiple minor ones:
Major:
Inject's debugger is not attached to the process before
injecting DLL files. This misses out on OutputDebugString
calls by anything logging in the DllMain functions of the hook
dlls.

Minor:
- Fix coloring of log entries
- Add ASCII header to easily determine start
- Fix file logging, log _everything_ to a single log file
- Enhance inject's debugger: log further debug events to incrase
visibility on issues, proper exception handling for inject
- Re-iterated code structure of inject
2020-09-02 19:53:06 +00:00
icex2
de894ae5ce util/log: Add log_error which logs errors but does not abort
This is required for tools/applications that want or have to fail
gracefully but still need to highlight the severity in the log.
2020-09-02 19:53:06 +00:00
icex2
1d25abc10b util: Add signal module introducing signal and exception handling
Right now, this prints to the console using the util/log logger
only. Stacktrace on exception still needs to be added.
2020-09-02 19:53:05 +00:00
icex2
beea9e7bb7 util/log: Add TODO pointing out design flaw 2020-09-02 19:53:05 +00:00
icex2
fbb69d4550 Makefile: Fix minor inconsistency 2020-09-02 19:53:05 +00:00
icex2
e0fe9bcf94 Makefile: Fix clang format command 2020-09-02 19:53:05 +00:00
icex2
f9c0733af3 Adapt everything in BT5 to new capnhook API
This includes:
- Renaming of functions (iohook_invoke_next)
- Slight interface changes (com_proxy_wrap, iohook_push_handler)
- De-duplicate stuff from utils (iobuf, hr)
2020-09-02 19:35:15 +00:00
icex2
a016f27652 hook: Update capnhook source files from latest master
https://github.com/decafcode/capnhook
2020-09-02 19:35:15 +00:00
Will Xyen
3a1005f1f8 sdvxio: Add atomics to kfca/bio2 2020-08-16 20:58:12 -07:00
Will Xyen
f1700ca551 sdvxio-bio2: Add better comments to amp command, and sleep in fini 2020-08-15 11:37:03 -07:00
Will Xyen
60b081b01c Merge branch 'sdvxio-bio2' of dev.s-ul.eu:djhackers/bemanitools into sdvxio-bio2 2020-08-14 19:11:57 -07:00
Will Xyen
89989ae49c doc: add documentation for sdvxio-bio2 2020-08-15 02:11:45 +00:00
Will Xyen
ff1b9ff9a2 sdvxio-bio2: Add sdvxio BIO2 along with bio2drv and aciotest updates 2020-08-15 02:11:45 +00:00
Will Xyen
4c0f60fc93 sdvx: Allow setting digital amp level from sdvxio
For SDVX 1-4: passes amp values through as-is

For SDVX5: ignores the amp volume control (as the game uses windows volume controls, which will work without any hooks)
2020-08-15 02:11:45 +00:00
Will Xyen
caba39843f bio2: bio2_bi2a_state -> bi2a_sdvx_state 2020-08-15 02:11:45 +00:00
Will Xyen
f028bdeb20 iidxhook-util: allow setting specified ICCA emulation version 2020-08-15 02:11:44 +00:00
Will Xyen
bfbb7103ef bio2emu: refactor BIO2 emulation 2020-08-15 02:11:44 +00:00
Will Xyen
95e073271f doc: add documentation for sdvxio-bio2 2020-08-14 19:05:44 -07:00
Will Xyen
cdefc2b09e sdvxio-bio2: Add sdvxio BIO2 along with bio2drv and aciotest updates 2020-08-14 18:44:05 -07:00
Will Xyen
9246e7f9e0 sdvx: Allow setting digital amp level from sdvxio
For SDVX 1-4: passes amp values through as-is

For SDVX5: ignores the amp volume control (as the game uses windows volume controls, which will work without any hooks)
2020-08-14 04:38:38 -07:00
icex2
bbb5157460 Version bump 5.33 2020-08-13 13:15:41 +02:00
icex2
e0cfc8c948 Bump to version 5.32 and update changelog 2020-08-13 13:14:54 +02:00
Will Xyen
69ef963451 Merge branch 'master' of dev.s-ul.eu:djhackers/bemanitools 2020-08-12 12:31:14 -07:00
icex2
93cfb591b7 iidxio-ezusb: Update documentation 2020-08-12 18:01:22 +00:00
icex2
3396c09fbf ezusb-iidx: Add macro to enable D01 board emulation
Used for dumping firmware, only. C02 and D01 boards only differ
in the FPGA firmware written to the board on boot up. This doesn't
matter for emulation, but when using a real IO board with
iidxio-ezusb.dll, a different FPGA firmware is required to be
flashed in order to get working turntables, top unit lights etc
2020-08-12 18:01:22 +00:00
icex2
bd8dc18b30 iidxio-ezusb: Support coin mech input 2020-08-11 22:05:46 +02:00
Will Xyen
86517bd001 bio2: bio2_bi2a_state -> bi2a_sdvx_state 2020-08-02 23:16:28 -07:00
Will Xyen
172c83a854 iidxhook-util: allow setting specified ICCA emulation version 2020-08-02 23:03:04 -07:00
Will Xyen
c7137659bc bio2emu: refactor BIO2 emulation 2020-08-02 23:02:13 -07:00
Will Xyen
8e9abf9906 launcher: Set soft_id_code before calling init as some games use it then 2020-07-24 08:47:25 -07:00
Will Xyen
62bb717582 hdxs: Is really an HDXB 1.0.1 apparently
Also update enum hdxs_rgb_light_idx to start from 0 since that's what the function does
2020-07-10 11:06:13 -07:00
Will Xyen
4816fb809d launcher: Use more descriptive error message for ERROR_MOD_NOT_FOUND 2020-07-06 18:03:25 +00:00
178 changed files with 7902 additions and 1767 deletions

View File

@@ -1,4 +1,13 @@
# Release history
## 5.33
* iidxhook9: Support for IIDX27
* sdvxio-bio2: Driver for real sdvx5 cabinet IO hardware
* vigem-sdvxio: Tool to allow using sdvxio (kfca or bio2) to emulate an xbox controller
* Various bugfixes and refactoring for inject and launcher
## 5.32
* Various bugfixes
## 5.31
* DDR: Fix p3io and extio lights not working / being swapped when using geninput
* DDR: Add HDXS light support

View File

@@ -26,7 +26,7 @@ toolchain_64 := x86_64-w64-mingw32-
gitrev := $(shell git rev-parse HEAD)
cppflags := -I src -I src/main -I src/test -DGITREV=$(gitrev)
cflags := -O2 -pipe -ffunction-sections -fdata-sections \
-Wall -std=c99
-Wall -std=c99 -DPSAPI_VERSION=1
cflags_release := -Werror
ldflags := -Wl,--gc-sections -static-libgcc
@@ -58,27 +58,26 @@ print-building \
version
print-building:
@echo "Build gitrev "$(gitrev)"..."
$(V)echo "Build gitrev "$(gitrev)"..."
print-release:
@echo "Starting release build..."
$(V)echo "Starting release build..."
clean:
@echo "Cleaning up..."
$(V)echo "Cleaning up..."
$(V)rm -rf $(BUILDDIR)
code-format:
@echo "Applying clang-format..."
# -style=file enables reading .clang-format
@find src/ -iname *.h -o -iname *.c | xargs clang-format -i -style=file
$(V)echo "Applying clang-format..."
$(V)find src/ -name '*.c' -o -name '*.h' | xargs clang-format -i -style=file
run-tests:
@echo "Running tests..."
@./run-tests-wine.sh
$(V)echo "Running tests..."
$(V)./run-tests-wine.sh
# Generate a version file to identify the build
version:
@echo "$(gitrev)" > version
$(V)echo "$(gitrev)" > version
build-docker:
$(V)docker rm -f $(docker_container_name) 2> /dev/null || true

View File

@@ -78,7 +78,9 @@ include src/main/aciodrv/Module.mk
include src/main/acioemu/Module.mk
include src/main/aciotest/Module.mk
include src/main/asio/Module.mk
include src/main/bio2drv/Module.mk
include src/main/bio2emu/Module.mk
include src/main/bio2emu-iidx/Module.mk
include src/main/bsthook/Module.mk
include src/main/bstio/Module.mk
include src/main/camhook/Module.mk
@@ -121,12 +123,14 @@ include src/main/iidxhook5/Module.mk
include src/main/iidxhook6/Module.mk
include src/main/iidxhook7/Module.mk
include src/main/iidxhook8/Module.mk
include src/main/iidxhook9/Module.mk
include src/main/iidxio/Module.mk
include src/main/iidxio-ezusb/Module.mk
include src/main/iidxio-ezusb2/Module.mk
include src/main/iidxiotest/Module.mk
include src/main/inject/Module.mk
include src/main/jbio/Module.mk
include src/main/jbiotest/Module.mk
include src/main/jbhook/Module.mk
include src/main/jbhook1/Module.mk
include src/main/launcher/Module.mk
@@ -141,10 +145,13 @@ include src/main/sdvxhook2/Module.mk
include src/main/sdvxhook2-cn/Module.mk
include src/main/sdvxio/Module.mk
include src/main/sdvxio-kfca/Module.mk
include src/main/sdvxio-bio2/Module.mk
include src/main/security/Module.mk
include src/main/unicorntail/Module.mk
include src/main/util/Module.mk
include src/main/vefxio/Module.mk
include src/main/vigem-sdvxio/Module.mk
include src/main/vigemstub/Module.mk
include src/test/cconfig/Module.mk
include src/test/d3d9hook/Module.mk
@@ -171,6 +178,7 @@ $(zipdir)/tools.zip: \
build/bin/indep-32/iidxiotest.exe \
build/bin/indep-32/iidx-ezusb-exit-hook.dll \
build/bin/indep-32/iidx-ezusb2-exit-hook.dll \
build/bin/indep-32/jbiotest.exe \
build/bin/indep-32/pcbidgen.exe \
dist/iidx/ezusb-boot.bat \
dist/iidx/ezusb2-boot.bat \
@@ -188,6 +196,8 @@ $(zipdir)/tools-x64.zip: \
build/bin/indep-64/iidxiotest.exe \
build/bin/indep-64/iidx-ezusb-exit-hook.dll \
build/bin/indep-64/iidx-ezusb2-exit-hook.dll \
build/bin/indep-64/jbiotest.exe \
build/bin/indep-64/mempatch-hook.dll \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -j $@ $^
@@ -196,11 +206,8 @@ $(zipdir)/iidx-09-to-12.zip: \
build/bin/indep-32/iidxhook1.dll \
build/bin/indep-32/config.exe \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/iidxio.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/vefxio.dll \
build/bin/indep-32/inject.exe \
dist/iidx/config.bat \
@@ -224,11 +231,8 @@ $(zipdir)/iidx-13.zip: \
build/bin/avs2_0-32/iidxhook2.dll \
build/bin/indep-32/config.exe \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/iidxio.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/vefxio.dll \
build/bin/indep-32/inject.exe \
dist/iidx/config.bat \
@@ -243,11 +247,8 @@ $(zipdir)/iidx-14-to-17.zip: \
build/bin/avs2_0-32/iidxhook3.dll \
build/bin/indep-32/config.exe \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/iidxio.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/vefxio.dll \
build/bin/indep-32/inject.exe \
dist/iidx/config.bat \
@@ -269,11 +270,8 @@ $(zipdir)/iidx-18.zip: \
build/bin/avs2_1101-32/launcher.exe \
build/bin/indep-32/config.exe \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/iidxio.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/vefxio.dll \
dist/iidx/config.bat \
dist/iidx/gamestart-18.bat \
@@ -288,11 +286,8 @@ $(zipdir)/iidx-19.zip: \
build/bin/avs2_1304-32/launcher.exe \
build/bin/indep-32/config.exe \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/iidxio.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/vefxio.dll \
dist/iidx/config.bat \
dist/iidx/gamestart-19.bat \
@@ -307,11 +302,8 @@ $(zipdir)/iidx-20.zip: \
build/bin/avs2_1508-32/launcher.exe \
build/bin/indep-32/config.exe \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/iidxio.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/vefxio.dll \
dist/iidx/config.bat \
dist/iidx/gamestart-20.bat \
@@ -326,11 +318,8 @@ $(zipdir)/iidx-21-to-24.zip: \
build/bin/avs2_1601-32/launcher.exe \
build/bin/indep-32/config.exe \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/iidxio.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/vefxio.dll \
dist/iidx/config.bat \
dist/iidx/gamestart-21.bat \
@@ -351,11 +340,8 @@ $(zipdir)/iidx-25-to-26.zip: \
build/bin/avs2_1700-64/launcher.exe \
build/bin/indep-64/config.exe \
build/bin/indep-64/eamio.dll \
build/bin/indep-64/eamio-icca.dll \
build/bin/indep-64/geninput.dll \
build/bin/indep-64/iidxio.dll \
build/bin/indep-64/iidxio-ezusb.dll \
build/bin/indep-64/iidxio-ezusb2.dll \
build/bin/indep-64/vefxio.dll \
dist/iidx/config.bat \
dist/iidx/gamestart-25.bat \
@@ -367,6 +353,38 @@ $(zipdir)/iidx-25-to-26.zip: \
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/iidx-27.zip: \
build/bin/avs2_1700-64/iidxhook9.dll \
build/bin/avs2_1700-64/launcher.exe \
build/bin/indep-64/config.exe \
build/bin/indep-64/eamio.dll \
build/bin/indep-64/geninput.dll \
build/bin/indep-64/iidxio.dll \
build/bin/indep-64/vefxio.dll \
dist/iidx/config.bat \
dist/iidx/gamestart-27.bat \
dist/iidx/iidxhook-27.conf \
dist/iidx/vefx.txt \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/iidx-hwio-x86.zip: \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/iidx-hwio-x64.zip: \
build/bin/indep-64/eamio-icca.dll \
build/bin/indep-64/iidxio-ezusb.dll \
build/bin/indep-64/iidxio-ezusb2.dll \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/jb-01.zip: \
build/bin/avs2_803-32/jbhook1.dll \
build/bin/indep-32/inject.exe \
@@ -414,7 +432,6 @@ $(zipdir)/sdvx-01-to-04.zip: \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/geninput.dll \
build/bin/indep-32/sdvxio.dll \
build/bin/indep-32/sdvxio-kfca.dll \
dist/sdvx/config.bat \
dist/sdvx/gamestart.bat \
| $(zipdir)/
@@ -428,7 +445,6 @@ $(zipdir)/sdvx-05.zip: \
build/bin/indep-64/eamio.dll \
build/bin/indep-64/geninput.dll \
build/bin/indep-64/sdvxio.dll \
build/bin/indep-64/sdvxio-kfca.dll \
dist/sdvx5/config.bat \
dist/sdvx5/gamestart.bat \
dist/sdvx5/sdvxhook2.conf \
@@ -450,6 +466,22 @@ $(zipdir)/sdvx-05-cn.zip: \
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/sdvx-hwio-x86.zip: \
build/bin/indep-32/sdvxio-kfca.dll \
build/bin/indep-32/sdvxio-bio2.dll \
build/bin/indep-32/vigem-sdvxio.exe \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/sdvx-hwio-x64.zip: \
build/bin/indep-64/sdvxio-kfca.dll \
build/bin/indep-64/sdvxio-bio2.dll \
build/bin/indep-64/vigem-sdvxio.exe \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/ddr-12-to-16.zip: \
build/bin/avs2_1508-32/launcher.exe \
build/bin/avs2_1508-32/ddrhook.dll \
@@ -548,12 +580,17 @@ $(BUILDDIR)/bemanitools.zip: \
$(zipdir)/iidx-20.zip \
$(zipdir)/iidx-21-to-24.zip \
$(zipdir)/iidx-25-to-26.zip \
$(zipdir)/iidx-27.zip \
$(zipdir)/iidx-hwio-x86.zip \
$(zipdir)/iidx-hwio-x64.zip \
$(zipdir)/jb-01.zip \
$(zipdir)/jb-05-to-07.zip \
$(zipdir)/jb-08.zip \
$(zipdir)/sdvx-01-to-04.zip \
$(zipdir)/sdvx-05.zip \
$(zipdir)/sdvx-05-cn.zip \
$(zipdir)/sdvx-hwio-x86.zip \
$(zipdir)/sdvx-hwio-x64.zip \
$(zipdir)/tools.zip \
$(zipdir)/tools-x64.zip \
CHANGELOG.md \

View File

@@ -1,7 +1,8 @@
# Bemanitools 5
[![pipeline status](https://dev.s-ul.eu/djhackers/bemanitools/badges/master/pipeline.svg)](https://dev.s-ul.eu/djhackers/bemanitools/commits/master)
Version: 5.31</br>
Version: 5.33
[Release history](CHANGELOG.md)
A collection of tools to run [various Bemani arcade games](#list-of-supported-games).
@@ -40,6 +41,7 @@ authentic gameplay experience, and easier support for a broader range of release
* Beatmania IIDX 24 SINOBUZ (iidx-21-to-24.zip) using [iidxhook7](doc/iidxhook/iidxhook7.md)
* Beatmania IIDX 25 CANNON BALLERS (iidx-25-to-26.zip) using [iidxhook8](doc/iidxhook/iidxhook8.md)
* Beatmania IIDX 26 Rootage (iidx-25-to-26.zip) using [iidxhook8](doc/iidxhook/iidxhook8.md)
* Beatmania IIDX 27 Heroic Verse (iidx-27.zip) using [iidxhook9](doc/iidxhook/iidxhook9.md)
* jubeat
* jubeat (experimental/buggy) (jb-01.zip) using [jbhook1](doc/jbhook1/jbhook1.md)
* jubeat saucer (fulfill) (jb-05-to-07.zip) using jbhook

16
dist/iidx/gamestart-27.bat vendored Normal file
View File

@@ -0,0 +1,16 @@
@echo off
cd /d %~dp0
if not exist dev mkdir dev
if not exist dev\e mkdir dev\e
if not exist dev\g mkdir dev\g
if not exist dev\nvram mkdir dev\nvram
if not exist dev\raw mkdir dev\raw
if not exist dev\raw\log mkdir dev\raw\log
if not exist dev\raw\fscache mkdir dev\raw\fscache
for /R prop\defaults %%D in (*.*) do (
if not exist dev\nvram\%%~nxD copy /y prop\defaults\%%~nxD dev\nvram
)
launcher -H 134217728 -B iidxhook9.dll bm2dx.dll --config iidxhook-27.conf %*

63
dist/iidx/iidxhook-27.conf vendored Normal file
View File

@@ -0,0 +1,63 @@
# Disable card reader emulation and enable usage of real card reader hardware on COM0 (for games supporting slotted readers)
io.disable_card_reader_emu=false
# Disable BIO2 emulation and enable usage of real BIO2 hardware
io.disable_bio2_emu=false
# Disables the poll limiter, warning very high CPU usage may arise
io.disable_poll_limiter=false
# Lightning cab mode (requires additional IO emulation)
io.lightning_mode=false
# Disable camera connection
io.disable_cams=false
# Disables the built in file hooks, requiring manual file creation
io.disable_file_hooks=false
# Disables the camera emulation
cam.disable_emu=false
# Override camera device ID detection (copy from device manager, do not escape)
cam.device_id1=
# Override camera device ID detection (copy from device manager, do not escape)
cam.device_id2=
# Run the game in a framed window (requires windowed option)
gfx.framed=true
# Run the game windowed
gfx.windowed=false
# Confine mouse coursor to window
gfx.confined=false
# Windowed width, -1 for default size
gfx.window_width=-1
# Windowed height, -1 for default size
gfx.window_height=-1
# Forced refresh rate, -1 to not force any (try 59 or 60 if monitor check fails to lock on high refresh rate monitors)
gfx.forced_refresh_rate=-1
# D3D9ex device adapter (monitor), -1 to use default, 0, 1, 2 etc. to use specified adapter
gfx.device_adapter=-1
# Orientation to force monitor into, -1 to use default, 0, 1, 2, 3 to do 0, 90, 180, 270 degrees
gfx.force_orientation=-1
# IP of adapter to force override with
adapter.override_ip=
# Force ASIO audio mode/device (if applicable / TDJ default)
asio.force_asio=false
# Force WASAPI audio mode (if applicable / LDJ default)
asio.force_wasapi=true
# The ASIO device name to use
asio.device_name=XONAR SOUND CARD(64)

View File

@@ -10,6 +10,9 @@ io.disable_poll_limiter=false
# Forces game to think headphones are attached
io.force_headphones=false
# Disables the built in file hooks, requiring manual file creation (/dev/raw/j.dest)
io.disable_file_hooks=false
# Run the game in a framed window (requires windowed option)
gfx.framed=true

View File

@@ -23,6 +23,7 @@ games:
* [iidxhook6](iidxhook6.md): Tricoro
* [iidxhook7](iidxhook7.md): SPADA, PENDUAL, copula, SINOBUZ
* [iidxhook8](iidxhook8.md): CANNON BALLERS, Rootage
* [iidxhook8](iidxhook9.md): Heroic Verse
When building kactools, independent packages are created for each set of games
which are ready to be dropped on top of vanilla AC data dumps. We recommend

View File

@@ -2,6 +2,7 @@
The following games are compatible with this version of iidxhook:
* CANNON BALLERS
* Rootage
The games must be bootstrapped using [launcher](../launcher.md).

179
doc/iidxhook/iidxhook9.md Normal file
View File

@@ -0,0 +1,179 @@
# Game list
The following games are compatible with this version of iidxhook:
* Heroic Verse
The games must be bootstrapped using [launcher](../launcher.md).
# Data setup and running the game
## Supported versions of Windows
This version requires at least Win 7 x64 and will not run, like the
former versions, on Win XP x86!
## Dependencies
Make sure to have the following dependencies installed:
* DirectX 9
* Visual C++ 2010 Redistributable Package (x64)
## Data setup
We assume that you are using a clean/vanilla data dump. Ensure your ("concents")
folder with your unpacked data looks like this:
- data
- modules
- prop
* Copy/Move all files from the *modules* directory to the root folder, so they
are located next to the *data* and *prop* folders.
* Copy all files from *prop/defaults* to the *prop* folder.
* Setup proper paths for *dev/nvram* and *dev/raw* in *prop/avs-config.xml* by
replacing the *<fs>*-block in that file with the following block:
```
<fs>
<root>
<device __type="str">.</device>
</root>
<mounttable>
<vfs name="boot" fstype="fs" src="dev/raw" dst="/dev/raw" opt="vf=1,posix=1"/>
<vfs name="boot" fstype="fs" src="dev/nvram" dst="/dev/nvram" opt="vf=0,posix=1"/>
</mounttable>
<nr_mountpoint __type="u16">256</nr_mountpoint>
<nr_filedesc __type="u16">256</nr_filedesc>
</fs>
```
* Unpack the package containing iidxhook9 into the root folder so iidxhook9.dll
and all other files are located in the same folder as *data*, *prop*,
*bm2dx.dll*, etc.
* Run the gamestart-XX.bat file as admin. Where XX matches the version you
want to run.
* Note: if you know what you're doing and would like to keep all the dlls in the
*modules* folder then you extract iidxhook9 into that directory, move
gamestart-XX.bat up, and edit the paths.
# Configuring iidxhook
The hook library can be configured via cmd arguments or a configuration file.
The latter is generated (*iidxhook-XX.conf* in the same directory) on the first
start of the game using the gamestart-XX.bat file (again, XX matches your target
game version). It contains default values for all available parameters and
comments explaining each parameter. Please follow the comments when configuring
your setup.
Add the argument *-h* when running gamestart-XX.bat
(e.g. *gamestart-XX.bat -h*) to print help/usage information with a list of
all available parameters. Every parameter can be either set as command line
argument or using a configuration file.
To set a parameter from the command line, just add it as an argument after
the bat file like this
```
gamestart-09.bat -p gfx.windowed=true -p gfx.framed=true
```
The syntax for the "key=value" is the same as in the config file. Make sure
to have a pre-ceeding "-p" for every parameter added.
However, if a parameter is specifed in the configuration file and as a command
line argument, the command line argument overrides the config file's value.
# Eamuse network setup
If you want to run the games online, you need a valid PCBID and the service URL.
Open *prop/ea3-config.xml* and set the values of the *ea3/id/pcbid* and
*ea3/network/services* nodes accordingly.
Run the game with the gamestart-XX.bat file and enable network on the operator
menu. When enabled, the game seems to hang and expects you to power
cycle the machine (i.e. quit the game and restart it).
# Real hardware support
### BIO2 hardware
Set the *io.disable_bio2_emu* configuration value to *1* to disable BIO2
emulation to run the game using real BIO2 hardware.
### Ezusb and other
Use the specific iidxio API implementations, e.g. iidxio-ezusb2.dll to use
the IO2 EZUSB board, to run the game on real hardware. Thanks to a common
abstraction layer, you can also use custom IO boards or whatever Konami hardware
is going to be available in the future. Obviously, someone has to write a
driver, first.
## Slotted/Wave pass card readers
Replace the default *eamio.dll* with the *eamio-icca.dll* and have either your
slotted (IIDX, DDR Supernova or GF/DM type) or new wave pass card readers
conencted and and assigned to *COM1*.
### ICCA device settings (device manager)
* Port: COM1
* BAUD rate: 57600
* Data bits: 8
* Parity: None
* Stop bits: 1
* Flow control: None
If you encounter issues after the game opened the device, e.g. application
stuck, try a USB <-> COM dongle instead of using one of the COM ports of the
mainboard.
# Troubleshooting and FAQ
## The game does not run "well" (frame drops, drifting offsync etc)
This can be related to various issues:
* Make sure to run the game as (true) Administrator especially on Windows 7 and
newer. This will also get rid of various other errors (see below) that are
related to permission issues.
* Run the game's process with a higher priority:
```
start "" /relatime "gamestart.bat"
```
* Enforce v-sync enabled in your GPU settings.
* Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx)
that is not jumping around. Use the timebase feature of one of the newer games to
check that or enable iidxhook's timebase and check the log output for the
determined value. Run this a few times and check if the results differ.
## "NETWORK WARNING" instead of "NETWORK OK"
This can be caused by:
* Invalid PCBID
* Firewall blocking connections
* Invalid eamuse url or port specified
* Game is not run using the Administrator account
Make sure to check these things first
## My songs are offsync
From IIDX 20 (or Lincle very final revision) onwards, the game comes with
a built-in auto timebase option ("monitor check" on startup) which
dynamically, detects the refresh rate of your current setup. Thus, BT5's
timebase option is not included from this hook version onwards, anymore.
Ensure that refresh rate displayed is very stable, e.g. 60.00x hz, and the
game should be able to provide you with a smooth and sync game experience.
## My game runs too fast
iidxhook can limit the frame rate for you (refer to help/config file)
## My game crashes when I try fullscreen
Use dxwnd and set settings like "Acquire admin caps" and "Fullscreen only"
## Background videos aren't working. When starting a song, windows is playing the error sound and a message box appears
You are missing a codec to decode and play the videos. There are different
methods available to get background videos working. Probably, the easiest
solution: grab the CLVSD.ax file and go to Start -> Run -> regsvr32 clvsd.ax
Make sure to run cmd.exe as Administrator, otherwise you will get errors caused
by invalid permissions.
## I installed the CLVSD.ax codec but the game crashes or displays a message box that tells me to disable my debugger
If songs keep crashing upon start and you get an error message that says
```
DirectShow Texture3D Sample
Could not create source filter to graph! hr=0x80040266
```
despite having the codec (CLVSD.ax) installed, remove the debug flag (*-D*)
from gamestart or use a CLVSD.ax codec which has the debugger checks removed.

View File

@@ -1,4 +1,4 @@
This library drives a "legacy" ezusb IO board, also known as C02 IO, and
This library drives a "legacy" ezusb IO board, also known as C02/D01 IO, and
implements the iidxio API of BT5. Thus, it allows you to use this IO board with
*any* version of IIDX that is supported by BT5.
@@ -24,5 +24,7 @@ the status LEDs on the side of the board should show a blinking pattern.
dump to the FPGA.
* There is a script called ezusb-boot.bat which combines the two steps above
and can be integrated into the startup process of a dedicated setup.
* If you are using a D01 IO board, use the `fpga-d01.bin` file instead of the
`fpga.bin` file.
* If you ignore these steps, you will either run into errors or parts of the
IO board won't work (e.g. lights).

View File

@@ -0,0 +1,18 @@
This library talks to a BIO2 BI2A node and implements the sdvxio API of BT5.
Thus, it allows you to use a BIO2 SDVX device board with *any* version of SDVX that is supported by BT5.
# Setup
* Make sure that your BIO2 is flashed with a SDVX firmware (if you can boot SDVX5 with it, it's already the right firmware)
* The firmware persists between boots, so as long as it's flashed to the correct firmware, you no longer need to do this step
* Rename sdvxio-kfca.dll to sdvxio.dll.
* Ensure that your gamestart.bat actually injects the appropriate sdvxhook dll
for example:
```
launcher -K sdvxhook.dll soundvoltex.dll ...*
```
or that the application otherwise uses sdvxio.dll
* sdvxio-bio2 implements BIO2 device autodetection
* if this fails please make a bug report, and set the port manually in the config
* You can change the port and baudrate by editing the sdvxio-bio2.conf that should be created by default
* Please connect any additional acio devices to their own port (ex: SDVX5 expects an ICCA by itself on COM2)

View File

@@ -0,0 +1,24 @@
This application allows you to use any sdvxio backend, e.g. `sdvxio-kfca.dll`, to be available as a XBOX 360 game controller on windows.
Thus, it allows you to use a real cab with *any* game that supports xinput.
# Setup
* Install [ViGEmBus](https://github.com/ViGEm/ViGEmBus/releases)
* Place the following in the same folder as vigem-sdvxio:
* Get a copy of [ViGEmClient.dll](https://bin.jvnv.net/file/ZgMJK/ViGEmClient.zip) (or from bemanitools-supplements)
* Rename your corresponding `sdvxio-XXX.dll`, e.g. `sdvxio-kfca.dll`, to `sdvxio.dll`.
* Run `vigem-sdvxio.exe` so that the config file gets created
* Edit `vigem-sdvxio.conf` so that the config file gets created as needed
# Usage
* Run `vigem-sdvxio.exe`
* To quit the program, hit the TEST + SERVICE button at the same time
# Mapping
* BT ABCD are mapped to ABXY
* FX LR are mapped to LB/RB
* VOL LR are mapped to L thumbstick X/Y (either in absolute or relative mode depending on the config)
# Additional Notes For Cabinets (Running on embedded Windows 7):
* Make sure that you follow the instructions exactly from the release page ([Prerequisites for Windows 7](https://github.com/ViGEm/ViGEmBus/wiki/Prerequisites-for-Windows-7))
* If you get an error while trying to install KB3033929, re-enable windows update
* Make sure to ewfmgr C: -commit and reboot after installing the drivers (this only needs to be done once)

3
doc/tools/jbiotest.md Normal file
View File

@@ -0,0 +1,3 @@
Testing tool for development of jbio libraries used for emulating the main io
hardware on bemanitools for jubeat. Just place jbiotest.exe and your
custom jbio.dll in the same folder and run jbiotest.exe.

519
src/imports/ViGEm/Client.h Normal file
View File

@@ -0,0 +1,519 @@
/*
MIT License
Copyright (c) 2017-2019 Nefarius Software Solutions e.U. and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef ViGEmClient_h__
#define ViGEmClient_h__
#ifdef __cplusplus
extern "C" {
#endif
#include "ViGEm/Common.h"
#ifdef VIGEM_DYNAMIC
#ifdef VIGEM_EXPORTS
#define VIGEM_API __declspec(dllexport)
#else
#define VIGEM_API __declspec(dllimport)
#endif
#else
#define VIGEM_API
#endif
/**
* \typedef enum _VIGEM_ERRORS
*
* \brief Defines an alias representing the ViGEm errors.
*/
typedef enum _VIGEM_ERRORS {
VIGEM_ERROR_NONE = 0x20000000,
VIGEM_ERROR_BUS_NOT_FOUND = 0xE0000001,
VIGEM_ERROR_NO_FREE_SLOT = 0xE0000002,
VIGEM_ERROR_INVALID_TARGET = 0xE0000003,
VIGEM_ERROR_REMOVAL_FAILED = 0xE0000004,
VIGEM_ERROR_ALREADY_CONNECTED = 0xE0000005,
VIGEM_ERROR_TARGET_UNINITIALIZED = 0xE0000006,
VIGEM_ERROR_TARGET_NOT_PLUGGED_IN = 0xE0000007,
VIGEM_ERROR_BUS_VERSION_MISMATCH = 0xE0000008,
VIGEM_ERROR_BUS_ACCESS_FAILED = 0xE0000009,
VIGEM_ERROR_CALLBACK_ALREADY_REGISTERED = 0xE0000010,
VIGEM_ERROR_CALLBACK_NOT_FOUND = 0xE0000011,
VIGEM_ERROR_BUS_ALREADY_CONNECTED = 0xE0000012,
VIGEM_ERROR_BUS_INVALID_HANDLE = 0xE0000013,
VIGEM_ERROR_XUSB_USERINDEX_OUT_OF_RANGE = 0xE0000014,
VIGEM_ERROR_INVALID_PARAMETER = 0xE0000015
} VIGEM_ERROR;
/**
* \def VIGEM_SUCCESS(_val_) (_val_ == VIGEM_ERROR_NONE);
*
* \brief A macro that defines success.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param _val_ The VIGEM_ERROR value.
*/
#define VIGEM_SUCCESS(_val_) (_val_ == VIGEM_ERROR_NONE)
/**
* \typedef struct _VIGEM_CLIENT_T *PVIGEM_CLIENT
*
* \brief Defines an alias representing a driver connection object.
*/
typedef struct _VIGEM_CLIENT_T *PVIGEM_CLIENT;
/**
* \typedef struct _VIGEM_TARGET_T *PVIGEM_TARGET
*
* \brief Defines an alias representing a target device object.
*/
typedef struct _VIGEM_TARGET_T *PVIGEM_TARGET;
typedef _Function_class_(EVT_VIGEM_TARGET_ADD_RESULT) VOID CALLBACK
EVT_VIGEM_TARGET_ADD_RESULT(
PVIGEM_CLIENT Client, PVIGEM_TARGET Target, VIGEM_ERROR Result);
typedef EVT_VIGEM_TARGET_ADD_RESULT *PFN_VIGEM_TARGET_ADD_RESULT;
typedef _Function_class_(EVT_VIGEM_X360_NOTIFICATION) VOID CALLBACK
EVT_VIGEM_X360_NOTIFICATION(
PVIGEM_CLIENT Client,
PVIGEM_TARGET Target,
UCHAR LargeMotor,
UCHAR SmallMotor,
UCHAR LedNumber,
LPVOID UserData);
typedef EVT_VIGEM_X360_NOTIFICATION *PFN_VIGEM_X360_NOTIFICATION;
typedef _Function_class_(EVT_VIGEM_DS4_NOTIFICATION) VOID CALLBACK
EVT_VIGEM_DS4_NOTIFICATION(
PVIGEM_CLIENT Client,
PVIGEM_TARGET Target,
UCHAR LargeMotor,
UCHAR SmallMotor,
DS4_LIGHTBAR_COLOR LightbarColor,
LPVOID UserData);
typedef EVT_VIGEM_DS4_NOTIFICATION *PFN_VIGEM_DS4_NOTIFICATION;
/**
* \fn PVIGEM_CLIENT vigem_alloc(void);
*
* \brief Allocates an object representing a driver connection.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \return A new driver connection object.
*/
VIGEM_API PVIGEM_CLIENT vigem_alloc(void);
/**
* \fn void vigem_free(PVIGEM_CLIENT vigem);
*
* \brief Frees up memory used by the driver connection object.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
*/
VIGEM_API void vigem_free(PVIGEM_CLIENT vigem);
/**
* \fn VIGEM_ERROR vigem_connect(PVIGEM_CLIENT vigem);
*
* \brief Initializes the driver object and establishes a connection to the
* emulation bus driver. Returns an error if no compatible bus device has been
* found.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR vigem_connect(PVIGEM_CLIENT vigem);
/**
* \fn void vigem_disconnect(PVIGEM_CLIENT vigem);
*
* \brief Disconnects from the bus device and resets the driver object state.
* The driver object may be reused again after calling this function. When
* called, all targets which may still be connected will be destroyed
* automatically. Be aware, that allocated target objects won't be automatically
* freed, this has to be taken care of by the caller.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
*/
VIGEM_API void vigem_disconnect(PVIGEM_CLIENT vigem);
/**
* \fn PVIGEM_TARGET vigem_target_x360_alloc(void);
*
* \brief Allocates an object representing an Xbox 360 Controller device.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \return A PVIGEM_TARGET representing an Xbox 360 Controller device.
*/
VIGEM_API PVIGEM_TARGET vigem_target_x360_alloc(void);
/**
* \fn PVIGEM_TARGET vigem_target_ds4_alloc(void);
*
* \brief Allocates an object representing a DualShock 4 Controller device.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \return A PVIGEM_TARGET representing a DualShock 4 Controller device.
*/
VIGEM_API PVIGEM_TARGET vigem_target_ds4_alloc(void);
/**
* \fn void vigem_target_free(PVIGEM_TARGET target);
*
* \brief Frees up memory used by the target device object. This does not
* automatically remove the associated device from the bus, if present. If the
* target device doesn't get removed before this call, the device becomes
* orphaned until the owning process is terminated.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
*/
VIGEM_API void vigem_target_free(PVIGEM_TARGET target);
/**
* \fn VIGEM_ERROR vigem_target_add(PVIGEM_CLIENT vigem, PVIGEM_TARGET target);
*
* \brief Adds a provided target device to the bus driver, which is equal to a
* device plug-in event of a physical hardware device. This function blocks
* until the target device is in full operational mode.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
* \param target The target device object.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR
vigem_target_add(PVIGEM_CLIENT vigem, PVIGEM_TARGET target);
/**
* \fn VIGEM_ERROR vigem_target_add_async(PVIGEM_CLIENT vigem, PVIGEM_TARGET
* target, PVIGEM_TARGET_ADD_RESULT result);
*
* \brief Adds a provided target device to the bus driver, which is equal to a
* device plug-in event of a physical hardware device. This function immediately
* returns. An optional callback may be registered which gets called on error or
* if the target device has become fully operational.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
* \param target The target device object.
* \param result An optional function getting called when the target device
* becomes available.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR vigem_target_add_async(
PVIGEM_CLIENT vigem,
PVIGEM_TARGET target,
PFN_VIGEM_TARGET_ADD_RESULT result);
/**
* \fn VIGEM_ERROR vigem_target_remove(PVIGEM_CLIENT vigem, PVIGEM_TARGET
* target);
*
* \brief Removes a provided target device from the bus driver, which is equal
* to a device unplug event of a physical hardware device. The target device
* object may be reused after this function is called. If this function is never
* called on target device objects, they will be removed from the bus when the
* owning process terminates.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
* \param target The target device object.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR
vigem_target_remove(PVIGEM_CLIENT vigem, PVIGEM_TARGET target);
/**
* \fn VIGEM_ERROR vigem_target_x360_register_notification(PVIGEM_CLIENT vigem,
* PVIGEM_TARGET target, PVIGEM_X360_NOTIFICATION notification);
*
* \brief Registers a function which gets called, when LED index or vibration
* state changes occur on the provided target device. This function fails if the
* provided target device isn't fully operational or in an erroneous state.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
* \param target The target device object.
* \param notification The notification callback.
* \param userData The user data passed to the notification callback.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR vigem_target_x360_register_notification(
PVIGEM_CLIENT vigem,
PVIGEM_TARGET target,
PFN_VIGEM_X360_NOTIFICATION notification,
LPVOID userData);
/**
* \fn VIGEM_ERROR vigem_target_ds4_register_notification(PVIGEM_CLIENT vigem,
* PVIGEM_TARGET target, PVIGEM_DS4_NOTIFICATION notification);
*
* \brief Registers a function which gets called, when LightBar or vibration
* state changes occur on the provided target device. This function fails if the
* provided target device isn't fully operational or in an erroneous state.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
* \param target The target device object.
* \param notification The notification callback.
* \param userData The user data passed to the notification callback.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR vigem_target_ds4_register_notification(
PVIGEM_CLIENT vigem,
PVIGEM_TARGET target,
PFN_VIGEM_DS4_NOTIFICATION notification,
LPVOID userData);
/**
* \fn void vigem_target_x360_unregister_notification(PVIGEM_TARGET target);
*
* \brief Removes a previously registered callback function from the provided
* target object.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
*/
VIGEM_API void vigem_target_x360_unregister_notification(PVIGEM_TARGET target);
/**
* \fn void vigem_target_ds4_unregister_notification(PVIGEM_TARGET target);
*
* \brief Removes a previously registered callback function from the provided
* target object.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
*/
VIGEM_API void vigem_target_ds4_unregister_notification(PVIGEM_TARGET target);
/**
* \fn void vigem_target_set_vid(PVIGEM_TARGET target, USHORT vid);
*
* \brief Overrides the default Vendor ID value with the provided one.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
* \param vid The Vendor ID to set.
*/
VIGEM_API void vigem_target_set_vid(PVIGEM_TARGET target, USHORT vid);
/**
* \fn void vigem_target_set_pid(PVIGEM_TARGET target, USHORT pid);
*
* \brief Overrides the default Product ID value with the provided one.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
* \param pid The Product ID to set.
*/
VIGEM_API void vigem_target_set_pid(PVIGEM_TARGET target, USHORT pid);
/**
* \fn USHORT vigem_target_get_vid(PVIGEM_TARGET target);
*
* \brief Returns the Vendor ID of the provided target device object.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
*
* \return The Vendor ID.
*/
VIGEM_API USHORT vigem_target_get_vid(PVIGEM_TARGET target);
/**
* \fn USHORT vigem_target_get_pid(PVIGEM_TARGET target);
*
* \brief Returns the Product ID of the provided target device object.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
*
* \return The Product ID.
*/
VIGEM_API USHORT vigem_target_get_pid(PVIGEM_TARGET target);
/**
* \fn VIGEM_ERROR vigem_target_x360_update(PVIGEM_CLIENT vigem, PVIGEM_TARGET
* target, XUSB_REPORT report);
*
* \brief Sends a state report to the provided target device.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
* \param target The target device object.
* \param report The report to send to the target device.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR vigem_target_x360_update(
PVIGEM_CLIENT vigem, PVIGEM_TARGET target, XUSB_REPORT report);
/**
* \fn VIGEM_ERROR vigem_target_ds4_update(PVIGEM_CLIENT vigem, PVIGEM_TARGET
* target, DS4_REPORT report);
*
* \brief Sends a state report to the provided target device.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param vigem The driver connection object.
* \param target The target device object.
* \param report The report to send to the target device.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR vigem_target_ds4_update(
PVIGEM_CLIENT vigem, PVIGEM_TARGET target, DS4_REPORT report);
/**
* \fn ULONG vigem_target_get_index(PVIGEM_TARGET target);
*
* \brief Returns the internal index (serial number) the bus driver assigned
* to the provided target device object. Note that this value is specific to the
* inner workings of the bus driver, it does not reflect related values like
* player index or device arrival order experienced by other APIs. It may be
* used to identify the target device object for its lifetime. This value
* becomes invalid once the target device is removed from the bus and may change
* on the next addition of the device.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
*
* \return The internally used index of the target device.
*/
VIGEM_API ULONG vigem_target_get_index(PVIGEM_TARGET target);
/**
* \fn VIGEM_TARGET_TYPE vigem_target_get_type(PVIGEM_TARGET target);
*
* \brief Returns the type of the provided target device object.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 28.08.2017
*
* \param target The target device object.
*
* \return A VIGEM_TARGET_TYPE.
*/
VIGEM_API VIGEM_TARGET_TYPE vigem_target_get_type(PVIGEM_TARGET target);
/**
* \fn BOOL vigem_target_is_attached(PVIGEM_TARGET target);
*
* \brief Returns TRUE if the provided target device object is currently
* attached to the bus, FALSE otherwise.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 30.08.2017
*
* \param target The target device object.
*
* \return TRUE if device is attached to the bus, FALSE otherwise.
*/
VIGEM_API BOOL vigem_target_is_attached(PVIGEM_TARGET target);
/**
* \fn VIGEM_API VIGEM_ERROR vigem_target_x360_get_user_index(PVIGEM_CLIENT
* vigem, PVIGEM_TARGET target, PULONG index);
*
* \brief Returns the user index of the emulated Xenon device. This value
* correspondents to the (zero-based) index number representing the player
* number via LED present on a physical controller and is compatible to the
* dwUserIndex propery of the XInput* APIs.
*
* \author Benjamin "Nefarius" H<>glinger
* \date 10.05.2018
*
* \param vigem The driver connection object.
* \param target The target device object.
* \param index The (zero-based) user index of the Xenon device.
*
* \return A VIGEM_ERROR.
*/
VIGEM_API VIGEM_ERROR vigem_target_x360_get_user_index(
PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PULONG index);
#ifdef __cplusplus
}
#endif
#endif // ViGEmClient_h__

191
src/imports/ViGEm/Common.h Normal file
View File

@@ -0,0 +1,191 @@
/*
MIT License
Copyright (c) 2017-2019 Nefarius Software Solutions e.U. and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
//
// Represents the desired target type for the emulated device.
//
typedef enum _VIGEM_TARGET_TYPE {
//
// Microsoft Xbox 360 Controller (wired)
//
Xbox360Wired = 0,
//
// Sony DualShock 4 (wired)
//
DualShock4Wired = 2 // NOTE: 1 skipped on purpose to maintain compatibility
} VIGEM_TARGET_TYPE,
*PVIGEM_TARGET_TYPE;
//
// Possible XUSB report buttons.
//
typedef enum _XUSB_BUTTON {
XUSB_GAMEPAD_DPAD_UP = 0x0001,
XUSB_GAMEPAD_DPAD_DOWN = 0x0002,
XUSB_GAMEPAD_DPAD_LEFT = 0x0004,
XUSB_GAMEPAD_DPAD_RIGHT = 0x0008,
XUSB_GAMEPAD_START = 0x0010,
XUSB_GAMEPAD_BACK = 0x0020,
XUSB_GAMEPAD_LEFT_THUMB = 0x0040,
XUSB_GAMEPAD_RIGHT_THUMB = 0x0080,
XUSB_GAMEPAD_LEFT_SHOULDER = 0x0100,
XUSB_GAMEPAD_RIGHT_SHOULDER = 0x0200,
XUSB_GAMEPAD_GUIDE = 0x0400,
XUSB_GAMEPAD_A = 0x1000,
XUSB_GAMEPAD_B = 0x2000,
XUSB_GAMEPAD_X = 0x4000,
XUSB_GAMEPAD_Y = 0x8000
} XUSB_BUTTON,
*PXUSB_BUTTON;
//
// Represents an XINPUT_GAMEPAD-compatible report structure.
//
typedef struct _XUSB_REPORT {
USHORT wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XUSB_REPORT, *PXUSB_REPORT;
//
// Initializes a _XUSB_REPORT structure.
//
VOID FORCEINLINE XUSB_REPORT_INIT(_Out_ PXUSB_REPORT Report)
{
RtlZeroMemory(Report, sizeof(XUSB_REPORT));
}
//
// The color value (RGB) of a DualShock 4 Lightbar
//
typedef struct _DS4_LIGHTBAR_COLOR {
//
// Red part of the Lightbar (0-255).
//
UCHAR Red;
//
// Green part of the Lightbar (0-255).
//
UCHAR Green;
//
// Blue part of the Lightbar (0-255).
//
UCHAR Blue;
} DS4_LIGHTBAR_COLOR, *PDS4_LIGHTBAR_COLOR;
//
// DualShock 4 digital buttons
//
typedef enum _DS4_BUTTONS {
DS4_BUTTON_THUMB_RIGHT = 1 << 15,
DS4_BUTTON_THUMB_LEFT = 1 << 14,
DS4_BUTTON_OPTIONS = 1 << 13,
DS4_BUTTON_SHARE = 1 << 12,
DS4_BUTTON_TRIGGER_RIGHT = 1 << 11,
DS4_BUTTON_TRIGGER_LEFT = 1 << 10,
DS4_BUTTON_SHOULDER_RIGHT = 1 << 9,
DS4_BUTTON_SHOULDER_LEFT = 1 << 8,
DS4_BUTTON_TRIANGLE = 1 << 7,
DS4_BUTTON_CIRCLE = 1 << 6,
DS4_BUTTON_CROSS = 1 << 5,
DS4_BUTTON_SQUARE = 1 << 4
} DS4_BUTTONS,
*PDS4_BUTTONS;
//
// DualShock 4 special buttons
//
typedef enum _DS4_SPECIAL_BUTTONS {
DS4_SPECIAL_BUTTON_PS = 1 << 0,
DS4_SPECIAL_BUTTON_TOUCHPAD = 1 << 1
} DS4_SPECIAL_BUTTONS,
*PDS4_SPECIAL_BUTTONS;
//
// DualShock 4 directional pad (HAT) values
//
typedef enum _DS4_DPAD_DIRECTIONS {
DS4_BUTTON_DPAD_NONE = 0x8,
DS4_BUTTON_DPAD_NORTHWEST = 0x7,
DS4_BUTTON_DPAD_WEST = 0x6,
DS4_BUTTON_DPAD_SOUTHWEST = 0x5,
DS4_BUTTON_DPAD_SOUTH = 0x4,
DS4_BUTTON_DPAD_SOUTHEAST = 0x3,
DS4_BUTTON_DPAD_EAST = 0x2,
DS4_BUTTON_DPAD_NORTHEAST = 0x1,
DS4_BUTTON_DPAD_NORTH = 0x0
} DS4_DPAD_DIRECTIONS,
*PDS4_DPAD_DIRECTIONS;
//
// DualShock 4 HID Input report
//
typedef struct _DS4_REPORT {
BYTE bThumbLX;
BYTE bThumbLY;
BYTE bThumbRX;
BYTE bThumbRY;
USHORT wButtons;
BYTE bSpecial;
BYTE bTriggerL;
BYTE bTriggerR;
} DS4_REPORT, *PDS4_REPORT;
//
// Sets the current state of the D-PAD on a DualShock 4 report.
//
VOID FORCEINLINE
DS4_SET_DPAD(_Out_ PDS4_REPORT Report, _In_ DS4_DPAD_DIRECTIONS Dpad)
{
Report->wButtons &= ~0xF;
Report->wButtons |= (USHORT) Dpad;
}
VOID FORCEINLINE DS4_REPORT_INIT(_Out_ PDS4_REPORT Report)
{
RtlZeroMemory(Report, sizeof(DS4_REPORT));
Report->bThumbLX = 0x80;
Report->bThumbLY = 0x80;
Report->bThumbRX = 0x80;
Report->bThumbRY = 0x80;
DS4_SET_DPAD(Report, DS4_BUTTON_DPAD_NONE);
}

68
src/imports/ViGEm/Util.h Normal file
View File

@@ -0,0 +1,68 @@
#pragma once
#include "ViGEm/Common.h"
#include <limits.h>
VOID FORCEINLINE
XUSB_TO_DS4_REPORT(_Out_ PXUSB_REPORT Input, _Out_ PDS4_REPORT Output)
{
if (Input->wButtons & XUSB_GAMEPAD_BACK)
Output->wButtons |= DS4_BUTTON_SHARE;
if (Input->wButtons & XUSB_GAMEPAD_START)
Output->wButtons |= DS4_BUTTON_OPTIONS;
if (Input->wButtons & XUSB_GAMEPAD_LEFT_THUMB)
Output->wButtons |= DS4_BUTTON_THUMB_LEFT;
if (Input->wButtons & XUSB_GAMEPAD_RIGHT_THUMB)
Output->wButtons |= DS4_BUTTON_THUMB_RIGHT;
if (Input->wButtons & XUSB_GAMEPAD_LEFT_SHOULDER)
Output->wButtons |= DS4_BUTTON_SHOULDER_LEFT;
if (Input->wButtons & XUSB_GAMEPAD_RIGHT_SHOULDER)
Output->wButtons |= DS4_BUTTON_SHOULDER_RIGHT;
if (Input->wButtons & XUSB_GAMEPAD_GUIDE)
Output->bSpecial |= DS4_SPECIAL_BUTTON_PS;
if (Input->wButtons & XUSB_GAMEPAD_A)
Output->wButtons |= DS4_BUTTON_CROSS;
if (Input->wButtons & XUSB_GAMEPAD_B)
Output->wButtons |= DS4_BUTTON_CIRCLE;
if (Input->wButtons & XUSB_GAMEPAD_X)
Output->wButtons |= DS4_BUTTON_SQUARE;
if (Input->wButtons & XUSB_GAMEPAD_Y)
Output->wButtons |= DS4_BUTTON_TRIANGLE;
Output->bTriggerL = Input->bLeftTrigger;
Output->bTriggerR = Input->bRightTrigger;
if (Input->bLeftTrigger > 0)
Output->wButtons |= DS4_BUTTON_TRIGGER_LEFT;
if (Input->bRightTrigger > 0)
Output->wButtons |= DS4_BUTTON_TRIGGER_RIGHT;
if (Input->wButtons & XUSB_GAMEPAD_DPAD_UP)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_NORTH);
if (Input->wButtons & XUSB_GAMEPAD_DPAD_RIGHT)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_EAST);
if (Input->wButtons & XUSB_GAMEPAD_DPAD_DOWN)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_SOUTH);
if (Input->wButtons & XUSB_GAMEPAD_DPAD_LEFT)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_WEST);
if (Input->wButtons & XUSB_GAMEPAD_DPAD_UP &&
Input->wButtons & XUSB_GAMEPAD_DPAD_RIGHT)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_NORTHEAST);
if (Input->wButtons & XUSB_GAMEPAD_DPAD_RIGHT &&
Input->wButtons & XUSB_GAMEPAD_DPAD_DOWN)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_SOUTHEAST);
if (Input->wButtons & XUSB_GAMEPAD_DPAD_DOWN &&
Input->wButtons & XUSB_GAMEPAD_DPAD_LEFT)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_SOUTHWEST);
if (Input->wButtons & XUSB_GAMEPAD_DPAD_LEFT &&
Input->wButtons & XUSB_GAMEPAD_DPAD_UP)
DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_NORTHWEST);
Output->bThumbLX = ((Input->sThumbLX + ((USHRT_MAX / 2) + 1)) / 257);
Output->bThumbLY = (-(Input->sThumbLY + ((USHRT_MAX / 2) - 1)) / 257);
Output->bThumbLY = (Output->bThumbLY == 0) ? 0xFF : Output->bThumbLY;
Output->bThumbRX = ((Input->sThumbRX + ((USHRT_MAX / 2) + 1)) / 257);
Output->bThumbRY = (-(Input->sThumbRY + ((USHRT_MAX / 2) + 1)) / 257);
Output->bThumbRY = (Output->bThumbRY == 0) ? 0xFF : Output->bThumbRY;
}

View File

@@ -0,0 +1,399 @@
/*
MIT License
Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
//
// GUID identifying the bus device. Used by client library to detect and
// communicate.
//
// IMPORTANT: make sure to change this value if you fork it or introduce
// breaking changes!
//
// {96E42B22-F5E9-42F8-B043-ED0F932F014F}
DEFINE_GUID(
GUID_DEVINTERFACE_BUSENUM_VIGEM,
0x96E42B22,
0xF5E9,
0x42F8,
0xB0,
0x43,
0xED,
0x0F,
0x93,
0x2F,
0x01,
0x4F);
#pragma once
#include "ViGEm/Common.h"
//
// Common version for user-mode library and driver compatibility
//
// On initialization, the user-mode library has this number embedded
// and sends it to the bus on its enumeration. The bus compares this
// number to the one it was compiled with. If they match, the bus
// access is permitted and success reported. If they mismatch, an
// error is reported and the user-mode library skips this instance.
//
#define VIGEM_COMMON_VERSION 0x0001
#define FILE_DEVICE_BUSENUM FILE_DEVICE_BUS_EXTENDER
#define BUSENUM_IOCTL(_index_) \
CTL_CODE(FILE_DEVICE_BUSENUM, _index_, METHOD_BUFFERED, FILE_READ_DATA)
#define BUSENUM_W_IOCTL(_index_) \
CTL_CODE(FILE_DEVICE_BUSENUM, _index_, METHOD_BUFFERED, FILE_WRITE_DATA)
#define BUSENUM_R_IOCTL(_index_) \
CTL_CODE(FILE_DEVICE_BUSENUM, _index_, METHOD_BUFFERED, FILE_READ_DATA)
#define BUSENUM_RW_IOCTL(_index_) \
CTL_CODE( \
FILE_DEVICE_BUSENUM, \
_index_, \
METHOD_BUFFERED, \
FILE_WRITE_DATA | FILE_READ_DATA)
#define IOCTL_VIGEM_BASE 0x801
//
// IO control codes
//
#define IOCTL_VIGEM_PLUGIN_TARGET BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x000)
#define IOCTL_VIGEM_UNPLUG_TARGET BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x001)
#define IOCTL_VIGEM_CHECK_VERSION BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x002)
#define IOCTL_XUSB_REQUEST_NOTIFICATION \
BUSENUM_RW_IOCTL(IOCTL_VIGEM_BASE + 0x200)
#define IOCTL_XUSB_SUBMIT_REPORT BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x201)
#define IOCTL_DS4_SUBMIT_REPORT BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x202)
#define IOCTL_DS4_REQUEST_NOTIFICATION BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x203)
//#define IOCTL_XGIP_SUBMIT_REPORT BUSENUM_W_IOCTL (IOCTL_VIGEM_BASE +
//0x204) #define IOCTL_XGIP_SUBMIT_INTERRUPT BUSENUM_W_IOCTL
//(IOCTL_VIGEM_BASE + 0x205)
#define IOCTL_XUSB_GET_USER_INDEX BUSENUM_RW_IOCTL(IOCTL_VIGEM_BASE + 0x206)
//
// Data structure used in PlugIn and UnPlug ioctls
//
#pragma region Plugin
//
// Data structure used in IOCTL_VIGEM_PLUGIN_TARGET requests.
//
typedef struct _VIGEM_PLUGIN_TARGET {
//
// sizeof (struct _BUSENUM_HARDWARE)
//
IN ULONG Size;
//
// Serial number of target device.
//
IN ULONG SerialNo;
//
// Type of the target device to emulate.
//
VIGEM_TARGET_TYPE TargetType;
//
// If set, the vendor ID the emulated device is reporting
//
USHORT VendorId;
//
// If set, the product ID the emulated device is reporting
//
USHORT ProductId;
} VIGEM_PLUGIN_TARGET, *PVIGEM_PLUGIN_TARGET;
//
// Initializes a VIGEM_PLUGIN_TARGET structure.
//
VOID FORCEINLINE VIGEM_PLUGIN_TARGET_INIT(
_Out_ PVIGEM_PLUGIN_TARGET PlugIn,
_In_ ULONG SerialNo,
_In_ VIGEM_TARGET_TYPE TargetType)
{
RtlZeroMemory(PlugIn, sizeof(VIGEM_PLUGIN_TARGET));
PlugIn->Size = sizeof(VIGEM_PLUGIN_TARGET);
PlugIn->SerialNo = SerialNo;
PlugIn->TargetType = TargetType;
}
#pragma endregion
#pragma region Unplug
//
// Data structure used in IOCTL_VIGEM_UNPLUG_TARGET requests.
//
typedef struct _VIGEM_UNPLUG_TARGET {
//
// sizeof (struct _REMOVE_HARDWARE)
//
IN ULONG Size;
//
// Serial number of target device.
//
ULONG SerialNo;
} VIGEM_UNPLUG_TARGET, *PVIGEM_UNPLUG_TARGET;
//
// Initializes a VIGEM_UNPLUG_TARGET structure.
//
VOID FORCEINLINE
VIGEM_UNPLUG_TARGET_INIT(_Out_ PVIGEM_UNPLUG_TARGET UnPlug, _In_ ULONG SerialNo)
{
RtlZeroMemory(UnPlug, sizeof(VIGEM_UNPLUG_TARGET));
UnPlug->Size = sizeof(VIGEM_UNPLUG_TARGET);
UnPlug->SerialNo = SerialNo;
}
#pragma endregion
#pragma region Check version
typedef struct _VIGEM_CHECK_VERSION {
IN ULONG Size;
IN ULONG Version;
} VIGEM_CHECK_VERSION, *PVIGEM_CHECK_VERSION;
VOID FORCEINLINE VIGEM_CHECK_VERSION_INIT(
_Out_ PVIGEM_CHECK_VERSION CheckVersion, _In_ ULONG Version)
{
RtlZeroMemory(CheckVersion, sizeof(VIGEM_CHECK_VERSION));
CheckVersion->Size = sizeof(VIGEM_CHECK_VERSION);
CheckVersion->Version = Version;
}
#pragma endregion
#pragma region XUSB(aka Xbox 360 device) section
//
// Data structure used in IOCTL_XUSB_REQUEST_NOTIFICATION requests.
//
typedef struct _XUSB_REQUEST_NOTIFICATION {
//
// sizeof(struct _XUSB_REQUEST_NOTIFICATION)
//
ULONG Size;
//
// Serial number of target device.
//
ULONG SerialNo;
//
// Vibration intensity value of the large motor (0-255).
//
UCHAR LargeMotor;
//
// Vibration intensity value of the small motor (0-255).
//
UCHAR SmallMotor;
//
// Index number of the slot/LED that XUSB.sys has assigned.
//
UCHAR LedNumber;
} XUSB_REQUEST_NOTIFICATION, *PXUSB_REQUEST_NOTIFICATION;
//
// Initializes a XUSB_REQUEST_NOTIFICATION structure.
//
VOID FORCEINLINE XUSB_REQUEST_NOTIFICATION_INIT(
_Out_ PXUSB_REQUEST_NOTIFICATION Request, _In_ ULONG SerialNo)
{
RtlZeroMemory(Request, sizeof(XUSB_REQUEST_NOTIFICATION));
Request->Size = sizeof(XUSB_REQUEST_NOTIFICATION);
Request->SerialNo = SerialNo;
}
//
// Data structure used in IOCTL_XUSB_SUBMIT_REPORT requests.
//
typedef struct _XUSB_SUBMIT_REPORT {
//
// sizeof(struct _XUSB_SUBMIT_REPORT)
//
ULONG Size;
//
// Serial number of target device.
//
ULONG SerialNo;
//
// Report to submit to the target device.
//
XUSB_REPORT Report;
} XUSB_SUBMIT_REPORT, *PXUSB_SUBMIT_REPORT;
//
// Initializes an XUSB report.
//
VOID FORCEINLINE
XUSB_SUBMIT_REPORT_INIT(_Out_ PXUSB_SUBMIT_REPORT Report, _In_ ULONG SerialNo)
{
RtlZeroMemory(Report, sizeof(XUSB_SUBMIT_REPORT));
Report->Size = sizeof(XUSB_SUBMIT_REPORT);
Report->SerialNo = SerialNo;
}
typedef struct _XUSB_GET_USER_INDEX {
//
// sizeof(struct _XUSB_GET_USER_INDEX)
//
ULONG Size;
//
// Serial number of target device.
//
ULONG SerialNo;
//
// User index of target device.
//
OUT ULONG UserIndex;
} XUSB_GET_USER_INDEX, *PXUSB_GET_USER_INDEX;
//
// Initializes XUSB_GET_USER_INDEX structure.
//
VOID FORCEINLINE XUSB_GET_USER_INDEX_INIT(
_Out_ PXUSB_GET_USER_INDEX GetRequest, _In_ ULONG SerialNo)
{
RtlZeroMemory(GetRequest, sizeof(XUSB_GET_USER_INDEX));
GetRequest->Size = sizeof(XUSB_GET_USER_INDEX);
GetRequest->SerialNo = SerialNo;
}
#pragma endregion
#pragma region DualShock 4 section
typedef struct _DS4_OUTPUT_REPORT {
//
// Vibration intensity value of the small motor (0-255).
//
UCHAR SmallMotor;
//
// Vibration intensity value of the large motor (0-255).
//
UCHAR LargeMotor;
//
// Color values of the Lightbar.
//
DS4_LIGHTBAR_COLOR LightbarColor;
} DS4_OUTPUT_REPORT, *PDS4_OUTPUT_REPORT;
//
// Data structure used in IOCTL_DS4_REQUEST_NOTIFICATION requests.
//
typedef struct _DS4_REQUEST_NOTIFICATION {
//
// sizeof(struct _XUSB_REQUEST_NOTIFICATION)
//
ULONG Size;
//
// Serial number of target device.
//
ULONG SerialNo;
//
// The HID output report
//
DS4_OUTPUT_REPORT Report;
} DS4_REQUEST_NOTIFICATION, *PDS4_REQUEST_NOTIFICATION;
//
// Initializes a DS4_REQUEST_NOTIFICATION structure.
//
VOID FORCEINLINE DS4_REQUEST_NOTIFICATION_INIT(
_Out_ PDS4_REQUEST_NOTIFICATION Request, _In_ ULONG SerialNo)
{
RtlZeroMemory(Request, sizeof(DS4_REQUEST_NOTIFICATION));
Request->Size = sizeof(DS4_REQUEST_NOTIFICATION);
Request->SerialNo = SerialNo;
}
//
// DualShock 4 request data
//
typedef struct _DS4_SUBMIT_REPORT {
//
// sizeof(struct _DS4_SUBMIT_REPORT)
//
ULONG Size;
//
// Serial number of target device.
//
ULONG SerialNo;
//
// HID Input report
//
DS4_REPORT Report;
} DS4_SUBMIT_REPORT, *PDS4_SUBMIT_REPORT;
//
// Initializes a DualShock 4 report.
//
VOID FORCEINLINE
DS4_SUBMIT_REPORT_INIT(_Out_ PDS4_SUBMIT_REPORT Report, _In_ ULONG SerialNo)
{
RtlZeroMemory(Report, sizeof(DS4_SUBMIT_REPORT));
Report->Size = sizeof(DS4_SUBMIT_REPORT);
Report->SerialNo = SerialNo;
DS4_REPORT_INIT(&Report->Report);
}
#pragma endregion

View File

@@ -230,4 +230,6 @@ int avs_fs_addfs(void *filesys_struct);
int avs_fs_mount(
const char *mountpoint, const char *fsroot, const char *fstype, void *data);
bool avs_is_active();
#endif

View File

@@ -0,0 +1,27 @@
LIBRARY ViGEmClient
EXPORTS
vigem_alloc
vigem_connect
vigem_disconnect
vigem_free
vigem_target_add
vigem_target_add_async
vigem_target_ds4_alloc
vigem_target_ds4_register_notification
vigem_target_ds4_unregister_notification
vigem_target_ds4_update
vigem_target_free
vigem_target_get_index
vigem_target_get_pid
vigem_target_get_type
vigem_target_get_vid
vigem_target_is_attached
vigem_target_remove
vigem_target_set_pid
vigem_target_set_vid
vigem_target_x360_alloc
vigem_target_x360_get_user_index
vigem_target_x360_register_notification
vigem_target_x360_unregister_notification
vigem_target_x360_update

View File

@@ -23,6 +23,7 @@ EXPORTS
std_setenv @213 NONAME
avs_boot @298 NONAME
avs_shutdown @299 NONAME
avs_is_active @302 NONAME
log_body_fatal @379 NONAME
log_body_warning @380 NONAME
log_body_info @381 NONAME

View File

@@ -0,0 +1,27 @@
LIBRARY ViGEmClient
EXPORTS
vigem_alloc
vigem_connect
vigem_disconnect
vigem_free
vigem_target_add
vigem_target_add_async
vigem_target_ds4_alloc
vigem_target_ds4_register_notification
vigem_target_ds4_unregister_notification
vigem_target_ds4_update
vigem_target_free
vigem_target_get_index
vigem_target_get_pid
vigem_target_get_type
vigem_target_get_vid
vigem_target_is_attached
vigem_target_remove
vigem_target_set_pid
vigem_target_set_vid
vigem_target_x360_alloc
vigem_target_x360_get_user_index
vigem_target_x360_register_notification
vigem_target_x360_unregister_notification
vigem_target_x360_update

View File

@@ -36,6 +36,7 @@ enum ac_io_node_type {
AC_IO_NODE_TYPE_LED_SPIKE = 0x05010000,
AC_IO_NODE_TYPE_KFCA = 0x09060000,
AC_IO_NODE_TYPE_BI2A = 0x0d060000,
AC_IO_NODE_TYPE_RVOL = 0x09060001,
};
#pragma pack(push, 1)

View File

@@ -1,6 +1,8 @@
#ifndef AC_IO_KFCA_H
#define AC_IO_KFCA_H
#include <stdint.h>
#define AC_IO_CMD_KFCA_POLL 0x0113
#define AC_IO_CMD_KFCA_WATCHDOG 0x0120
#define AC_IO_CMD_KFCA_AMP_CONTROL 0x0128

View File

@@ -166,6 +166,7 @@ static int aciodrv_device_receive(uint8_t *buffer, int size)
#ifdef AC_IO_MSG_LOG
aciodrv_device_log_buffer("Recv (1)", recv_buf, recv_size);
log_warning("Expected %d got %d", recv_buf[4], recv_size - 6);
#endif
/* recv_size - 1: omit checksum for checksum calc */
@@ -321,7 +322,11 @@ bool aciodrv_send_and_recv(struct ac_io_message *msg, int resp_size)
int send_size = offsetof(struct ac_io_message, cmd.raw) + msg->cmd.nbytes;
#ifdef AC_IO_MSG_LOG
log_info("Beginning send on %d: %04x (%d b)", msg->addr, msg->cmd.code, send_size);
log_info(
"Beginning send on %d: %04x (%d b)",
msg->addr,
msg->cmd.code,
send_size);
#endif
if (aciodrv_device_send((uint8_t *) msg, send_size) <= 0) {
return false;

View File

@@ -37,7 +37,12 @@ static bool aciodrv_kfca_watchdog_start(uint8_t node_id)
*/
}
static bool aciodrv_kfca_amp(uint8_t node_id)
bool aciodrv_kfca_amp(
uint8_t node_id,
uint8_t primary,
uint8_t headphone,
uint8_t unused,
uint8_t subwoofer)
{
struct ac_io_message msg;
@@ -45,12 +50,10 @@ static bool aciodrv_kfca_amp(uint8_t node_id)
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_AMP_CONTROL);
msg.cmd.nbytes = 4;
// yes this sets the amp to 100% volume
// TODO: expose this to sdvxio instead at some point
msg.cmd.raw[0] = 0;
msg.cmd.raw[1] = 0;
msg.cmd.raw[2] = 0;
msg.cmd.raw[3] = 0;
msg.cmd.raw[0] = primary;
msg.cmd.raw[1] = headphone;
msg.cmd.raw[2] = unused;
msg.cmd.raw[3] = subwoofer;
if (!aciodrv_send_and_recv(
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
@@ -69,7 +72,7 @@ bool aciodrv_kfca_init(uint8_t node_id)
return false;
}
if (!aciodrv_kfca_amp(node_id)) {
if (!aciodrv_kfca_amp(node_id, 0, 0, 0, 0)) {
return false;
}

View File

@@ -30,4 +30,22 @@ bool aciodrv_kfca_poll(
const struct ac_io_kfca_poll_out *pout,
struct ac_io_kfca_poll_in *pin);
/**
* Set the KFCA digital amp level
*
* @param node_id Id of the node to query (0 based).
* @param primary primary volume (96-0)
* @param headphone headphone volume (96-0)
* @param unused unknown volume (96-0) (unused)
* @param subwoofer subwoofer volume (96-0)
* @return True on success, false on error.
* @note Note 96 (or 100?) is lowest volume level, 0 is highest
*/
bool aciodrv_kfca_amp(
uint8_t node_id,
uint8_t primary,
uint8_t headphone,
uint8_t unused,
uint8_t subwoofer);
#endif

View File

@@ -30,8 +30,16 @@ void ac_io_emu_init(struct ac_io_emu *emu, const wchar_t *filename)
log_assert(emu != NULL);
log_assert(filename != NULL);
HRESULT hr;
memset(emu, 0, sizeof(*emu));
emu->fd = iohook_open_dummy_fd();
hr = iohook_open_nul_fd(&emu->fd);
if (hr != S_OK) {
log_fatal("Opening nul fd failed: %08lx", hr);
}
emu->wfilename = wstr_dup(filename);
wstr_narrow(filename, &emu->filename);
ac_io_in_init(&emu->in);

View File

@@ -105,11 +105,11 @@ static void ac_io_emu_hdxs_cmd_send_version(
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_LED_STRIP);
resp.cmd.version.flag = 0x00;
resp.cmd.version.major = 0x01;
resp.cmd.version.minor = 0x06;
resp.cmd.version.revision = 0x00;
resp.cmd.version.minor = 0x00;
resp.cmd.version.revision = 0x01;
memcpy(
resp.cmd.version.product_code,
"HDXS",
"HDXB",
sizeof(resp.cmd.version.product_code));
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));

View File

@@ -1,10 +1,12 @@
exes += aciotest
libs_aciotest := \
bio2drv \
aciodrv \
util \
src_aciotest := \
icca.c \
kfca.c \
bi2a-sdvx.c \
main.c \

View File

@@ -0,0 +1,58 @@
#include "aciotest/bi2a-sdvx.h"
#include "acio/acio.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bio2drv/bi2a-sdvx.h"
bool aciotest_bi2a_sdvx_handler_init(uint8_t node_id, void **ctx)
{
*ctx = malloc(sizeof(uint32_t));
*((uint32_t *) *ctx) = 0;
return bio2drv_bi2a_sdvx_init(node_id);
}
bool aciotest_bi2a_sdvx_handler_update(uint8_t node_id, void *ctx)
{
struct bi2a_sdvx_state_in pin;
struct bi2a_sdvx_state_out pout;
memset(&pout, 0, sizeof(pout));
if (!bio2drv_bi2a_sdvx_poll(node_id, &pout, &pin)) {
return false;
}
pin.raw[0] = ac_io_u16(pin.raw[0]);
pin.raw[1] = ac_io_u16(pin.raw[1]);
printf(
">>> BI2A (SDVX) %d:\n"
"BTN A B C D: %d %d %d %d\n"
"FX-L R: %d %d\n"
"VOL L: %d\n"
"VOL R: %d\n"
"START COIN TEST SERV REC HP: %d %d %d %d %d %d\n",
node_id,
pin.buttons_1.b_a,
pin.buttons_1.b_b,
pin.buttons_1.b_c,
pin.buttons_1.b_d,
pin.buttons_1.b_fxl,
pin.buttons_2.b_fxr,
pin.analogs[0].a_val,
pin.analogs[1].a_val,
pin.buttons_1.b_start,
pin.analogs[0].a_coin,
pin.analogs[0].a_test,
pin.analogs[0].a_service,
pin.buttons_1.b_recorder,
pin.buttons_1.b_headphone);
return true;
}

View File

@@ -0,0 +1,10 @@
#ifndef ACIOTEST_BI2A_SDVX_H
#define ACIOTEST_BI2A_SDVX_H
#include <stdbool.h>
#include <stdint.h>
bool aciotest_bi2a_sdvx_handler_init(uint8_t node_id, void **ctx);
bool aciotest_bi2a_sdvx_handler_update(uint8_t node_id, void *ctx);
#endif

View File

@@ -6,6 +6,7 @@
#include "aciodrv/device.h"
#include "aciotest/bi2a-sdvx.h"
#include "aciotest/handler.h"
#include "aciotest/icca.h"
#include "aciotest/kfca.h"
@@ -13,6 +14,7 @@
#include "util/log.h"
static uint8_t aciotest_cnt = 0;
static uint8_t bi2a_mode = 0;
/**
* Enumerate supported ACIO nodes based on their product id.
@@ -35,6 +37,17 @@ static bool aciotest_assign_handler(
return true;
}
if (!memcmp(product, "BI2A", 4)) {
if (bi2a_mode == 0) {
handler->init = aciotest_bi2a_sdvx_handler_init;
handler->update = aciotest_bi2a_sdvx_handler_update;
return true;
} else {
printf("Unknown BI2A device specified");
}
}
return false;
}
@@ -125,7 +138,7 @@ int main(int argc, char **argv)
}
/* avoid cpu banging */
Sleep(20);
Sleep(30);
}
return 0;

View File

@@ -5,9 +5,11 @@
#include "util/log.h"
#define ASIOHOOK_CONFIG_IO_FORCE_ASIO_KEY "asio.force_asio"
#define ASIOHOOK_CONFIG_IO_FORCE_WASAPI_KEY "asio.force_wasapi"
#define ASIOHOOK_CONFIG_IO_ASIO_DEVICE_NAME_KEY "asio.device_name"
#define ASIOHOOK_CONFIG_IO_DEFAULT_FORCE_ASIO_VALUE false
#define ASIOHOOK_CONFIG_IO_DEFAULT_FORCE_WASAPI_VALUE false
#define ASIOHOOK_CONFIG_IO_DEFAULT_ASIO_DEVICE_NAME_VALUE \
"XONAR SOUND CARD(64)"
@@ -17,7 +19,13 @@ void asiohook_config_init(struct cconfig *config)
config,
ASIOHOOK_CONFIG_IO_FORCE_ASIO_KEY,
ASIOHOOK_CONFIG_IO_DEFAULT_FORCE_ASIO_VALUE,
"Force ASIO audio mode/device");
"Force ASIO audio mode/device (if applicable)");
cconfig_util_set_bool(
config,
ASIOHOOK_CONFIG_IO_FORCE_WASAPI_KEY,
ASIOHOOK_CONFIG_IO_DEFAULT_FORCE_WASAPI_VALUE,
"Force WASAPI audio mode (if applicable)");
cconfig_util_set_str(
config,
@@ -40,6 +48,17 @@ void asiohook_config_asio_get(
ASIOHOOK_CONFIG_IO_FORCE_ASIO_KEY,
ASIOHOOK_CONFIG_IO_DEFAULT_FORCE_ASIO_VALUE);
}
if (!cconfig_util_get_bool(
config,
ASIOHOOK_CONFIG_IO_FORCE_WASAPI_KEY,
&config_asio->force_wasapi,
ASIOHOOK_CONFIG_IO_DEFAULT_FORCE_WASAPI_VALUE)) {
log_warning(
"Invalid value for key '%s' specified, fallback "
"to default '%d'",
ASIOHOOK_CONFIG_IO_FORCE_WASAPI_KEY,
ASIOHOOK_CONFIG_IO_DEFAULT_FORCE_WASAPI_VALUE);
}
if (!cconfig_util_get_str(
config,

View File

@@ -7,6 +7,7 @@
struct asiohook_config_asio {
bool force_asio;
bool force_wasapi;
char replacement_name[128];
};

View File

@@ -52,19 +52,20 @@ enum hdxs_light_bit {
LIGHT_HD_P2_LEFT_RIGHT = 0x0D,
};
enum hdxs_rgb_light_bit {
LIGHT_HD_P1_SPEAKER_F_R = 0x20,
LIGHT_HD_P1_SPEAKER_F_G = 0x21,
LIGHT_HD_P1_SPEAKER_F_B = 0x22,
LIGHT_HD_P2_SPEAKER_F_R = 0x23,
LIGHT_HD_P2_SPEAKER_F_G = 0x24,
LIGHT_HD_P2_SPEAKER_F_B = 0x25,
LIGHT_HD_P1_SPEAKER_W_R = 0x26,
LIGHT_HD_P1_SPEAKER_W_G = 0x27,
LIGHT_HD_P1_SPEAKER_W_B = 0x28,
LIGHT_HD_P2_SPEAKER_W_R = 0x29,
LIGHT_HD_P2_SPEAKER_W_G = 0x2A,
LIGHT_HD_P2_SPEAKER_W_B = 0x2B,
// the indexing starts from 0x20 if you're looking in geninput
enum hdxs_rgb_light_idx {
LIGHT_HD_P1_SPEAKER_F_R = 0x00,
LIGHT_HD_P1_SPEAKER_F_G = 0x01,
LIGHT_HD_P1_SPEAKER_F_B = 0x02,
LIGHT_HD_P2_SPEAKER_F_R = 0x03,
LIGHT_HD_P2_SPEAKER_F_G = 0x04,
LIGHT_HD_P2_SPEAKER_F_B = 0x05,
LIGHT_HD_P1_SPEAKER_W_R = 0x06,
LIGHT_HD_P1_SPEAKER_W_G = 0x07,
LIGHT_HD_P1_SPEAKER_W_B = 0x08,
LIGHT_HD_P2_SPEAKER_W_R = 0x09,
LIGHT_HD_P2_SPEAKER_W_G = 0x0A,
LIGHT_HD_P2_SPEAKER_W_B = 0x0B,
};
enum extio_light_bit {

View File

@@ -98,4 +98,12 @@ uint16_t sdvx_io_get_input_gpio(uint8_t gpio_bank);
uint16_t sdvx_io_get_spinner_pos(uint8_t spinner_no);
/* Sets the volume of the digital amps if possible.
Range is between 0-96, where 0 is MAX and 96 is LOW. */
bool sdvx_io_set_amp_volume(
uint8_t primary,
uint8_t headphone,
uint8_t subwoofer);
#endif

View File

@@ -1,18 +1,7 @@
#ifndef IIDXHOOK_BI2A_H
#define IIDXHOOK_BI2A_H
#ifndef BIO2_BI2A_IIDX
#define BIO2_BI2A_IIDX
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "bio2emu/emu.h"
enum bio2_iidx_cmd {
// Custom Stuff
BIO2_BI2A_CMD_UNK_0100 = 0x0100,
BIO2_BI2A_CMD_UNK_0120 = 0x0120,
BIO2_BI2A_CMD_POLL = 0x0152,
};
#include "bio2/bio2.h"
#pragma pack(push, 1)
struct bi2a_iidx_slider {
@@ -114,8 +103,4 @@ _Static_assert(
"bi2a_iidx_state_out is the wrong size");
#pragma pack(pop)
void bio2_emu_bi2a_init(struct bio2emu_port *in, bool disable_poll_limiter);
void bio2_emu_bi2a_dispatch_request(
struct bio2emu_port *bio2port, const struct ac_io_message *req);
#endif

75
src/main/bio2/bi2a-sdvx.h Normal file
View File

@@ -0,0 +1,75 @@
#ifndef BIO2_BI2A_SDVX
#define BIO2_BI2A_SDVX
#include "bio2/bio2.h"
#pragma pack(push, 1)
struct bi2a_sdvx_analog {
uint16_t unk1 : 1;
uint16_t a_coin : 1;
uint16_t a_service : 1;
uint16_t a_test : 1;
uint16_t unk2 : 2;
uint16_t a_val : 10;
};
struct bi2a_sdvx_buttons1 {
uint8_t b_fxl : 1;
uint8_t b_d : 1;
uint8_t b_c : 1;
uint8_t b_b : 1;
uint8_t b_a : 1;
uint8_t b_start : 1;
uint8_t b_recorder : 1;
uint8_t b_headphone : 1;
};
struct bi2a_sdvx_buttons2 {
uint8_t unk : 5;
uint8_t b_ex2 : 1;
uint8_t b_ex1 : 1;
uint8_t b_fxr : 1;
};
struct bi2a_sdvx_state_in {
union {
uint16_t raw[8];
struct {
struct bi2a_sdvx_analog analogs[4];
uint8_t unk_1; // coin mech?
struct bi2a_sdvx_buttons1 buttons_1;
struct bi2a_sdvx_buttons2 buttons_2;
uint8_t unk_2[5];
};
};
};
struct bi2a_sdvx_state_out {
uint8_t unk1[2];
uint8_t c_block;
uint8_t woof_r;
uint8_t woof_g;
uint8_t unk2;
uint8_t woof_b;
uint8_t controller[3];
uint8_t unk3;
uint8_t gpio[7]; // START, ABCD, FXL, FXR
uint8_t unk4[3];
uint8_t generator[3];
uint8_t unk5[3];
uint8_t gpio2[3]; // pop, titleL, titleR
uint8_t unk6;
uint8_t wingUpper[3];
uint8_t wingLower[3];
uint8_t unk7[3];
};
_Static_assert(
sizeof(struct bi2a_sdvx_state_in) == 16,
"bi2a_sdvx_state_in is the wrong size");
_Static_assert(
sizeof(struct bi2a_sdvx_state_out) == 40,
"bi2a_sdvx_state_out is the wrong size");
#pragma pack(pop)
#endif

15
src/main/bio2/bio2.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef BIO2_BIO2
#define BIO2_BIO2
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
enum bio2_bi2a_cmd {
// Custom Stuff
BIO2_BI2A_CMD_UNK_0100 = 0x0100,
BIO2_BI2A_CMD_UNK_0120 = 0x0120,
BIO2_BI2A_CMD_POLL = 0x0152, // For IIDX
};
#endif

View File

@@ -0,0 +1,9 @@
libs += bio2drv
libs_bio2drv := \
aciodrv
src_bio2drv := \
detect.c \
config-bio2.c \
bi2a-sdvx.c \

View File

@@ -0,0 +1,138 @@
#define LOG_MODULE "bio2drv-bi2a_sdvx"
#include "bio2drv/bi2a-sdvx.h"
#include <stdio.h>
#include <string.h>
#include "aciodrv/device.h"
#include "util/log.h"
// this is probably InitIO
static bool bio2drv_bi2a_sdvx_init_io(uint8_t node_id)
{
struct ac_io_message msg;
msg.addr = node_id + 1;
msg.cmd.code = ac_io_u16(AC_IO_CMD_CLEAR);
msg.cmd.nbytes = 1;
msg.cmd.count = 0x3B;
if (!aciodrv_send_and_recv(
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
log_warning("Init node failed");
return 0;
}
log_warning("Init of node %d, status: %d", node_id, msg.cmd.status);
return 1;
}
static bool bio2drv_bi2a_sdvx_watchdog_start(uint8_t node_id)
{
// exit early and don't actually call watchdog
// the watchdog call actually returns different sized packets depending on
// the state this results in an issue during packet processing (see: #68)
return true;
/*
struct ac_io_message msg;
msg.addr = node_id + 1;
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_WATCHDOG);
msg.cmd.nbytes = 2;
msg.cmd.nbytes = 2;
// uint16_t: 6000
msg.cmd.raw[0] = 23;
msg.cmd.raw[1] = 112;
if (!aciodrv_send_and_recv(
&msg, offsetof(struct ac_io_message, cmd.raw) + 2
)) {
log_warning("Starting watchdog failed"); return false;
}
log_warning("Started watchdog of node %d, status: %d",
node_id, msg.cmd.status);
return true;
*/
}
bool bio2drv_bi2a_sdvx_amp(
uint8_t node_id,
uint8_t unused_1,
uint8_t unused_2,
uint8_t left,
uint8_t right)
{
struct ac_io_message msg;
msg.addr = node_id + 1;
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_AMP_CONTROL);
msg.cmd.nbytes = 4;
// the BIO2 DOES NOT MATCH THE KFCA for what these mean
// 0 and 1 are ignored, 2 and 3 seem to map to left/right for ALL 3 AMPS
// in the future, maybe use windows APIs and write the volume levels there
msg.cmd.raw[0] = unused_1;
msg.cmd.raw[1] = unused_2;
msg.cmd.raw[2] = left;
msg.cmd.raw[3] = right;
if (!aciodrv_send_and_recv(
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
log_warning("Setting AMP failed");
return false;
}
log_warning("Started AMP node %d", node_id);
return true;
}
bool bio2drv_bi2a_sdvx_init(uint8_t node_id)
{
if (!bio2drv_bi2a_sdvx_init_io(node_id)) {
return false;
}
if (!bio2drv_bi2a_sdvx_watchdog_start(node_id)) {
return false;
}
if (!bio2drv_bi2a_sdvx_amp(node_id, 0, 0, 0, 0)) {
return false;
}
return true;
}
bool bio2drv_bi2a_sdvx_poll(
uint8_t node_id,
const struct bi2a_sdvx_state_out *pout,
struct bi2a_sdvx_state_in *pin)
{
struct ac_io_message msg;
msg.addr = node_id + 1;
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_POLL);
msg.cmd.nbytes = sizeof(*pout);
/* buffer size of data we expect */
*(struct bi2a_sdvx_state_out *) msg.cmd.raw = *pout;
if (!aciodrv_send_and_recv(
&msg, offsetof(struct ac_io_message, cmd.raw) + sizeof(*pin))) {
log_warning("Polling of node %d failed", node_id + 1);
return false;
}
if (pin != NULL) {
memcpy(pin, &msg.cmd.raw, sizeof(*pin));
}
return true;
}

View File

@@ -0,0 +1,51 @@
#ifndef BIO2DRV_BI2A_H
#define BIO2DRV_BI2A_H
#include "bio2/bi2a-sdvx.h"
/**
* Initialize a BI2A node.
*
* @param node_id Id of the node to initialize (0 based).
* @return True if successful, false on error.
* @note This module is supposed to be used in combination with the common
* device driver foundation.
* @see driver.h
*/
bool bio2drv_bi2a_sdvx_init(uint8_t node_id);
/**
* Poll the board
*
* @param node_id Id of the node to query (0 based).
* @param state Pointer to a state struct to return the current state to
* (optional, NULL for none).
* @return True on success, false on error.
* @note This module is supposed to be used in combination with the common
* device driver foundation.
* @see driver.h
*/
bool bio2drv_bi2a_sdvx_poll(
uint8_t node_id,
const struct bi2a_sdvx_state_out *pout,
struct bi2a_sdvx_state_in *pin);
/**
* Set the BIO2 BI2A SDVX digital amp level
*
* @param node_id Id of the node to query (0 based).
* @param primary primary volume (96-0)
* @param headphone headphone volume (96-0)
* @param unused unknown volume (96-0) (unused)
* @param subwoofer subwoofer volume (96-0)
* @return True on success, false on error.
* @note Note 96 is lowest volume level, 0 is highest
*/
bool bio2drv_bi2a_sdvx_amp(
uint8_t node_id,
uint8_t unused_1,
uint8_t unused_2,
uint8_t left,
uint8_t right);
#endif

View File

@@ -0,0 +1,75 @@
#include "cconfig/cconfig-util.h"
#include "bio2drv/config-bio2.h"
#include "util/log.h"
#define BIO2DRV_CONFIG_BIO2_AUTO_KEY "bio2.autodetect"
#define BIO2DRV_CONFIG_BIO2_PORT_KEY "bio2.port"
#define BIO2DRV_CONFIG_BIO2_BAUD_KEY "bio2.baud"
#define BIO2DRV_CONFIG_BIO2_DEFAULT_AUTO_VALUE true
#define BIO2DRV_CONFIG_BIO2_DEFAULT_PORT_VALUE "COM4"
#define BIO2DRV_CONFIG_BIO2_DEFAULT_BAUD_VALUE 115200
void bio2drv_config_bio2_init(struct cconfig *config)
{
cconfig_util_set_bool(
config,
BIO2DRV_CONFIG_BIO2_AUTO_KEY,
BIO2DRV_CONFIG_BIO2_DEFAULT_AUTO_VALUE,
"Autodetect BIO2 port (default: on)");
cconfig_util_set_str(
config,
BIO2DRV_CONFIG_BIO2_PORT_KEY,
BIO2DRV_CONFIG_BIO2_DEFAULT_PORT_VALUE,
"BIO2 serial port");
cconfig_util_set_int(
config,
BIO2DRV_CONFIG_BIO2_BAUD_KEY,
BIO2DRV_CONFIG_BIO2_DEFAULT_BAUD_VALUE,
"BIO2 bus baudrate (real devices expect 115200)");
}
void bio2drv_config_bio2_get(
struct bio2drv_config_bio2 *config_bio2, struct cconfig *config)
{
if (!cconfig_util_get_bool(
config,
BIO2DRV_CONFIG_BIO2_AUTO_KEY,
&config_bio2->autodetect,
BIO2DRV_CONFIG_BIO2_DEFAULT_AUTO_VALUE)) {
log_warning(
"Invalid value for key '%s' specified, fallback "
"to default '%d'",
BIO2DRV_CONFIG_BIO2_AUTO_KEY,
BIO2DRV_CONFIG_BIO2_DEFAULT_AUTO_VALUE);
}
if (!cconfig_util_get_str(
config,
BIO2DRV_CONFIG_BIO2_PORT_KEY,
config_bio2->port,
sizeof(config_bio2->port) - 1,
BIO2DRV_CONFIG_BIO2_DEFAULT_PORT_VALUE)) {
log_warning(
"Invalid value for key '%s' specified, fallback "
"to default '%s'",
BIO2DRV_CONFIG_BIO2_PORT_KEY,
BIO2DRV_CONFIG_BIO2_DEFAULT_PORT_VALUE);
}
if (!cconfig_util_get_int(
config,
BIO2DRV_CONFIG_BIO2_BAUD_KEY,
&config_bio2->baud,
BIO2DRV_CONFIG_BIO2_DEFAULT_BAUD_VALUE)) {
log_warning(
"Invalid value for key '%s' specified, fallback "
"to default '%d'",
BIO2DRV_CONFIG_BIO2_BAUD_KEY,
BIO2DRV_CONFIG_BIO2_DEFAULT_BAUD_VALUE);
}
}

View File

@@ -0,0 +1,19 @@
#ifndef BIO2DRV_CONFIG_BIO2_H
#define BIO2DRV_CONFIG_BIO2_H
#include <windows.h>
#include "cconfig/cconfig.h"
struct bio2drv_config_bio2 {
bool autodetect;
char port[64];
int32_t baud;
};
void bio2drv_config_bio2_init(struct cconfig *config);
void bio2drv_config_bio2_get(
struct bio2drv_config_bio2 *config_bio2, struct cconfig *config);
#endif

163
src/main/bio2drv/detect.c Normal file
View File

@@ -0,0 +1,163 @@
#define LOG_MODULE "bio2drv-detect"
#include <initguid.h>
#include <windows.h>
#include <cfgmgr32.h>
#include <setupapi.h>
#include "bio2drv/detect.h"
#include <stdio.h>
#include <string.h>
#include "util/log.h"
DEFINE_GUID(
GUID_COM_BUS_ENUMERATOR,
0x4D36E978,
0xE325,
0x11CE,
0xBF,
0xC1,
0x08,
0x00,
0x2B,
0xE1,
0x03,
0x18);
static char work_buffer[0x400];
static bool check_property(
HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA PDeviceInfoData, DWORD property)
{
if (SetupDiGetDeviceRegistryPropertyA(
DeviceInfoSet,
PDeviceInfoData,
property,
NULL,
(BYTE *) work_buffer,
sizeof(work_buffer),
NULL)) {
if (strstr(work_buffer, "BIO2(VIDEO)")) {
log_info("Found matching device: %s", work_buffer);
return true;
}
}
log_info("Device with property: %s does not match", work_buffer);
return false;
}
static bool check_id(
HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA PDeviceInfoData)
{
if (CM_Get_Device_IDA(PDeviceInfoData->DevInst, work_buffer, sizeof(work_buffer), 0)) {
return false;
}
if (strstr(work_buffer, "VID_1CCF&PID_804C")) {
log_info("Found matching device: %s", work_buffer);
return true;
}
if (strstr(work_buffer, "VID_1CCF&PID_8040")) {
log_info("Found matching device: %s", work_buffer);
return true;
}
log_info("Device with ID: %s does not match", work_buffer);
return false;
}
static bool get_device_by_filter(
bool id_filter, DWORD property, size_t devnum, char *path, size_t length)
{
HDEVINFO DeviceInfoSet = SetupDiGetClassDevsA(
&GUID_COM_BUS_ENUMERATOR, NULL, NULL, DIGCF_PRESENT);
if (DeviceInfoSet == INVALID_HANDLE_VALUE) {
return false;
}
SP_DEVINFO_DATA DeviceInfoData;
ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
DWORD idx = 0;
size_t num_found = 0;
while (SetupDiEnumDeviceInfo(DeviceInfoSet, idx, &DeviceInfoData)) {
HKEY DeviceRegKey = SetupDiOpenDevRegKey(
DeviceInfoSet,
&DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
KEY_QUERY_VALUE);
if (DeviceRegKey) {
bool found = false;
log_info("Found a serial device at index: %ld", idx);
if (!id_filter) {
if (check_property(DeviceInfoSet, &DeviceInfoData, property)) {
found = true;
}
} else {
if (check_id(DeviceInfoSet, &DeviceInfoData)) {
found = true;
}
}
if (found) {
if (num_found == devnum) {
DWORD path_length = length;
RegQueryValueExA(
DeviceRegKey,
"PortName",
0,
NULL,
(BYTE *) path,
&path_length);
log_info("Using port: %s", path);
return true;
}
num_found++;
}
}
++idx;
}
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
log_warning("No matching device found");
return false;
}
void bio2drv_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
}
bool bio2drv_detect(
enum bio2drv_detect_mode mode, size_t index, char *path, size_t length)
{
if (mode == DETECT_DEVICEDESC) {
return get_device_by_filter(
false, SPDRP_DEVICEDESC, index, path, length);
} else if (mode == DETECT_FRIENDLYNAME) {
return get_device_by_filter(
false, SPDRP_FRIENDLYNAME, index, path, length);
} else if (mode == DETECT_DEVICEID) {
return get_device_by_filter(true, -1, index, path, length);
}
log_warning("Unknown autodetect mode: %d", mode);
return false;
}

23
src/main/bio2drv/detect.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef BIO2DRV_DETECT_H
#define BIO2DRV_DETECT_H
#include <stdbool.h>
#include "bemanitools/glue.h"
enum bio2drv_detect_mode {
DETECT_DEVICEDESC = 0x1, // look for serial devices containing BIO2(VIDEO)
DETECT_FRIENDLYNAME = 0x2, // look for serial devices containing BIO2(VIDEO)
DETECT_DEVICEID = 0x3, // look for serial devices by vid/pid
};
void bio2drv_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal);
bool bio2drv_detect(
enum bio2drv_detect_mode mode, size_t devnum, char *path, size_t length);
#endif

View File

@@ -0,0 +1,7 @@
libs += bio2emu-iidx
libs_bio2emu-iidx := \
bio2emu \
src_bio2emu-iidx := \
bi2a.c \

View File

@@ -1,7 +1,6 @@
#define LOG_MODULE "bio2emu-bi2a"
#define LOG_MODULE "bio2emu-iidx"
#include "iidxhook8/bi2a.h"
#include "bio2emu/emu.h"
#include "bio2emu-iidx/bi2a.h"
#include <windows.h> /* for _BitScanForward */

View File

@@ -0,0 +1,12 @@
#ifndef BIO2EMU_IIDX_BI2A_H
#define BIO2EMU_IIDX_BI2A_H
#include "bio2/bi2a-iidx.h"
#include "bio2emu/emu.h"
void bio2_emu_bi2a_init(struct bio2emu_port *in, bool disable_poll_limiter);
void bio2_emu_bi2a_dispatch_request(
struct bio2emu_port *bio2port, const struct ac_io_message *req);
#endif

View File

@@ -69,7 +69,7 @@ bio2emu_port_dispatch_irp(struct irp *irp)
}
if (!selected_emu) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
struct ac_io_emu *emu = &selected_emu->acio;

View File

@@ -350,8 +350,8 @@ static CONFIGRET my_CM_Get_Device_IDA(
log_info("%s: Injecting custom parent ID for BIO2", __FUNCTION__);
strcpy(Buffer, devpath);
Buffer[devpathsize - 1] =
'\0' + (dnDevInst & CUSTOM_DEVICE_INSTANCE_IDXMASK);
Buffer[devpathsize - 2] =
'0' + (dnDevInst & CUSTOM_DEVICE_INSTANCE_IDXMASK);
log_info("%s: %s", __FUNCTION__, Buffer);
return CR_SUCCESS;
}

View File

@@ -52,7 +52,7 @@ ac_io_bus_dispatch_irp(struct irp *irp)
log_assert(irp != NULL);
if (!ac_io_emu_match_irp(&ac_io_emu, irp)) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
for (;;) {

View File

@@ -20,10 +20,6 @@
#include "util/defs.h"
#include "util/log.h"
static const irp_handler_t bsthook_handlers[] = {
ac_io_bus_dispatch_irp,
};
static bool my_dll_entry_init(char *sidcode, struct property_node *config);
static bool my_dll_entry_main(void);
@@ -116,7 +112,7 @@ BOOL WINAPI DllMain(HMODULE self, DWORD reason, void *ctx)
args_free(argc, argv);
app_hook_init(my_dll_entry_init, my_dll_entry_main);
iohook_init(bsthook_handlers, lengthof(bsthook_handlers));
iohook_push_handler(ac_io_bus_dispatch_irp);
rs232_hook_init();
gfx_init();

View File

@@ -41,7 +41,7 @@ static HRESULT STDCALL my_CreateDevice(
D3DPRESENT_PARAMETERS *pp,
IDirect3DDevice9 **pdev)
{
IDirect3D9 *real = COM_PROXY_UNWRAP(self);
IDirect3D9 *real = com_proxy_downcast(self)->real;
HRESULT hr;
log_misc("IDirect3D9::CreateDevice hook hit");
@@ -61,11 +61,17 @@ static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver)
IDirect3D9 *api;
IDirect3D9Vtbl *api_vtbl;
struct com_proxy *api_proxy;
HRESULT res;
log_info("Direct3DCreate9 hook hit");
api = real_Direct3DCreate9(sdk_ver);
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
res = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
if (res != S_OK) {
log_fatal("Wrapping com proxy failed: 0x%ld", res);
}
api_vtbl = api_proxy->vptr;
api_vtbl->CreateDevice = my_CreateDevice;

View File

@@ -341,7 +341,14 @@ static HRESULT my_MFEnumDeviceSources(
for (UINT32 i = 0; i < nsrcs; ++i) {
api = (*pppSourceActivate)[i];
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
ret = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
if (ret != S_OK) {
log_warning("Wrapping com proxy failed: %08lx", ret);
return ret;
}
api_vtbl = api_proxy->vptr;
real_GetAllocatedString = api_vtbl->GetAllocatedString;

View File

@@ -160,7 +160,6 @@ static const struct light_def ddr_lights[] = {
{0x2A, IDS_DDR_HD_P2_SPEAKER_W_G},
{0x2B, IDS_DDR_HD_P2_SPEAKER_W_B},
// EXTIO
{0x1E, IDS_DDR_P1_UP},
{0x1D, IDS_DDR_P1_DOWN},

View File

@@ -242,7 +242,7 @@ static HRESULT STDCALL my_CreateDeviceEx(
D3DDISPLAYMODEEX *fdm,
IDirect3DDevice9Ex **pdev)
{
IDirect3D9Ex *real = COM_PROXY_UNWRAP(self);
IDirect3D9Ex *real = com_proxy_downcast(self)->real;
HRESULT hr;
if (d3d9ex_device_adapter >= 0) {
@@ -337,7 +337,14 @@ static HRESULT STDCALL my_Direct3DCreate9Ex(UINT sdk_ver, IDirect3D9Ex **api)
hr = real_Direct3DCreate9Ex(sdk_ver, api);
api_ = *api;
api_proxy = com_proxy_wrap(api_, sizeof(*api_->lpVtbl));
hr = com_proxy_wrap(&api_proxy, api_, sizeof(*api_->lpVtbl));
if (hr != S_OK) {
log_warning("Wrapping com proxy failed: %08lx", hr);
return hr;
}
api_vtbl = api_proxy->vptr;
api_vtbl->CreateDeviceEx = my_CreateDeviceEx;

View File

@@ -107,7 +107,7 @@ com4_dispatch_irp(struct irp *irp)
log_assert(irp != NULL);
if (!ac_io_emu_match_irp(&com4_ac_io_emu, irp)) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
for (;;) {

View File

@@ -72,10 +72,18 @@ static HRESULT STDCALL my_DirectInput8Create(
res = real_DirectInput8Create(
hinst, dwVersion, riidltf, (LPVOID *) &api, punkOuter);
if (res != DI_OK) {
return res;
}
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
res = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
if (res != S_OK) {
log_warning("Wrapping com proxy failed: %08lx", res);
return res;
}
api_vtbl = api_proxy->vptr;
api_vtbl->EnumDevices = my_EnumDevices;

View File

@@ -32,24 +32,6 @@ static bool my_dll_entry_main(void);
bool standard_def;
bool _15khz;
static const irp_handler_t ddrhook_com4_handlers[] = {
p3io_emu_dispatch_irp,
extio_dispatch_irp,
spike_dispatch_irp,
usbmem_dispatch_irp,
com4_dispatch_irp,
};
static const irp_handler_t ddrhook_handlers[] = {
/* Same as above, but without COM4 emulation.
See ddrhook/p3io.c for details. */
p3io_emu_dispatch_irp,
extio_dispatch_irp,
spike_dispatch_irp,
usbmem_dispatch_irp,
};
static bool my_dll_entry_init(char *sidcode, struct property_node *param)
{
int argc;
@@ -90,10 +72,14 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
args_free(argc, argv);
iohook_push_handler(p3io_emu_dispatch_irp);
iohook_push_handler(extio_dispatch_irp);
iohook_push_handler(spike_dispatch_irp);
iohook_push_handler(usbmem_dispatch_irp);
if (com4) {
iohook_init(ddrhook_com4_handlers, lengthof(ddrhook_com4_handlers));
} else {
iohook_init(ddrhook_handlers, lengthof(ddrhook_handlers));
/* See ddrhook/p3io.c for details. */
iohook_push_handler(com4_dispatch_irp);
}
rs232_hook_init();

View File

@@ -33,7 +33,13 @@ void extio_init(void)
{
log_assert(extio_fd == NULL);
extio_fd = iohook_open_dummy_fd();
HRESULT hr;
hr = iohook_open_nul_fd(&extio_fd);
if (hr != S_OK) {
log_fatal("Opening nul fd failed: %08lx", hr);
}
}
void extio_fini(void)
@@ -51,7 +57,7 @@ extio_dispatch_irp(struct irp *irp)
log_assert(irp != NULL);
if (irp->op != IRP_OP_OPEN && irp->fd != extio_fd) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
switch (irp->op) {
@@ -75,7 +81,7 @@ static HRESULT extio_open(struct irp *irp)
log_assert(irp != NULL);
if (!wstr_eq(irp->open_filename, L"COM1")) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
log_info("EXTIO RS232 port opened");

View File

@@ -43,7 +43,7 @@ static HRESULT STDCALL my_CreateDevice(
{
IDirect3D9 *real;
real = COM_PROXY_UNWRAP(self);
real = com_proxy_downcast(self)->real;
if (gfx_windowed) {
pp->Windowed = TRUE;
@@ -58,11 +58,18 @@ static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver)
IDirect3D9 *api;
IDirect3D9Vtbl *api_vtbl;
struct com_proxy *api_proxy;
HRESULT hr;
log_info("Direct3DCreate9 hook hit");
api = real_Direct3DCreate9(sdk_ver);
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
hr = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
if (hr != S_OK) {
log_fatal("Wrapping com proxy failed: %08lx", hr);
}
api_vtbl = api_proxy->vptr;
api_vtbl->CreateDevice = my_CreateDevice;

View File

@@ -47,7 +47,7 @@ spike_dispatch_irp(struct irp *irp)
log_assert(irp != NULL);
if (!ac_io_emu_match_irp(&spike_ac_io_emu, irp)) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
for (;;) {

View File

@@ -34,7 +34,13 @@ void usbmem_init(void)
{
log_assert(usbmem_fd == NULL);
usbmem_fd = iohook_open_dummy_fd();
HRESULT hr;
hr = iohook_open_nul_fd(&usbmem_fd);
if (hr != S_OK) {
log_fatal("Opening nul fd failed: %08lx", hr);
}
}
void usbmem_fini(void)
@@ -52,7 +58,7 @@ usbmem_dispatch_irp(struct irp *irp)
log_assert(irp != NULL);
if (irp->op != IRP_OP_OPEN && irp->fd != usbmem_fd) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
switch (irp->op) {
@@ -76,7 +82,7 @@ static HRESULT usbmem_open(struct irp *irp)
log_assert(irp != NULL);
if (!wstr_eq(irp->open_filename, L"COM3")) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
irp->fd = usbmem_fd;

View File

@@ -95,7 +95,13 @@ static HRESULT STDCALL my_DirectInput8Create(
return res;
}
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
res = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
if (res != S_OK) {
log_warning("Wrapping com proxy failed: %08lx", res);
return res;
}
api_vtbl = api_proxy->vptr;
api_vtbl->EnumDevices = my_EnumDevices;

View File

@@ -53,7 +53,14 @@ void ezusb_emu_device_hook_init(struct ezusb_emu_msg_hook *msg_hook)
{
log_assert(ezusb_emu_fd == NULL);
ezusb_emu_fd = iohook_open_dummy_fd();
HRESULT hr;
hr = iohook_open_nul_fd(&ezusb_emu_fd);
if (hr != S_OK) {
log_fatal("Opening nul fd failed: %08lx", hr);
}
ezusb_emu_dev_fx_msg_hook = msg_hook;
}
@@ -70,7 +77,7 @@ HRESULT
ezusb_emu_device_dispatch_irp(struct irp *irp)
{
if (irp->op != IRP_OP_OPEN && irp->fd != ezusb_emu_fd) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
/* read/write are not supported, and the game-side EZUSB code constantly
@@ -98,7 +105,7 @@ static HRESULT ezusb_open(struct irp *irp)
log_assert(irp != NULL);
if (!wstr_eq(irp->open_filename, L"\\\\.\\Ezusb-0")) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
irp->fd = ezusb_emu_fd;

View File

@@ -160,6 +160,10 @@ static HRESULT ezusb_iidx_emu_msg_interrupt_read(struct iobuf *read)
otherwise the game's fpga check will fail */
msg_resp->fpga2_check_flag_unkn = 2;
#ifdef EZUSB_IIDX_EMU_D01_BOARD
msg_resp->inverted_pad &= ~(1 << 4);
#endif
read->pos = sizeof(*msg_resp);
return S_OK;

View File

@@ -59,7 +59,9 @@ struct ezusb_iidx_msg_interrupt_read_packet {
1: Not used
2: Not used
3: Not used
4: Not used
4: C02/D01 board identifier, 1 = C02, 0 = D01 (on active low). This defines how the game has
to flash the FPGA board since D01 needs a different firmware (see result of FPGA CHECK on
game bootup)
5: Not used
6: usb mute?
7: Not used

View File

@@ -67,7 +67,14 @@ void ezusb2_emu_device_hook_init(struct ezusb_emu_msg_hook *msg_hook)
{
log_assert(ezusb_fd == NULL);
ezusb_fd = iohook_open_dummy_fd();
HRESULT hr;
hr = iohook_open_nul_fd(&ezusb_fd);
if (hr != S_OK) {
log_fatal("Opening nul fd failed: %08lx", hr);
}
ezusb_emu_dev_fx2_msg_hook = msg_hook;
}
@@ -88,7 +95,7 @@ HRESULT
ezusb2_emu_device_dispatch_irp(struct irp *irp)
{
if (irp->op != IRP_OP_OPEN && irp->fd != ezusb_fd) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
/* read/write are not supported, and the game-side EZUSB code constantly
@@ -112,7 +119,7 @@ static HRESULT ezusb_open(struct irp *irp)
log_assert(irp != NULL);
if (!wstr_eq(irp->open_filename, L"\\\\.\\Ezusb-0")) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
irp->fd = ezusb_fd;

View File

@@ -1,10 +1,14 @@
libs += hook
src_hook := \
args.c \
com-proxy.c \
d3d9.c \
hr.c \
iobuf.c \
iohook.c \
pe.c \
peb.c \
process.c \
table.c \

137
src/main/hook/args.c Normal file
View File

@@ -0,0 +1,137 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "hook/args.h"
/* This does not handle escaped double quotes inside args correctly yet */
static HRESULT args_push(
int *argc,
char ***argv,
const char *begin,
const char *end)
{
int tmp_argc;
char **tmp_argv;
size_t nchars;
char *str;
nchars = end - begin;
str = malloc(nchars + 1);
if (str == NULL) {
goto fail;
}
memcpy(str, begin, nchars);
str[nchars] = '\0';
tmp_argc = *argc + 1;
tmp_argv = realloc(*argv, tmp_argc * sizeof(char **));
if (tmp_argv == NULL) {
goto fail;
}
tmp_argv[tmp_argc - 1] = str;
*argv = tmp_argv;
*argc = tmp_argc;
return S_OK;
fail:
free(str);
return E_OUTOFMEMORY;
}
HRESULT args_recover(int *argc_out, char ***argv_out)
{
int argc;
char **argv;
const char *begin;
const char *pos;
bool quote;
HRESULT hr;
assert(argc_out != NULL);
assert(argv_out != NULL);
*argc_out = 0;
*argv_out = NULL;
argc = 0;
argv = NULL;
quote = false;
for (begin = pos = GetCommandLine() ; *pos ; pos++) {
switch (*pos) {
case '"':
if (!quote) {
quote = true;
begin = pos + 1;
} else {
hr = args_push(&argc, &argv, begin, pos);
if (FAILED(hr)) {
goto fail;
}
quote = false;
begin = NULL;
}
break;
case ' ':
if (!quote && begin != NULL) {
args_push(&argc, &argv, begin, pos);
begin = NULL;
}
break;
default:
if (begin == NULL) {
begin = pos;
}
break;
}
}
if (begin != NULL && !quote) {
hr = args_push(&argc, &argv, begin, pos);
if (FAILED(hr)) {
goto fail;
}
}
*argc_out = argc;
*argv_out = argv;
return S_OK;
fail:
args_free(argc, argv);
return hr;
}
void args_free(int argc, char **argv)
{
int i;
for (i = 0 ; i < argc ; i++) {
free(argv[i]);
}
free(argv);
}

6
src/main/hook/args.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <windows.h>
HRESULT args_recover(int *argc, char ***argv);
void args_free(int argc, char **argv);

View File

@@ -1,90 +1,177 @@
#include <unknwn.h>
#include <windows.h>
#include <unknwn.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include "hook/com-proxy.h"
#include "util/defs.h"
#include "util/mem.h"
static void com_proxy_free(struct com_proxy *proxy);
static HRESULT STDMETHODCALLTYPE com_proxy_query_interface(
IUnknown *unk,
REFIID iid,
void **iface);
static ULONG STDMETHODCALLTYPE com_proxy_addref(IUnknown *unk);
static ULONG STDMETHODCALLTYPE com_proxy_release(IUnknown *unk);
#ifdef _WIN64
#ifdef __amd64
/***** 64-BIT TRAMPOLINE *****/
/***** 64-BIT TRAMPOLINE *****/
#define SLOT_OFFSET 0x0A
static const uint8_t com_proxy_tramp[] = {
/* mov rcx, [rcx+8] ; Replace this with this->real */
0x48,
0x8B,
0x49,
0x08,
/* mov rcx, [rcx+8] ; Get this->real */
0x48, 0x8B, 0x49, 0x08,
/* mov rax, [rcx] ; Get real->vtbl */
0x48,
0x8B,
0x01,
/* mov rax, [rcx] ; Get this->vtbl */
0x48, 0x8B, 0x01,
/* mov rax, [rax+XX] ; Get vtbl->slot_XX */
0x48,
0x8B,
0x80,
-1,
-1,
-1,
-1,
0x48, 0x8B, 0x80, -1, -1, -1, -1,
/* jmp rax ; Continue to slot_XX */
0xFF,
0xE0,
0xFF, 0xE0,
};
#else
/***** 32-BIT TRAMPOLINE *****/
/***** 32-BIT TRAMPOLINE *****/
#define SLOT_OFFSET 0x0F
static const uint8_t com_proxy_tramp[] = {
/* mov eax, [esp+4] ; Get this */
0x8B,
0x44,
0x24,
0x04,
0x8B, 0x44, 0x24, 0x04,
/* mov eax, [eax+4] ; Get this->real */
0x8B,
0x40,
0x04,
0x8B, 0x40, 0x04,
/* mov [esp+4], eax ; Replace this with this->real on stack */
0x89,
0x44,
0x24,
0x04,
0x89, 0x44, 0x24, 0x04,
/* mov ecx, [eax] ; Get real->vtbl */
0x8B,
0x08,
/* mov ecx, [eax] ; Get this->vtbl */
0x8B, 0x08,
/* mov ecx, [ecx+XX] ; Get vtbl->slot_XX */
0x8B,
0x89,
-1,
-1,
-1,
-1,
0x8B, 0x89, -1, -1, -1, -1,
/* jmp ecx ; Continue to slot_XX */
0xFF,
0xE1};
0xFF, 0xE1
};
#endif
static HRESULT STDCALL
com_proxy_query_interface(IUnknown *ptr, REFIID iid, void **iface)
HRESULT com_proxy_wrap(
struct com_proxy **out,
void *real,
size_t vtbl_size)
{
struct com_proxy *self = (struct com_proxy *) ptr;
IUnknown *obj = self->real; /* Not necessarily the real IUnknown* */
struct com_proxy *proxy;
void **vtbl;
uint8_t *cur_tramp;
size_t nslots;
size_t i;
HRESULT hr;
assert(out != NULL);
assert(real != NULL);
*out = NULL;
proxy = calloc(1, sizeof(*proxy));
if (proxy == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
proxy->vptr = malloc(vtbl_size);
if (proxy->vptr == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
nslots = vtbl_size / sizeof(void *);
proxy->tramps = VirtualAlloc(
NULL,
sizeof(com_proxy_tramp) * nslots,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (proxy->tramps == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
proxy->real = real;
/* Set up proxied IUnknown impl */
vtbl = proxy->vptr;
vtbl[0] = com_proxy_query_interface;
vtbl[1] = com_proxy_addref;
vtbl[2] = com_proxy_release;
/* Populate trampoline code for remaining vtbl entries */
for (i = 3 /* Skip IUnknown */ ; i < nslots ; i++) {
cur_tramp = proxy->tramps + i * sizeof(com_proxy_tramp);
/* Copy template */
memcpy(cur_tramp, com_proxy_tramp, sizeof(com_proxy_tramp));
/* Patch XX into vtbl lookup (see definition of tramp) */
*((uint32_t *) (cur_tramp + SLOT_OFFSET)) = i * sizeof(void *);
/* Set vtable entry */
vtbl[i] = cur_tramp;
}
*out = proxy;
proxy = NULL;
hr = S_OK;
end:
com_proxy_free(proxy);
return hr;
}
static void com_proxy_free(struct com_proxy *proxy)
{
if (proxy == NULL) {
return;
}
if (proxy->cleanup_ctx != NULL) {
proxy->cleanup_ctx(proxy->ctx);
}
if (proxy->tramps != NULL) {
VirtualFree(proxy->tramps, 0, MEM_RELEASE);
}
free(proxy->vptr);
free(proxy);
}
static HRESULT STDMETHODCALLTYPE com_proxy_query_interface(
IUnknown *unk,
REFIID iid,
void **iface)
{
struct com_proxy *proxy;
IUnknown *obj;
assert(unk != NULL);
proxy = (struct com_proxy *) unk;
obj = proxy->real; /* Not necessarily this object's canonical IUnknown */
/* To some extent, COM is designed to support shennanigans like these.
We can safely pass the call straight through to the underlying
@@ -104,70 +191,35 @@ com_proxy_query_interface(IUnknown *ptr, REFIID iid, void **iface)
return IUnknown_QueryInterface(obj, iid, iface);
}
static ULONG STDCALL com_proxy_addref(IUnknown *ptr)
static ULONG STDMETHODCALLTYPE com_proxy_addref(IUnknown *unk)
{
struct com_proxy *self = (struct com_proxy *) ptr;
IUnknown *obj = self->real;
struct com_proxy *proxy;
IUnknown *obj;
assert(unk != NULL);
proxy = (struct com_proxy *) unk;
obj = proxy->real;
return IUnknown_AddRef(obj);
}
static ULONG STDCALL com_proxy_release(IUnknown *ptr)
static ULONG STDMETHODCALLTYPE com_proxy_release(IUnknown *unk)
{
struct com_proxy *self = (struct com_proxy *) ptr;
IUnknown *obj = self->real;
struct com_proxy *proxy;
IUnknown *real;
ULONG result;
result = IUnknown_Release(obj);
assert(unk != NULL);
proxy = (struct com_proxy *) unk;
real = proxy->real;
result = IUnknown_Release(real);
if (!result) {
/* Last ref to underlying object released */
VirtualFree(self->tramps, 0, MEM_RELEASE);
free(self->vptr);
free(self);
com_proxy_free(proxy);
}
return result;
}
struct com_proxy *com_proxy_wrap(void *iface, size_t vtbl_size)
{
struct com_proxy *self;
void **vtbl;
uint8_t *cur_tramp;
uint32_t nslots;
uint32_t i;
nslots = vtbl_size / sizeof(void *);
self = xmalloc(sizeof(*self));
self->vptr = xmalloc(vtbl_size);
self->real = iface;
self->tramps = VirtualAlloc(
NULL,
sizeof(com_proxy_tramp) * nslots,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
/* Set up proxied IUnknown impl */
vtbl = self->vptr;
vtbl[0] = com_proxy_query_interface;
vtbl[1] = com_proxy_addref;
vtbl[2] = com_proxy_release;
/* Populate trampoline code for remaining vtbl entries */
for (i = 3 /* Skip IUnknown */; i < nslots; i++) {
cur_tramp = self->tramps + i * sizeof(com_proxy_tramp);
/* Copy template */
memcpy(cur_tramp, com_proxy_tramp, sizeof(com_proxy_tramp));
/* Patch XX into vtbl lookup (see definition of tramp) */
*((uint32_t *) (cur_tramp + SLOT_OFFSET)) = i * sizeof(void *);
/* Set vtable entry */
vtbl[i] = cur_tramp;
}
return self;
}

View File

@@ -1,14 +1,9 @@
#ifndef HOOK_COM_PROXY_H
#define HOOK_COM_PROXY_H
#pragma once
#include <stddef.h>
#include <stdint.h>
/* N.B. Here be dragons. You really ought to be fairly familiar with COM
before using this, otherwise you risk ignoring the subtler issues at your
peril. */
#define COM_PROXY_UNWRAP(self) (((struct com_proxy *) self)->real)
#define com_proxy_downcast(self) ((struct com_proxy *) self)
struct com_proxy {
/* Pointer to vtable filled with trampolines. Edit these as you please.
@@ -18,13 +13,20 @@ struct com_proxy {
/* Interface pointer wrapped by this proxy. */
void *real;
/* Context pointer for use by hook code. */
void *ctx;
/* Optional cleanup callback, will be called during com_proxy deallocation
to clean up *ctx. */
void (*cleanup_ctx)(void *ctx);
/* Dynamically generated x86 trampoline code. The initial vtable entries
all point into code located here. */
uint8_t *tramps;
};
/* Wrap a COM interface pointer in a proxy. This is an object that acts just
like the object that it wraps, but has a freely editable vtable, which you
like the object that it wraps but has a freely editable vtable, which you
can modify in order to intercept a subset of the interface's method calls.
By default, all the vtable slots contain dynamically generated trampolines
@@ -34,9 +36,8 @@ struct com_proxy {
NOTE! This does not AddRef the underlying interface.
NOTE! This function wraps COM POINTERS, not COM OBJECTS (since the latter
is, in general, impossible). If you're insufficiently versed in COM to
understand the difference... well, you really should be, but the following
observations are a start:
is, in general, impossible). Consequences of this distinction include the
following:
1. Do not wrap IUnknown pointers with this function. This will break the
IUnknown::QueryInterface contract. This refers to _the_ unique
@@ -44,11 +45,11 @@ struct com_proxy {
extends IUnknown). Wrapping the unique IUnknown* for an object will
cause it to no longer be unique.
2. Callers can "jailbreak" your wrapper using IUnknown::QueryInterface.
2. Callers can "jailbreak" your wrapper using IUnknown::QueryInterface
unless you provide a custom QueryInterface implementation to prevent them
from doing so. */
Generally this isn't an issue for DirectX objects, since nobody ever seems
to use QueryInterface with them. */
struct com_proxy *com_proxy_wrap(void *iface, size_t vtbl_size);
#endif
HRESULT com_proxy_wrap(
struct com_proxy **out,
void *real,
size_t vtbl_size);

View File

@@ -517,6 +517,7 @@ static HRESULT hook_d3d9_irp_handler_real_ctx_create(struct hook_d3d9_irp *irp)
IDirect3D9 *api;
IDirect3D9Vtbl *api_vtbl;
struct com_proxy *api_proxy;
HRESULT res;
log_assert(irp);
@@ -526,7 +527,12 @@ static HRESULT hook_d3d9_irp_handler_real_ctx_create(struct hook_d3d9_irp *irp)
return E_FAIL;
}
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
res = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
if (res != S_OK) {
log_fatal("Wrapping com proxy failed: %08lx", res);
}
api_vtbl = api_proxy->vptr;
real_CreateDevice = api_vtbl->CreateDevice;
@@ -624,7 +630,13 @@ hook_d3d9_irp_handler_real_dev_create_device(struct hook_d3d9_irp *irp)
}
api = *irp->args.ctx_create_device.pdev;
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
hr = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
if (hr != S_OK) {
log_warning("Wrapping com proxy failed: %08lx", hr);
return hr;
}
api_vtbl = api_proxy->vptr;
real_CreateTexture = api_vtbl->CreateTexture;

33
src/main/hook/hr.c Normal file
View File

@@ -0,0 +1,33 @@
#include <windows.h>
#include <stdint.h>
#include "hook/hr.h"
uint32_t hr_to_win32_error(HRESULT hr)
{
if (SUCCEEDED(hr)) {
return ERROR_SUCCESS;
} else if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
return HRESULT_CODE(hr);
} else {
switch (hr) {
case E_ABORT: return ERROR_OPERATION_ABORTED;
case E_ACCESSDENIED: return ERROR_ACCESS_DENIED;
case E_FAIL: return ERROR_GEN_FAILURE;
case E_HANDLE: return ERROR_INVALID_HANDLE;
case E_INVALIDARG: return ERROR_INVALID_PARAMETER;
case E_NOINTERFACE: return ERROR_INVALID_FUNCTION;
case E_NOTIMPL: return ERROR_NOT_SUPPORTED;
case E_OUTOFMEMORY: return ERROR_OUTOFMEMORY;
case E_POINTER: return ERROR_INVALID_ADDRESS;
case E_UNEXPECTED: return ERROR_INTERNAL_ERROR;
default: return ERROR_INTERNAL_ERROR;
}
}
}
void hr_propagate_win32_(HRESULT hr)
{
SetLastError(hr_to_win32_error(hr));
}

View File

@@ -1,11 +1,10 @@
#ifndef UTIL_HR_H
#define UTIL_HR_H
#pragma once
#include <windows.h>
#include <stdint.h>
#define hr_propagate_win32(hr, r) (hr_propagate_win32_(hr), r)
HRESULT hr_from_win32(void);
uint32_t hr_to_win32_error(HRESULT hr);
void hr_propagate_win32_(HRESULT hr);
#endif

343
src/main/hook/iobuf.c Normal file
View File

@@ -0,0 +1,343 @@
#include <assert.h>
#include <string.h>
#include "hook/iobuf.h"
void iobuf_flip(struct const_iobuf *child, struct iobuf *parent)
{
assert(child != NULL);
assert(parent != NULL);
child->bytes = parent->bytes;
child->pos = 0;
child->nbytes = parent->pos;
}
size_t iobuf_move(struct iobuf *dest, struct const_iobuf *src)
{
size_t dest_avail;
size_t src_avail;
size_t chunksz;
assert(dest != NULL);
assert(dest->bytes != NULL || dest->nbytes == 0);
assert(dest->pos <= dest->nbytes);
assert(src != NULL);
assert(src->bytes != NULL || src->nbytes == 0);
assert(src->pos <= src->nbytes);
dest_avail = dest->nbytes - dest->pos;
src_avail = src->nbytes - src->pos;
chunksz = dest_avail < src_avail ? dest_avail : src_avail;
memcpy(&dest->bytes[dest->pos], &src->bytes[src->pos], chunksz);
dest->pos += chunksz;
src->pos += chunksz;
return chunksz;
}
size_t iobuf_shift(struct iobuf *dest, struct iobuf *src)
{
struct const_iobuf span;
assert(dest != NULL);
assert(src != NULL);
iobuf_flip(&span, src);
iobuf_move(dest, &span);
memmove(src->bytes, &src->bytes[span.pos], span.nbytes - span.pos);
src->pos -= span.pos;
return span.pos;
}
HRESULT iobuf_read(struct const_iobuf *src, void *bytes, size_t nbytes)
{
assert(src != NULL);
assert(bytes != NULL || nbytes == 0);
if (src->pos + nbytes > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
memcpy(bytes, &src->bytes[src->pos], nbytes);
src->pos += nbytes;
return S_OK;
}
HRESULT iobuf_read_8(struct const_iobuf *src, uint8_t *out)
{
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint8_t) > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
*out = src->bytes[src->pos++];
return S_OK;
}
HRESULT iobuf_read_be16(struct const_iobuf *src, uint16_t *out)
{
uint16_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint16_t) > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
value = src->bytes[src->pos++] << 8;
value |= src->bytes[src->pos++];
*out = value;
return S_OK;
}
HRESULT iobuf_read_be32(struct const_iobuf *src, uint32_t *out)
{
uint32_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint32_t) > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
value = src->bytes[src->pos++] << 24;
value |= src->bytes[src->pos++] << 16;
value |= src->bytes[src->pos++] << 8;
value |= src->bytes[src->pos++];
*out = value;
return S_OK;
}
HRESULT iobuf_read_be64(struct const_iobuf *src, uint64_t *out)
{
uint64_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint64_t) > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
value = ((uint64_t) src->bytes[src->pos++]) << 56;
value |= ((uint64_t) src->bytes[src->pos++]) << 48;
value |= ((uint64_t) src->bytes[src->pos++]) << 40;
value |= ((uint64_t) src->bytes[src->pos++]) << 32;
value |= ((uint64_t) src->bytes[src->pos++]) << 24;
value |= ((uint64_t) src->bytes[src->pos++]) << 16;
value |= ((uint64_t) src->bytes[src->pos++]) << 8;
value |= ((uint64_t) src->bytes[src->pos++]);
*out = value;
return S_OK;
}
HRESULT iobuf_read_le16(struct const_iobuf *src, uint16_t *out)
{
uint16_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint16_t) > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
value = src->bytes[src->pos++];
value |= src->bytes[src->pos++] << 8;
*out = value;
return S_OK;
}
HRESULT iobuf_read_le32(struct const_iobuf *src, uint32_t *out)
{
uint32_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint32_t) > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
value = src->bytes[src->pos++];
value |= src->bytes[src->pos++] << 8;
value |= src->bytes[src->pos++] << 16;
value |= src->bytes[src->pos++] << 24;
*out = value;
return S_OK;
}
HRESULT iobuf_read_le64(struct const_iobuf *src, uint64_t *out)
{
uint64_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint64_t) > src->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
value = ((uint64_t) src->bytes[src->pos++]);
value |= ((uint64_t) src->bytes[src->pos++]) << 8;
value |= ((uint64_t) src->bytes[src->pos++]) << 16;
value |= ((uint64_t) src->bytes[src->pos++]) << 24;
value |= ((uint64_t) src->bytes[src->pos++]) << 32;
value |= ((uint64_t) src->bytes[src->pos++]) << 40;
value |= ((uint64_t) src->bytes[src->pos++]) << 48;
value |= ((uint64_t) src->bytes[src->pos++]) << 56;
*out = value;
return S_OK;
}
HRESULT iobuf_write(struct iobuf *dest, const void *bytes, size_t nbytes)
{
assert(dest != NULL);
assert(bytes != NULL || nbytes == 0);
if (dest->pos + nbytes > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
memcpy(&dest->bytes[dest->pos], bytes, nbytes);
dest->pos += nbytes;
return S_OK;
}
HRESULT iobuf_write_8(struct iobuf *dest, uint8_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint8_t) > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = value;
return S_OK;
}
HRESULT iobuf_write_be16(struct iobuf *dest, uint16_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint16_t) > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = value >> 8;
dest->bytes[dest->pos++] = value;
return S_OK;
}
HRESULT iobuf_write_be32(struct iobuf *dest, uint32_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint32_t) > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = value >> 24;
dest->bytes[dest->pos++] = value >> 16;
dest->bytes[dest->pos++] = value >> 8;
dest->bytes[dest->pos++] = value;
return S_OK;
}
HRESULT iobuf_write_be64(struct iobuf *dest, uint64_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint64_t) > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = value >> 56;
dest->bytes[dest->pos++] = value >> 48;
dest->bytes[dest->pos++] = value >> 40;
dest->bytes[dest->pos++] = value >> 32;
dest->bytes[dest->pos++] = value >> 24;
dest->bytes[dest->pos++] = value >> 16;
dest->bytes[dest->pos++] = value >> 8;
dest->bytes[dest->pos++] = value;
return S_OK;
}
HRESULT iobuf_write_le16(struct iobuf *dest, uint16_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint16_t) > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = value;
dest->bytes[dest->pos++] = value >> 8;
return S_OK;
}
HRESULT iobuf_write_le32(struct iobuf *dest, uint32_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint32_t) > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = value;
dest->bytes[dest->pos++] = value >> 8;
dest->bytes[dest->pos++] = value >> 16;
dest->bytes[dest->pos++] = value >> 24;
return S_OK;
}
HRESULT iobuf_write_le64(struct iobuf *dest, uint64_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint64_t) > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = value;
dest->bytes[dest->pos++] = value >> 8;
dest->bytes[dest->pos++] = value >> 16;
dest->bytes[dest->pos++] = value >> 24;
dest->bytes[dest->pos++] = value >> 32;
dest->bytes[dest->pos++] = value >> 40;
dest->bytes[dest->pos++] = value >> 48;
dest->bytes[dest->pos++] = value >> 56;
return S_OK;
}

40
src/main/hook/iobuf.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
struct iobuf {
uint8_t *bytes;
size_t nbytes;
size_t pos;
};
struct const_iobuf {
const uint8_t *bytes;
size_t nbytes;
size_t pos;
};
void iobuf_flip(struct const_iobuf *child, struct iobuf *parent);
size_t iobuf_move(struct iobuf *dest, struct const_iobuf *src);
size_t iobuf_shift(struct iobuf *dest, struct iobuf *src);
HRESULT iobuf_read(struct const_iobuf *src, void *bytes, size_t nbytes);
HRESULT iobuf_read_8(struct const_iobuf *src, uint8_t *value);
HRESULT iobuf_read_be16(struct const_iobuf *src, uint16_t *value);
HRESULT iobuf_read_be32(struct const_iobuf *src, uint32_t *value);
HRESULT iobuf_read_be64(struct const_iobuf *src, uint64_t *value);
HRESULT iobuf_read_le16(struct const_iobuf *src, uint16_t *value);
HRESULT iobuf_read_le32(struct const_iobuf *src, uint32_t *value);
HRESULT iobuf_read_le64(struct const_iobuf *src, uint64_t *value);
HRESULT iobuf_write(struct iobuf *dest, const void *bytes, size_t nbytes);
HRESULT iobuf_write_8(struct iobuf *dest, uint8_t value);
HRESULT iobuf_write_be16(struct iobuf *dest, uint16_t value);
HRESULT iobuf_write_be32(struct iobuf *dest, uint32_t value);
HRESULT iobuf_write_be64(struct iobuf *dest, uint64_t value);
HRESULT iobuf_write_le16(struct iobuf *dest, uint16_t value);
HRESULT iobuf_write_le32(struct iobuf *dest, uint32_t value);
HRESULT iobuf_write_le64(struct iobuf *dest, uint64_t value);

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,11 @@
#ifndef HOOK_IOHOOK_H
#define HOOK_IOHOOK_H
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "util/iobuf.h"
#include "hook/iobuf.h"
enum irp_op {
IRP_OP_OPEN,
@@ -38,10 +37,14 @@ struct irp {
uint64_t seek_pos;
};
typedef HRESULT (*irp_handler_t)(struct irp *irp);
void iohook_init(const irp_handler_t *handlers, size_t nhandlers);
HANDLE iohook_open_dummy_fd(void);
HRESULT irp_invoke_next(struct irp *irp);
typedef HRESULT (*iohook_fn_t)(struct irp *irp);
HANDLE iohook_open_dummy_fd(void)
#ifdef __GNUC__
__attribute__((deprecated("Use iohook_open_nul_fd instead")))
#endif
;
HRESULT iohook_open_nul_fd(HANDLE *fd);
HRESULT iohook_push_handler(iohook_fn_t fn);
HRESULT iohook_invoke_next(struct irp *irp);

View File

@@ -1,20 +1,17 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "hook/pe.h"
#include "util/log.h"
typedef BOOL(WINAPI *dll_main_t)(HMODULE, uint32_t, void *);
static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe);
static uint32_t
pe_get_virtual_size(const IMAGE_SECTION_HEADER *sh, int nsections);
static void *pe_offset(void *ptr, size_t off);
static const void *pe_offsetc(const void *ptr, size_t off);
static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe);
static void *pe_offset(void *ptr, size_t off)
{
@@ -42,26 +39,6 @@ static const void *pe_offsetc(const void *ptr, size_t off)
return base + off;
}
static uint32_t
pe_get_virtual_size(const IMAGE_SECTION_HEADER *sh, int nsections)
{
uint32_t sec_end;
uint32_t size;
int i;
size = 0;
for (i = 0; i < nsections; i++) {
sec_end = sh[i].VirtualAddress + sh[i].Misc.VirtualSize;
if (size < sec_end) {
size = sec_end;
}
}
return size;
}
static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe)
{
const IMAGE_DOS_HEADER *dh;
@@ -76,13 +53,14 @@ static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe)
const pe_iid_t *pe_iid_get_first(HMODULE pe)
{
const IMAGE_NT_HEADERS *nth;
const IMAGE_DATA_DIRECTORY *idd;
const IMAGE_IMPORT_DESCRIPTOR *iid;
assert(pe != NULL);
nth = pe_get_nt_header(pe);
iid = pe_offsetc(
pe,
nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
.VirtualAddress);
idd = &nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
iid = pe_offsetc(pe, idd->VirtualAddress);
if (iid == NULL || iid->Name == 0) {
return NULL;
@@ -93,6 +71,9 @@ const pe_iid_t *pe_iid_get_first(HMODULE pe)
const char *pe_iid_get_name(HMODULE pe, const pe_iid_t *iid)
{
assert(pe != NULL);
assert(iid != NULL);
return pe_offsetc(pe, iid->Name);
}
@@ -100,6 +81,9 @@ const pe_iid_t *pe_iid_get_next(HMODULE pe, const pe_iid_t *iid)
{
const IMAGE_IMPORT_DESCRIPTOR *iid_next;
assert(pe != NULL);
assert(iid != NULL);
iid_next = iid + 1;
if (iid_next->Name != 0) {
@@ -109,27 +93,30 @@ const pe_iid_t *pe_iid_get_next(HMODULE pe, const pe_iid_t *iid)
}
}
bool pe_iid_get_iat_entry(
HMODULE pe, const pe_iid_t *iid, size_t n, struct pe_iat_entry *entry)
HRESULT pe_iid_get_iat_entry(
HMODULE pe,
const pe_iid_t *iid,
size_t n,
struct pe_iat_entry *entry)
{
const IMAGE_IMPORT_BY_NAME *import;
uintptr_t *import_rvas;
intptr_t *import_rvas;
void **pointers;
if (iid->OriginalFirstThunk == 0) {
log_warning("OriginalFirstThunk == 0");
}
assert(pe != NULL);
assert(iid != NULL);
assert(entry != NULL);
import_rvas = pe_offset(pe, iid->OriginalFirstThunk);
if (import_rvas[n] == 0) {
/* End of imports */
entry->name = NULL;
entry->ordinal = 0;
entry->ppointer = NULL;
memset(entry, 0, sizeof(*entry));
return false;
} else if (import_rvas[n] & INTPTR_MIN) {
return S_FALSE;
}
if (import_rvas[n] & INTPTR_MIN) {
/* Ordinal import */
entry->name = NULL;
entry->ordinal = (uint16_t) import_rvas[n];
@@ -143,143 +130,43 @@ bool pe_iid_get_iat_entry(
pointers = pe_offset(pe, iid->FirstThunk);
entry->ppointer = &pointers[n];
return true;
}
void pe_patch_pointer(void **ppointer, void *new_value)
{
DWORD old_protect;
VirtualProtect(
ppointer, sizeof(void *), PAGE_EXECUTE_READWRITE, &old_protect);
*ppointer = new_value;
VirtualProtect(ppointer, sizeof(void *), old_protect, &old_protect);
}
HMODULE
pe_explode(const uint8_t *bytes, uint32_t nbytes)
{
HMODULE base;
const IMAGE_DOS_HEADER *dh;
const IMAGE_NT_HEADERS *nth;
const IMAGE_SECTION_HEADER *sh;
uint32_t virtual_size;
uint32_t vflags;
int i;
dh = (IMAGE_DOS_HEADER *) bytes;
nth = pe_offsetc(bytes, dh->e_lfanew);
sh = pe_offsetc(bytes, dh->e_lfanew + sizeof(*nth));
virtual_size = pe_get_virtual_size(sh, nth->FileHeader.NumberOfSections);
base = (HMODULE) VirtualAlloc(
(void *) nth->OptionalHeader.ImageBase,
virtual_size,
MEM_RESERVE,
PAGE_NOACCESS);
if (base == NULL) {
/* Try again, allowing any base address */
base = (HMODULE) VirtualAlloc(
NULL, virtual_size, MEM_RESERVE, PAGE_NOACCESS);
if (base == NULL) {
/* Aargh */
log_fatal(
"Failed to VirtualAlloc %#x bytes of address space",
virtual_size);
}
}
log_misc(
"Exploding PE, base %p actual %p",
(void *) nth->OptionalHeader.ImageBase,
base);
/* Commit header region */
VirtualAlloc(
(void *) base,
nth->OptionalHeader.SizeOfHeaders,
MEM_COMMIT,
PAGE_READWRITE);
memcpy(base, dh, sizeof(*dh));
memcpy(pe_offset(base, dh->e_lfanew), nth, sizeof(*nth));
memcpy(
pe_offset(base, dh->e_lfanew + sizeof(*nth)),
sh,
sizeof(*sh) * nth->FileHeader.NumberOfSections);
for (i = 0; i < nth->FileHeader.NumberOfSections; i++) {
vflags = sh[i].Characteristics & 0x20000000 ? PAGE_EXECUTE_READWRITE :
PAGE_READWRITE;
VirtualAlloc(
pe_offset(base, sh[i].VirtualAddress),
sh[i].Misc.VirtualSize,
MEM_COMMIT,
vflags);
memcpy(
pe_offset(base, sh[i].VirtualAddress),
pe_offsetc(bytes, sh[i].PointerToRawData),
sh[i].SizeOfRawData);
}
return base;
}
void pe_relocate(HMODULE pe)
{
const IMAGE_NT_HEADERS *nth;
const IMAGE_DATA_DIRECTORY *dde;
const IMAGE_BASE_RELOCATION *chunk;
intptr_t delta_va;
const uint16_t *reloc;
uintptr_t *addr_ptr;
nth = pe_get_nt_header(pe);
delta_va = (intptr_t) pe - nth->OptionalHeader.ImageBase;
dde = nth->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_BASERELOC;
for (chunk = pe_offsetc(pe, dde->VirtualAddress);
(void *) chunk < pe_offsetc(pe, dde->VirtualAddress + dde->Size);
chunk = pe_offsetc(chunk, chunk->SizeOfBlock)) {
for (reloc = (uint16_t *) (chunk + 1);
(void *) reloc < pe_offsetc(chunk, chunk->SizeOfBlock);
reloc++) {
if (*reloc >> 12 == IMAGE_REL_BASED_HIGHLOW) {
addr_ptr =
pe_offset(pe, chunk->VirtualAddress + (*reloc & 0x0FFF));
*addr_ptr += delta_va;
}
}
}
return S_OK;
}
void *pe_get_export(HMODULE pe, const char *name, uint16_t ord)
{
const IMAGE_NT_HEADERS *nth;
const IMAGE_DATA_DIRECTORY *idd;
const IMAGE_EXPORT_DIRECTORY *ied;
const uint32_t *name_rvas;
const uint32_t *target_rvas;
const char *name_va;
DWORD i;
assert(pe != NULL);
nth = pe_get_nt_header(pe);
ied = pe_offsetc(
pe,
nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
.VirtualAddress);
idd = &nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
ied = pe_offsetc(pe, idd->VirtualAddress);
name_rvas = pe_offsetc(pe, ied->AddressOfNames);
target_rvas = pe_offsetc(pe, ied->AddressOfFunctions);
if (name != NULL) {
for (i = 0; i < ied->NumberOfNames; i++) {
if (name_rvas[i] != 0 &&
strcmp(pe_offsetc(pe, name_rvas[i]), name) == 0) {
return pe_offset(pe, target_rvas[i]);
for (i = 0 ; i < ied->NumberOfNames ; i++) {
if (name_rvas[i] == 0) {
/* Ordinal-only export, cannot match against this */
continue;
}
name_va = pe_offsetc(pe, name_rvas[i]);
if (strcmp(name_va, name) != 0) {
/* Name did not match */
continue;
}
return pe_offset(pe, target_rvas[i]);
}
return NULL;
@@ -290,13 +177,46 @@ void *pe_get_export(HMODULE pe, const char *name, uint16_t ord)
}
}
BOOL pe_call_dll_main(HMODULE pe, uint32_t reason, void *ctx)
void *pe_get_entry_point(HMODULE pe)
{
const IMAGE_NT_HEADERS *nth;
dll_main_t dll_main;
assert(pe != NULL);
nth = pe_get_nt_header(pe);
dll_main = pe_offset(pe, nth->OptionalHeader.AddressOfEntryPoint);
return dll_main(pe, reason, ctx);
return pe_offset(pe, nth->OptionalHeader.AddressOfEntryPoint);
}
HRESULT pe_patch(void *dest, const void *src, size_t nbytes)
{
DWORD old_protect;
BOOL ok;
assert(dest != NULL);
assert(src != NULL);
ok = VirtualProtect(
dest,
nbytes,
PAGE_EXECUTE_READWRITE,
&old_protect);
if (!ok) {
return HRESULT_FROM_WIN32(GetLastError());
}
memcpy(dest, src, nbytes);
ok = VirtualProtect(
dest,
nbytes,
old_protect,
&old_protect);
if (!ok) {
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}

View File

@@ -1,9 +1,9 @@
#ifndef HOOK_PE_H
#define HOOK_PE_H
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef IMAGE_IMPORT_DESCRIPTOR pe_iid_t;
@@ -17,14 +17,11 @@ struct pe_iat_entry {
const pe_iid_t *pe_iid_get_first(HMODULE pe);
const char *pe_iid_get_name(HMODULE pe, const pe_iid_t *iid);
const pe_iid_t *pe_iid_get_next(HMODULE pe, const pe_iid_t *iid);
bool pe_iid_get_iat_entry(
HMODULE pe, const pe_iid_t *iid, size_t n, struct pe_iat_entry *entry);
HRESULT pe_iid_get_iat_entry(
HMODULE pe,
const pe_iid_t *iid,
size_t n,
struct pe_iat_entry *entry);
void *pe_get_export(HMODULE pe, const char *name, uint16_t ord);
BOOL pe_call_dll_main(HMODULE pe, uint32_t reason, void *ctx);
void pe_patch_pointer(void **ppointer, void *new_value);
HMODULE pe_explode(const uint8_t *bytes, uint32_t nbytes);
void pe_relocate(HMODULE pe);
#endif
void *pe_get_entry_point(HMODULE pe);
HRESULT pe_patch(void *dest, const void *src, size_t nbytes);

View File

@@ -1,14 +1,13 @@
#include <windows.h>
#include <winternl.h>
#include "hook/peb.h"
#include <assert.h>
#include "util/defs.h"
#include "util/str.h"
#include "hook/peb.h"
static const PEB *peb_get(void)
{
#ifdef __amd64
#ifdef _M_AMD64
return (const PEB *) __readgsqword(0x60);
#else
return (const PEB *) __readfsdword(0x30);
@@ -23,7 +22,7 @@ const peb_dll_t *peb_dll_get_first(void)
peb = peb_get();
node = peb->Ldr->InMemoryOrderModuleList.Flink;
return containerof(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
return CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
}
const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll)
@@ -31,6 +30,8 @@ const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll)
const PEB *peb;
const LIST_ENTRY *node;
assert(dll != NULL);
peb = peb_get();
node = dll->InMemoryOrderLinks.Flink;
@@ -38,30 +39,12 @@ const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll)
return NULL;
}
return containerof(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
return CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
}
HMODULE
peb_dll_get_base(const peb_dll_t *dll)
HMODULE peb_dll_get_base(const peb_dll_t *dll)
{
assert(dll != NULL);
return dll->DllBase;
}
char *peb_dll_dup_name(const peb_dll_t *dll)
{
const UNICODE_STRING *wstr;
char *name;
size_t i;
wstr = &dll->FullDllName;
for (i = wstr->Length / 2 - 1; i > 0; i--) {
if (wstr->Buffer[i] == L'\\') {
wstr_narrow(&wstr->Buffer[i + 1], &name);
return name;
}
}
return NULL;
}

View File

@@ -1,5 +1,4 @@
#ifndef HOOK_PEB_H
#define HOOK_PEB_H
#pragma once
#include <windows.h>
#include <winternl.h>
@@ -10,5 +9,3 @@ const peb_dll_t *peb_dll_get_first(void);
const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll);
HMODULE peb_dll_get_base(const peb_dll_t *dll);
char *peb_dll_dup_name(const peb_dll_t *dll);
#endif

196
src/main/hook/process.c Normal file
View File

@@ -0,0 +1,196 @@
#include <windows.h>
#include <tlhelp32.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "hook/hr.h"
#include "hook/pe.h"
#include "hook/process.h"
static bool thread_match_startup(
const CONTEXT *ctx,
void *ntstart,
void *exe_entry)
{
#ifdef _M_AMD64
return ctx->Rip == (DWORD64) ntstart &&
ctx->Rcx == (DWORD64) exe_entry;
#else
return ctx->Eip == (DWORD) ntstart &&
ctx->Eax == (DWORD) exe_entry;
#endif
}
static void thread_patch_startup(
process_entry_t new_entry,
process_entry_t *orig_entry,
CONTEXT *ctx)
{
#ifdef _M_AMD64
*orig_entry = (void *) ctx->Rcx;
ctx->Rcx = (DWORD64) new_entry;
#else
*orig_entry = (void *) ctx->Eax;
ctx->Eax = (DWORD) new_entry;
#endif
}
static HRESULT process_hijack_try_thread(
process_entry_t new_entry,
process_entry_t *orig_entry,
DWORD thread_id)
{
CONTEXT ctx;
HMODULE exe;
HMODULE ntdll;
void *exe_entry;
void *ntstart;
HANDLE thread;
HRESULT hr;
BOOL ok;
thread = NULL;
exe = GetModuleHandleW(NULL);
if (exe == NULL) {
/* uhhhh... */
hr = E_UNEXPECTED;
goto end;
}
ntdll = GetModuleHandleW(L"ntdll.dll");
if (ntdll == NULL) {
/* Another 2 + 2 = 5 situation */
hr = E_UNEXPECTED;
goto end;
}
exe_entry = pe_get_entry_point(exe);
ntstart = GetProcAddress(ntdll, "RtlUserThreadStart");
if (ntstart == NULL) {
/* TODO Deal with WinXP, for the poor souls still stuck on that OS.
XP starts threads at LdrInitializeThunk instead (I think) */
hr = E_NOTIMPL;
goto end;
}
thread = OpenThread(
THREAD_GET_CONTEXT | THREAD_SET_CONTEXT,
FALSE,
thread_id);
if (thread == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
memset(&ctx, 0, sizeof(ctx));
#ifdef _M_AMD64
ctx.ContextFlags = CONTEXT_AMD64 | CONTEXT_FULL;
#else
ctx.ContextFlags = CONTEXT_i386 | CONTEXT_FULL;
#endif
ok = GetThreadContext(thread, &ctx);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
if (thread_match_startup(&ctx, ntstart, exe_entry)) {
thread_patch_startup(new_entry, orig_entry, &ctx);
ok = SetThreadContext(thread, &ctx);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
return S_OK;
} else {
return S_FALSE;
}
end:
if (thread != NULL) {
CloseHandle(thread);
}
return hr;
}
HRESULT process_hijack_startup(
process_entry_t new_entry,
process_entry_t *orig_entry)
{
THREADENTRY32 thread;
HANDLE snap;
DWORD pid;
HRESULT fault;
HRESULT hr;
BOOL ok;
assert(new_entry != NULL);
assert(orig_entry != NULL);
pid = GetCurrentProcessId();
snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (snap == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
thread.dwSize = sizeof(thread);
ok = Thread32First(snap, &thread);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
/* Return this if we don't find anything suitable */
fault = E_FAIL;
do {
if (thread.th32OwnerProcessID != pid) {
continue;
}
hr = process_hijack_try_thread(
new_entry,
orig_entry,
thread.th32ThreadID);
if (hr == S_OK) {
/* Main thread successfully hijacked, finish up */
goto end;
} else if (FAILED(hr)) {
/* Latch this error code, but don't abort, keep trying. */
fault = hr;
}
} while (Thread32Next(snap, &thread));
hr = fault;
end:
if (snap != INVALID_HANDLE_VALUE) {
CloseHandle(snap);
}
return hr;
}

9
src/main/hook/process.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
typedef DWORD (CALLBACK *process_entry_t)(void);
HRESULT process_hijack_startup(
process_entry_t new_entry,
process_entry_t *orig_entry);

View File

@@ -1,5 +1,6 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@@ -8,32 +9,44 @@
#include "hook/peb.h"
#include "hook/table.h"
#include "util/mem.h"
static const char apiset_prefix[] = "api-ms-win-core-";
static const size_t apiset_prefix_len = sizeof(apiset_prefix) - 1;
static void hook_table_apply_to_all(
const char *depname, const struct hook_symbol *syms, size_t nsyms);
const char *depname,
const struct hook_symbol *syms,
size_t nsyms);
static void hook_table_apply_to_iid(
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms);
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms);
static bool hook_table_match_module(
HMODULE target,
const char *iid_name,
const char *depname);
static bool hook_table_match_proc(
const struct pe_iat_entry *iate, const struct hook_symbol *sym);
const struct pe_iat_entry *iate,
const struct hook_symbol *sym);
static void hook_table_apply_to_all(
const char *depname, const struct hook_symbol *syms, size_t nsyms)
const char *depname,
const struct hook_symbol *syms,
size_t nsyms)
{
const peb_dll_t *dll;
HMODULE pe;
for (dll = peb_dll_get_first(); dll != NULL; dll = peb_dll_get_next(dll)) {
for ( dll = peb_dll_get_first() ;
dll != NULL ;
dll = peb_dll_get_next(dll)) {
pe = peb_dll_get_base(dll);
if (pe == NULL) {
/* wtf? */
continue;
continue; /* ?? Happens sometimes. */
}
hook_table_apply(pe, depname, syms, nsyms);
@@ -41,25 +54,29 @@ static void hook_table_apply_to_all(
}
void hook_table_apply(
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms)
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms)
{
const pe_iid_t *iid;
const char *iid_name;
assert(depname != NULL);
assert(syms != NULL || nsyms == 0);
if (target == NULL) {
/* Call out, which will then call us back repeatedly. Awkward, but
viewed from the outside it's good for usability. */
hook_table_apply_to_all(depname, syms, nsyms);
} else {
for (iid = pe_iid_get_first(target); iid != NULL;
iid = pe_iid_get_next(target, iid)) {
for ( iid = pe_iid_get_first(target) ;
iid != NULL ;
iid = pe_iid_get_next(target, iid)) {
iid_name = pe_iid_get_name(target, iid);
if (_stricmp(iid_name, depname) == 0) {
if (hook_table_match_module(target, iid_name, depname)) {
hook_table_apply_to_iid(target, iid, syms, nsyms);
}
}
@@ -67,10 +84,10 @@ void hook_table_apply(
}
static void hook_table_apply_to_iid(
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms)
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms)
{
struct pe_iat_entry iate;
size_t i;
@@ -79,8 +96,8 @@ static void hook_table_apply_to_iid(
i = 0;
while (pe_iid_get_iat_entry(target, iid, i++, &iate)) {
for (j = 0; j < nsyms; j++) {
while (pe_iid_get_iat_entry(target, iid, i++, &iate) == S_OK) {
for (j = 0 ; j < nsyms ; j++) {
sym = &syms[j];
if (hook_table_match_proc(&iate, sym)) {
@@ -88,17 +105,70 @@ static void hook_table_apply_to_iid(
*sym->link = *iate.ppointer;
}
pe_patch_pointer(iate.ppointer, sym->patch);
pe_patch(iate.ppointer, &sym->patch, sizeof(sym->patch));
}
}
}
}
static bool hook_table_match_proc(
const struct pe_iat_entry *iate, const struct hook_symbol *sym)
static bool hook_table_match_module(
HMODULE target,
const char *iid_name,
const char *depname)
{
if (sym->name != NULL && iate->name != NULL &&
strcmp(sym->name, iate->name) == 0) {
HMODULE kernel32;
int result;
/* OK, first do a straightforward match on the imported DLL name versus
the hook table DLL name. If it succeeds then we're done. */
result = _stricmp(iid_name, depname);
if (result == 0) {
return true;
}
/* If it failed then we have to check if this hook table targets kernel32.
We have to do some special processing around API sets in that case, so
stop here if kernel32 is not the subject of this hook table. */
if (_stricmp(depname, "kernel32.dll") != 0) {
return false;
}
/* There isn't really any good test for whether a DLL import wants a
concrete DLL or an abstract DLL providing a particular Windows API-set,
so we use a hacky check against the prefix. If the imported DLL name
looks like an apiset then we'll allow kernel32 hook tables to apply. */
result = _strnicmp(iid_name, apiset_prefix, apiset_prefix_len);
if (result != 0) {
return false;
}
/* ... EXCEPT for the case where we're hooking all DLLs loaded into the
process and we are currently examining kernel32 itself. In that case
there's some weird reference loops issues I don't entirely understand
right now. To avoid those, we just don't apply kernel32 hook tables to
kernel32 itself. */
kernel32 = GetModuleHandleW(L"kernel32.dll");
if (target == kernel32) {
return false;
}
return true;
}
static bool hook_table_match_proc(
const struct pe_iat_entry *iate,
const struct hook_symbol *sym)
{
if ( sym->name != NULL &&
iate->name != NULL &&
strcmp(sym->name, iate->name) == 0) {
return true;
}

View File

@@ -1,5 +1,4 @@
#ifndef HOOK_TABLE_H
#define HOOK_TABLE_H
#pragma once
#include <windows.h>
@@ -14,9 +13,13 @@ struct hook_symbol {
};
void hook_table_apply(
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms);
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms);
#endif
void hook_table_revert(
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms);

View File

@@ -7,4 +7,5 @@ src_hooklib := \
config-adapter.c \
rs232.c \
setupapi.c \
memfile.c \

268
src/main/hooklib/memfile.c Normal file
View File

@@ -0,0 +1,268 @@
#include "hooklib/memfile.h"
#include <stdbool.h>
#include <windows.h>
#include "hook/hr.h"
#include "hook/iohook.h"
#include "hook/table.h"
#include "util/array.h"
#include "util/log.h"
#include "util/str.h"
struct file_entry {
char *path;
wchar_t *wpath;
HANDLE fd;
int64_t pos;
const void *data;
uint32_t sz;
enum memfile_hook_path_mode path_mode;
};
static struct array hooked_files;
static CRITICAL_SECTION hooked_files_cs;
BOOL my_GetFileInformationByHandle(
HANDLE hFile,
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
BOOL (*real_GetFileInformationByHandle)(
HANDLE hFile,
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
static const struct hook_symbol memfile_hook_kernel32_syms[] = {
{.name = "GetFileInformationByHandle",
.patch = my_GetFileInformationByHandle,
.link = (void **) &real_GetFileInformationByHandle},
};
void memfile_hook_init(void)
{
array_init(&hooked_files);
InitializeCriticalSection(&hooked_files_cs);
hook_table_apply(
NULL,
"Kernel32.dll",
memfile_hook_kernel32_syms,
lengthof(memfile_hook_kernel32_syms));
log_info("Initialized");
}
void memfile_hook_fini(void)
{
EnterCriticalSection(&hooked_files_cs);
struct file_entry *entry;
for (size_t i = 0; i < hooked_files.nitems; i++) {
entry = array_item(struct file_entry, &hooked_files, i);
free(entry->wpath);
free(entry->path);
if (entry->fd != NULL) {
CloseHandle(entry->fd);
}
}
array_fini(&hooked_files);
LeaveCriticalSection(&hooked_files_cs);
DeleteCriticalSection(&hooked_files_cs);
log_info("Finished");
}
void memfile_hook_add_fd(
const char *path, enum memfile_hook_path_mode path_mode, const void *data, uint32_t sz)
{
log_assert(path != NULL);
HRESULT hr;
struct file_entry *entry = NULL;
EnterCriticalSection(&hooked_files_cs);
entry = array_append(struct file_entry, &hooked_files);
entry->path = strdup(path);
entry->wpath = str_widen(path);
hr = iohook_open_nul_fd(&entry->fd);
if (hr != S_OK) {
LeaveCriticalSection(&hooked_files_cs);
log_fatal("Opening nul fd failed: %08lx", hr);
}
entry->pos = 0;
entry->data = data;
entry->sz = sz;
entry->path_mode = path_mode;
LeaveCriticalSection(&hooked_files_cs);
log_misc("memfile_hook_add_fd: path %s, mode %d, data size %d", path, path_mode, sz);
}
static struct file_entry *memfile_hook_match_irp(const struct irp *irp)
{
struct file_entry *entry;
log_assert(irp != NULL);
if (irp->op == IRP_OP_OPEN) {
for (size_t i = 0; i < hooked_files.nitems; i++) {
entry = array_item(struct file_entry, &hooked_files, i);
if (entry->path_mode == ABSOLUTE_MATCH) {
if (wstr_eq(entry->wpath, irp->open_filename)) {
log_misc("Memfile Open: Absolute matched: %s", entry->path);
return entry;
}
} else if (entry->path_mode == ENDING_MATCH) {
if (wstr_ends_with(irp->open_filename, entry->wpath)) {
log_misc("Memfile Open: Ending matched: %s", entry->path);
return entry;
}
}
}
} else {
for (size_t i = 0; i < hooked_files.nitems; i++) {
entry = array_item(struct file_entry, &hooked_files, i);
if (entry->fd == irp->fd) {
return entry;
}
}
}
return NULL;
}
static HRESULT memfile_hook_irp_read(struct file_entry *entry, struct irp *irp)
{
log_assert(entry != NULL);
log_assert(irp != NULL);
size_t nread = min(entry->sz - entry->pos, irp->read.nbytes);
if (nread > 0) {
memcpy(irp->read.bytes + irp->read.pos, (uint8_t*)entry->data + entry->pos, nread);
entry->pos += nread;
irp->read.pos += nread;
}
return S_OK;
}
static HRESULT memfile_hook_irp_seek(struct file_entry *entry, struct irp *irp)
{
log_assert(entry != NULL);
log_assert(irp != NULL);
switch (irp->seek_origin) {
case FILE_BEGIN:
entry->pos = irp->seek_offset;
break;
case FILE_CURRENT:
entry->pos += irp->seek_offset;
break;
case FILE_END:
entry->pos = entry->sz + irp->seek_offset;
break;
default:
log_fatal("Invalid seek origin");
}
irp->seek_pos = entry->pos;
return S_OK;
}
static HRESULT
memfile_hook_irp_handler(struct file_entry *entry, struct irp *irp)
{
log_assert(entry != NULL);
log_assert(irp != NULL);
switch (irp->op) {
case IRP_OP_OPEN:
irp->fd = entry->fd;
entry->pos = 0;
return S_OK;
case IRP_OP_CLOSE:
return S_OK;
case IRP_OP_READ:
return memfile_hook_irp_read(entry, irp);
case IRP_OP_WRITE:
log_warning("write attempted on memfile, unsupported");
return S_OK;
case IRP_OP_IOCTL:
log_warning("ioctl attempted on memfile, unsupported");
return S_OK;
case IRP_OP_FSYNC:
log_warning("fsync attempted on memfile, unsupported");
return S_OK;
case IRP_OP_SEEK:
return memfile_hook_irp_seek(entry, irp);
default:
return E_NOTIMPL;
}
}
HRESULT memfile_hook_dispatch_irp(struct irp *irp)
{
log_assert(irp != NULL);
HRESULT hr;
EnterCriticalSection(&hooked_files_cs);
struct file_entry *entry = memfile_hook_match_irp(irp);
if (!entry) {
LeaveCriticalSection(&hooked_files_cs);
return iohook_invoke_next(irp);
}
hr = memfile_hook_irp_handler(entry, irp);
LeaveCriticalSection(&hooked_files_cs);
return hr;
}
// this isn't a standard iohook IRP atm.
BOOL my_GetFileInformationByHandle(
HANDLE hFile,
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
)
{
struct file_entry *entry;
for (size_t i = 0; i < hooked_files.nitems; i++) {
entry = array_item(struct file_entry, &hooked_files, i);
if (entry->fd == hFile) {
lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_READONLY;
lpFileInformation->nFileSizeHigh = 0;
lpFileInformation->nFileSizeLow = entry->sz;
return true;
}
}
return real_GetFileInformationByHandle(hFile, lpFileInformation);
}

View File

@@ -0,0 +1,42 @@
#ifndef HOOKLIB_MEMFILE_H
#define HOOKLIB_MEMFILE_H
#include <stdint.h>
#include <windows.h>
#include "hook/iohook.h"
/**
* memfile allows you to stub read-only files in memory.
*/
/**
* This hooks required functions and sets up the hook array.
* Please remember to install the irp below.
*/
void memfile_hook_init(void);
void memfile_hook_fini(void);
enum memfile_hook_path_mode {
ABSOLUTE_MATCH = 0x1,
ENDING_MATCH = 0x2,
};
/**
* Adds the specified path to the list of files to hook
*
* @param path path to hook
* @param path_mode path matching mode (1: abs, 2: ending)
* @param data file contents (buffer must exist until fini is called)
* @param sz size of file
*/
void memfile_hook_add_fd(
const char *path, enum memfile_hook_path_mode path_mode, const void *data, uint32_t sz);
/**
* iohook dispatch function. Needs to be installed.
*/
HRESULT memfile_hook_dispatch_irp(struct irp *irp);
#endif

View File

@@ -14,11 +14,11 @@
#include <stdbool.h>
#include <string.h>
#include "hook/hr.h"
#include "hook/iohook.h"
#include "hook/table.h"
#include "util/array.h"
#include "util/hr.h"
#include "util/log.h"
/* RS232 API hooks */
@@ -178,7 +178,7 @@ my_ClearCommError(HANDLE fd, uint32_t *errors, COMSTAT *status)
irp.read.bytes = (uint8_t *) &llstatus;
irp.read.nbytes = sizeof(llstatus);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning(
@@ -303,7 +303,7 @@ static BOOL STDCALL my_EscapeCommFunction(HANDLE fd, uint32_t cmd)
irp.fd = fd;
irp.ioctl = ioctl;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: ioctl failed: %lx", __func__, hr);
@@ -364,7 +364,7 @@ static BOOL STDCALL my_GetCommState(HANDLE fd, DCB *dcb)
irp.read.nbytes = sizeof(baud);
memset(&baud, 0, sizeof(baud));
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_GET_BAUD_RATE failed: %lx", __func__, hr);
@@ -380,7 +380,7 @@ static BOOL STDCALL my_GetCommState(HANDLE fd, DCB *dcb)
irp.read.nbytes = sizeof(handflow);
memset(&handflow, 0, sizeof(handflow));
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_GET_HANDFLOW failed: %lx", __func__, hr);
@@ -396,7 +396,7 @@ static BOOL STDCALL my_GetCommState(HANDLE fd, DCB *dcb)
irp.read.nbytes = sizeof(line_control);
memset(&line_control, 0, sizeof(line_control));
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning(
@@ -413,7 +413,7 @@ static BOOL STDCALL my_GetCommState(HANDLE fd, DCB *dcb)
irp.read.nbytes = sizeof(chars);
memset(&chars, 0, sizeof(chars));
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_GET_CHARS failed: %lx", __func__, hr);
@@ -508,7 +508,7 @@ static BOOL STDCALL my_PurgeComm(HANDLE fd, uint32_t flags)
irp.write.bytes = (uint8_t *) &flags;
irp.write.nbytes = sizeof(flags);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_PURGE failed: %lx", __func__, hr);
@@ -533,7 +533,7 @@ static BOOL STDCALL my_SetCommMask(HANDLE fd, uint32_t mask)
irp.write.bytes = (uint8_t *) &mask;
irp.write.nbytes = sizeof(mask);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_SET_WAIT_MASK failed: %lx", __func__, hr);
@@ -666,7 +666,7 @@ static BOOL STDCALL my_SetCommState(HANDLE fd, const DCB *dcb)
irp.write.bytes = (uint8_t *) &baud;
irp.write.nbytes = sizeof(baud);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_SET_BAUD_RATE failed: %lx", __func__, hr);
@@ -681,7 +681,7 @@ static BOOL STDCALL my_SetCommState(HANDLE fd, const DCB *dcb)
irp.write.bytes = (uint8_t *) &handflow;
irp.write.nbytes = sizeof(handflow);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_SET_HANDFLOW failed: %lx", __func__, hr);
@@ -696,7 +696,7 @@ static BOOL STDCALL my_SetCommState(HANDLE fd, const DCB *dcb)
irp.write.bytes = (uint8_t *) &line_control;
irp.write.nbytes = sizeof(line_control);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning(
@@ -712,7 +712,7 @@ static BOOL STDCALL my_SetCommState(HANDLE fd, const DCB *dcb)
irp.write.bytes = (uint8_t *) &chars;
irp.write.nbytes = sizeof(chars);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_SET_CHARS failed: %lx", __func__, hr);
@@ -748,7 +748,7 @@ static BOOL STDCALL my_SetCommTimeouts(HANDLE fd, COMMTIMEOUTS *src)
irp.write.bytes = (uint8_t *) &dest;
irp.write.nbytes = sizeof(dest);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_SET_TIMEOUTS failed: %lx", __func__, hr);
@@ -777,7 +777,7 @@ static BOOL STDCALL my_SetupComm(HANDLE fd, uint32_t in_q, uint32_t out_q)
irp.write.bytes = (uint8_t *) &qs;
irp.write.nbytes = sizeof(qs);
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning(
@@ -801,7 +801,7 @@ static BOOL STDCALL my_SetCommBreak(HANDLE fd)
irp.fd = fd;
irp.ioctl = IOCTL_SERIAL_SET_BREAK_ON;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_SET_BREAK_ON failed: %lx", __func__, hr);
@@ -824,7 +824,7 @@ static BOOL STDCALL my_ClearCommBreak(HANDLE fd)
irp.fd = fd;
irp.ioctl = IOCTL_SERIAL_SET_BREAK_OFF;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_warning("%s: IOCTL_SERIAL_SET_BREAK_OFF failed: %lx", __func__, hr);

View File

@@ -30,6 +30,16 @@
static struct ac_io_emu iidxhook_util_acio_emu;
static struct ac_io_emu_icca iidxhook_util_acio_emu_icca[2];
static bool iidxhook_util_icca_override_version;
static enum ac_io_emu_icca_version iidxhook_util_icca_override_version_value;
void iidxhook_util_acio_override_version(enum ac_io_emu_icca_version version)
{
iidxhook_util_icca_override_version = true;
iidxhook_util_icca_override_version_value = version;
}
void iidxhook_util_acio_init(bool legacy_mode)
{
uint8_t i;
@@ -43,6 +53,12 @@ void iidxhook_util_acio_init(bool legacy_mode)
for (i = 0; i < lengthof(iidxhook_util_acio_emu_icca); i++) {
ac_io_emu_icca_init(
&iidxhook_util_acio_emu_icca[i], &iidxhook_util_acio_emu, i);
if (iidxhook_util_icca_override_version) {
ac_io_emu_icca_set_version(
&iidxhook_util_acio_emu_icca[i],
iidxhook_util_icca_override_version_value
);
}
}
rs232_hook_add_fd(iidxhook_util_acio_emu.fd);
@@ -62,7 +78,7 @@ iidxhook_util_acio_dispatch_irp(struct irp *irp)
log_assert(irp != NULL);
if (!ac_io_emu_match_irp(&iidxhook_util_acio_emu, irp)) {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
for (;;) {

View File

@@ -5,6 +5,7 @@
#include <stdbool.h>
#include "acioemu/icca.h"
#include "hook/iohook.h"
/**
@@ -19,6 +20,12 @@
*/
void iidxhook_util_acio_init(bool legacy_mode);
/**
* Use the specified ICCA emulation version
*/
void iidxhook_util_acio_override_version(enum ac_io_emu_icca_version version);
/**
* Cleanup the ACIO emulation layer.
*/

View File

@@ -81,7 +81,7 @@ static bool chart_patch_file_load(
irp.open_access = GENERIC_READ;
irp.open_creation = OPEN_EXISTING;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
return false;
@@ -95,7 +95,7 @@ static bool chart_patch_file_load(
irp.seek_origin = FILE_END;
irp.seek_offset = 0;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_fatal("Seeking end failed");
@@ -110,7 +110,7 @@ static bool chart_patch_file_load(
irp.seek_origin = FILE_BEGIN;
irp.seek_offset = 0;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_fatal("Seeking begin failed");
@@ -123,7 +123,7 @@ static bool chart_patch_file_load(
irp.read.nbytes = nbytes;
irp.read.pos = 0;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_fatal("Reading failed");
@@ -139,13 +139,18 @@ static bool chart_patch_file_load(
irp.op = IRP_OP_CLOSE;
irp.fd = fd;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (FAILED(hr)) {
log_fatal("Closing failed");
}
file->fd = iohook_open_dummy_fd();
hr = iohook_open_nul_fd(&file->fd);
if (hr != S_OK) {
log_fatal("Opening nul fd failed: %08lx", hr);
}
memset(&file->state, 0, sizeof(struct const_iobuf));
file->state.bytes = bytes;
file->state.nbytes = nbytes;
@@ -383,7 +388,7 @@ chart_patch_file_close_irp(struct chart_patch_file *file, struct irp *irp_close)
memcpy(&irp, irp_close, sizeof(struct irp));
irp.fd = chart_patch_file_1.fd;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (hr != S_OK) {
log_warning("Closing dummy .1 file handle failed");
@@ -397,7 +402,7 @@ chart_patch_file_close_irp(struct chart_patch_file *file, struct irp *irp_close)
memcpy(&irp, irp_close, sizeof(struct irp));
irp.fd = chart_patch_file_checksum.fd;
hr = irp_invoke_next(&irp);
hr = iohook_invoke_next(&irp);
if (hr != S_OK) {
log_warning("Closing dummy checksum file handle failed");
@@ -525,7 +530,7 @@ iidxhook_util_chart_patch_dispatch_irp(struct irp *irp)
if (chart_patch_file_trap(irp)) {
hr = S_OK;
} else {
hr = irp_invoke_next(irp);
hr = iohook_invoke_next(irp);
}
} else {
if (irp->fd != INVALID_HANDLE_VALUE && irp->fd != NULL) {
@@ -536,7 +541,7 @@ iidxhook_util_chart_patch_dispatch_irp(struct irp *irp)
hr = chart_patch_file_dispatch_irp(
&chart_patch_file_checksum, irp);
} else {
hr = irp_invoke_next(irp);
hr = iohook_invoke_next(irp);
}
}
}
@@ -545,6 +550,6 @@ iidxhook_util_chart_patch_dispatch_irp(struct irp *irp)
return hr;
} else {
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
}

View File

@@ -97,8 +97,8 @@ settings_hook_dispatch_irp(struct irp *irp)
}
}
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}
return irp_invoke_next(irp);
return iohook_invoke_next(irp);
}

View File

@@ -49,12 +49,6 @@
#define IIDXHOOK1_CMD_USAGE \
"Usage: inject.exe iidxhook1.dll <bm2dx.exe> [options...]"
static const irp_handler_t iidxhook_handlers[] = {
ezusb_emu_device_dispatch_irp,
iidxhook_util_chart_patch_dispatch_irp,
settings_hook_dispatch_irp,
};
static const hook_d3d9_irp_handler_t iidxhook_d3d9_handlers[] = {
iidxhook_util_d3d9_irp_handler,
};
@@ -229,7 +223,9 @@ my_OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)
/* Set up IO emulation hooks _after_ IO API setup to allow
API implementations with real IO devices */
iohook_init(iidxhook_handlers, lengthof(iidxhook_handlers));
iohook_push_handler(ezusb_emu_device_dispatch_irp);
iohook_push_handler(iidxhook_util_chart_patch_dispatch_irp);
iohook_push_handler(settings_hook_dispatch_irp);
hook_setupapi_init(&ezusb_emu_desc_device.setupapi);
ezusb_emu_device_hook_init(ezusb_iidx_emu_msg_init());
@@ -248,12 +244,7 @@ skip:
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
{
if (reason == DLL_PROCESS_ATTACH) {
#ifdef DEBUG_HOOKING
FILE *file = fopen("iidxhook.dllmain.log", "w+");
log_to_writer(log_writer_file, file);
#else
log_to_writer(log_writer_null, NULL);
#endif
log_to_writer(log_writer_debug, NULL);
/* Bootstrap hook for further init tasks (see above) */
@@ -266,14 +257,6 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
adapter_hook_init();
eamuse_hook_init();
settings_hook_init();
#ifdef DEBUG_HOOKING
fflush(file);
fclose(file);
#endif
/* Logging to file and other destinations is handled by inject */
log_to_writer(log_writer_debug, NULL);
}
return TRUE;

View File

@@ -49,13 +49,6 @@
#define IIDXHOOK2_CMD_USAGE \
"Usage: inject.exe iidxhook2.dll <bm2dx.exe> [options...]"
static const irp_handler_t iidxhook_handlers[] = {
ezusb_emu_device_dispatch_irp,
iidxhook_util_acio_dispatch_irp,
iidxhook_util_chart_patch_dispatch_irp,
settings_hook_dispatch_irp,
};
static const hook_d3d9_irp_handler_t iidxhook_d3d9_handlers[] = {
iidxhook_util_d3d9_irp_handler,
};
@@ -223,7 +216,10 @@ my_OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)
/* Set up IO emulation hooks _after_ IO API setup to allow
API implementations with real IO devices */
iohook_init(iidxhook_handlers, lengthof(iidxhook_handlers));
iohook_push_handler(ezusb_emu_device_dispatch_irp);
iohook_push_handler(iidxhook_util_acio_dispatch_irp);
iohook_push_handler(iidxhook_util_chart_patch_dispatch_irp);
iohook_push_handler(settings_hook_dispatch_irp);
hook_setupapi_init(&ezusb_emu_desc_device.setupapi);
ezusb_emu_device_hook_init(ezusb_iidx_emu_msg_init());
@@ -247,12 +243,7 @@ skip:
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
{
if (reason == DLL_PROCESS_ATTACH) {
#ifdef DEBUG_HOOKING
FILE *file = fopen("iidxhook.dllmain.log", "w+");
log_to_writer(log_writer_file, file);
#else
log_to_writer(log_writer_null, NULL);
#endif
log_to_writer(log_writer_debug, NULL);
/* Bootstrap hook for further init tasks (see above) */
@@ -265,14 +256,6 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
adapter_hook_init();
eamuse_hook_init();
settings_hook_init();
#ifdef DEBUG_HOOKING
fflush(file);
fclose(file);
#endif
/* Logging to file and other destinations is handled by inject */
log_to_writer(log_writer_debug, NULL);
}
return TRUE;

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