mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-09-15 11:55:10 -05:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6766d4a3b | ||
|
|
843d78bc4b | ||
|
|
0792b29f3d | ||
|
|
e1d4810743 | ||
|
|
2110abc269 | ||
|
|
fc22f76988 | ||
|
|
3d4df4cd64 | ||
|
|
be5a773cb7 | ||
|
|
3ed6ac6290 | ||
|
|
e49149b03d | ||
|
|
af507e6dcb | ||
|
|
a9ebc30ea7 | ||
|
|
4f3135439d | ||
|
|
2d1c09d4e6 | ||
|
|
164d56b6eb | ||
|
|
1712f10f66 | ||
|
|
128496901e | ||
|
|
65a45740d2 | ||
|
|
8bd0f44e24 | ||
|
|
7733912ae2 | ||
|
|
55c9f5a521 | ||
|
|
917dc88826 | ||
|
|
475d438ec7 | ||
|
|
f23d3531a2 | ||
|
|
32c3428c59 | ||
|
|
ee034a1f08 | ||
|
|
54b321c7d9 | ||
|
|
de894ae5ce | ||
|
|
1d25abc10b | ||
|
|
beea9e7bb7 | ||
|
|
fbb69d4550 | ||
|
|
e0fe9bcf94 | ||
|
|
f9c0733af3 | ||
|
|
a016f27652 | ||
|
|
3a1005f1f8 | ||
|
|
f1700ca551 | ||
|
|
60b081b01c | ||
|
|
89989ae49c | ||
|
|
ff1b9ff9a2 | ||
|
|
4c0f60fc93 | ||
|
|
caba39843f | ||
|
|
f028bdeb20 | ||
|
|
bfbb7103ef | ||
|
|
95e073271f | ||
|
|
cdefc2b09e | ||
|
|
9246e7f9e0 | ||
|
|
bbb5157460 | ||
|
|
e0cfc8c948 | ||
|
|
69ef963451 | ||
|
|
93cfb591b7 | ||
|
|
3396c09fbf | ||
|
|
bd8dc18b30 | ||
|
|
86517bd001 | ||
|
|
172c83a854 | ||
|
|
c7137659bc | ||
|
|
8e9abf9906 | ||
|
|
62bb717582 | ||
|
|
4816fb809d |
@@ -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
|
||||
|
||||
19
GNUmakefile
19
GNUmakefile
@@ -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
|
||||
|
||||
89
Module.mk
89
Module.mk
@@ -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 \
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Bemanitools 5
|
||||
[](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
16
dist/iidx/gamestart-27.bat
vendored
Normal 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
63
dist/iidx/iidxhook-27.conf
vendored
Normal 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)
|
||||
|
||||
3
dist/sdvx5/sdvxhook2.conf
vendored
3
dist/sdvx5/sdvxhook2.conf
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
179
doc/iidxhook/iidxhook9.md
Normal 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.
|
||||
@@ -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).
|
||||
18
doc/sdvxhook/sdvxio-bio2.md
Normal file
18
doc/sdvxhook/sdvxio-bio2.md
Normal 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)
|
||||
24
doc/sdvxhook/vigem-sdvxio.md
Normal file
24
doc/sdvxhook/vigem-sdvxio.md
Normal 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
3
doc/tools/jbiotest.md
Normal 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
519
src/imports/ViGEm/Client.h
Normal 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
191
src/imports/ViGEm/Common.h
Normal 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
68
src/imports/ViGEm/Util.h
Normal 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;
|
||||
}
|
||||
399
src/imports/ViGEm/km/BusShared.h
Normal file
399
src/imports/ViGEm/km/BusShared.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
27
src/imports/import_32_indep_ViGEmClient.def
Normal file
27
src/imports/import_32_indep_ViGEmClient.def
Normal 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
|
||||
@@ -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
|
||||
|
||||
27
src/imports/import_64_indep_ViGEmClient.def
Normal file
27
src/imports/import_64_indep_ViGEmClient.def
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
exes += aciotest
|
||||
|
||||
libs_aciotest := \
|
||||
bio2drv \
|
||||
aciodrv \
|
||||
util \
|
||||
|
||||
src_aciotest := \
|
||||
icca.c \
|
||||
kfca.c \
|
||||
bi2a-sdvx.c \
|
||||
main.c \
|
||||
|
||||
58
src/main/aciotest/bi2a-sdvx.c
Normal file
58
src/main/aciotest/bi2a-sdvx.c
Normal 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;
|
||||
}
|
||||
10
src/main/aciotest/bi2a-sdvx.h
Normal file
10
src/main/aciotest/bi2a-sdvx.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
struct asiohook_config_asio {
|
||||
bool force_asio;
|
||||
bool force_wasapi;
|
||||
char replacement_name[128];
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
75
src/main/bio2/bi2a-sdvx.h
Normal 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
15
src/main/bio2/bio2.h
Normal 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
|
||||
9
src/main/bio2drv/Module.mk
Normal file
9
src/main/bio2drv/Module.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
libs += bio2drv
|
||||
|
||||
libs_bio2drv := \
|
||||
aciodrv
|
||||
|
||||
src_bio2drv := \
|
||||
detect.c \
|
||||
config-bio2.c \
|
||||
bi2a-sdvx.c \
|
||||
138
src/main/bio2drv/bi2a-sdvx.c
Normal file
138
src/main/bio2drv/bi2a-sdvx.c
Normal 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;
|
||||
}
|
||||
51
src/main/bio2drv/bi2a-sdvx.h
Normal file
51
src/main/bio2drv/bi2a-sdvx.h
Normal 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
|
||||
75
src/main/bio2drv/config-bio2.c
Normal file
75
src/main/bio2drv/config-bio2.c
Normal 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);
|
||||
}
|
||||
}
|
||||
19
src/main/bio2drv/config-bio2.h
Normal file
19
src/main/bio2drv/config-bio2.h
Normal 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
163
src/main/bio2drv/detect.c
Normal 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
23
src/main/bio2drv/detect.h
Normal 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
|
||||
7
src/main/bio2emu-iidx/Module.mk
Normal file
7
src/main/bio2emu-iidx/Module.mk
Normal file
@@ -0,0 +1,7 @@
|
||||
libs += bio2emu-iidx
|
||||
|
||||
libs_bio2emu-iidx := \
|
||||
bio2emu \
|
||||
|
||||
src_bio2emu-iidx := \
|
||||
bi2a.c \
|
||||
@@ -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 */
|
||||
|
||||
12
src/main/bio2emu-iidx/bi2a.h
Normal file
12
src/main/bio2emu-iidx/bi2a.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 (;;) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 (;;) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 (;;) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
137
src/main/hook/args.c
Normal 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
6
src/main/hook/args.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
HRESULT args_recover(int *argc, char ***argv);
|
||||
void args_free(int argc, char **argv);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
33
src/main/hook/hr.c
Normal 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));
|
||||
}
|
||||
@@ -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
343
src/main/hook/iobuf.c
Normal 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
40
src/main/hook/iobuf.h
Normal 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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
196
src/main/hook/process.c
Normal 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
9
src/main/hook/process.h
Normal 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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -7,4 +7,5 @@ src_hooklib := \
|
||||
config-adapter.c \
|
||||
rs232.c \
|
||||
setupapi.c \
|
||||
memfile.c \
|
||||
|
||||
|
||||
268
src/main/hooklib/memfile.c
Normal file
268
src/main/hooklib/memfile.c
Normal 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);
|
||||
}
|
||||
42
src/main/hooklib/memfile.h
Normal file
42
src/main/hooklib/memfile.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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 (;;) {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user