forked from Hay1tsme/segatools
Compare commits
157 Commits
2024-02-22
...
2025-07-27
Author | SHA1 | Date | |
---|---|---|---|
16dc43cfd8
|
|||
bb0b023ec0
|
|||
d83e7d3c3a
|
|||
ec00a83d6b
|
|||
bb01e131e9
|
|||
ded1375e88 | |||
0006731536
|
|||
ae168cdaf9
|
|||
e974a76fe6
|
|||
e2e4b37e3f | |||
f595af9686
|
|||
03513e7b0c
|
|||
cdb6815c5a
|
|||
d62b64ca5a
|
|||
4644e36ccc
|
|||
dbfc62b5d4
|
|||
24e8bc87a3 | |||
a65b43fe1a | |||
66a53dd2de
|
|||
ae3dd666f4
|
|||
a6126bf290
|
|||
015097972a
|
|||
67eda7458b | |||
b37e1105d0 | |||
9a6c4939c2 | |||
39711a994a | |||
61f95c3f2e | |||
70c3e2fe0f | |||
369fe28687 | |||
3371f3f437 | |||
a57542c2d2
|
|||
27116a7a41
|
|||
e850346b79
|
|||
4d0ef54279
|
|||
b8af67377c
|
|||
4cb76dd1ee
|
|||
efe01d92a6 | |||
004a2f6dcd | |||
a1611afffc | |||
1d63ab24d3 | |||
2f54183636 | |||
402bf0f247 | |||
4c20deb60a | |||
96ee1afc2f | |||
0c28765bdd | |||
96bf8cab81
|
|||
a3120181be
|
|||
80d4902cfc
|
|||
b4f5cdbe59 | |||
25431a9db1 | |||
a705ae8748 | |||
b52455339f | |||
ff21223f06 | |||
047733d122 | |||
21bb965382 | |||
11556a1332 | |||
d8202e1df4 | |||
2d3d6fc2bb | |||
6d8ffb46ef | |||
2069b1ea85 | |||
c80f903cf8 | |||
ceb2b63e8b | |||
83840e0a87 | |||
e50d6d8ebc | |||
e1a47cf365 | |||
8aef1cfa79 | |||
8fc24503c8
|
|||
ebf0f0b428 | |||
892eb2b859
|
|||
b80b9fbc19 | |||
cef3406691 | |||
97d2d6b9bc | |||
f39b9ce3a0 | |||
243bb778d1 | |||
66317a0054
|
|||
8c24e04900 | |||
3bb9404a38 | |||
6819963f06 | |||
36849bd09a
|
|||
5f817c8a36
|
|||
259b763a13
|
|||
2251585ef0
|
|||
c06bb408e7
|
|||
53fb8c28ea | |||
33452394e6 | |||
4fa9abffe8 | |||
88a5bdcd14 | |||
bb773a63ce | |||
25e79f87c2 | |||
79592514ba | |||
cdfd3bf655 | |||
f6c12fd230 | |||
86556ed2c8 | |||
9de48dd6ce | |||
d257887f6e | |||
3eef5dd209 | |||
599d5e3211 | |||
f18d074c5f | |||
6bd1bce419 | |||
96bdacfa7c | |||
d4bb7b6e0e | |||
70ac873d11 | |||
068651b6fa | |||
84e9ed3c9a | |||
c827b4c212 | |||
26624f25b1 | |||
824bc9abda | |||
cc5b87b559 | |||
e6794807a6 | |||
54cbbffae9 | |||
ac0f9f0587
|
|||
0061158188
|
|||
c535f18e40
|
|||
c91c7db3c7
|
|||
6a4cae1165 | |||
383039e16e | |||
37c26ecadb | |||
686d57d3ee
|
|||
b9204d4765
|
|||
5abc593b46
|
|||
fe14630b3d
|
|||
92fe2751e7 | |||
8c839b0d4e | |||
ccb655a12b | |||
ded89f6343 | |||
f3e31fc2ae
|
|||
965126c68a
|
|||
050951e56f
|
|||
7e5e0f132e
|
|||
4e58d3b9a2
|
|||
7d3cab256b | |||
b0f307f427
|
|||
7aa996193c | |||
9353c9872f | |||
d8b3d41809
|
|||
3bfb046afc
|
|||
9fe98b227b
|
|||
b77ce7b457
|
|||
517469a60c
|
|||
1069cfee26 | |||
d3a0faa530 | |||
00b3d5b7bb
|
|||
04fcd0d09a
|
|||
25e954fb55 | |||
a8c6ac70e4 | |||
eb1ec0e261
|
|||
482a6e530a | |||
65173e1fa6 | |||
4041844ea9 | |||
47a65e5e51
|
|||
774a639bb7
|
|||
097b74d849
|
|||
2590e749ca
|
|||
9c66488906
|
|||
f570869946
|
|||
629ded4018 | |||
ca36a879cb
|
4
.clang-format
Normal file
4
.clang-format
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 4
|
||||
---
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -18,3 +18,9 @@ build/
|
||||
|
||||
# External dependencies
|
||||
subprojects/capnhook
|
||||
|
||||
# For enabling debug logging on local builds
|
||||
MesonLocalOptions.mk
|
||||
|
||||
# Some meson cache thing
|
||||
.meson-subproject-wrap-hash.txt
|
||||
|
11
Makefile
11
Makefile
@ -5,12 +5,19 @@ V ?= @
|
||||
BUILD_DIR := build
|
||||
BUILD_DIR_32 := $(BUILD_DIR)/build32
|
||||
BUILD_DIR_64 := $(BUILD_DIR)/build64
|
||||
BUILD_DIR_GAMES_32 := $(BUILD_DIR_32)/games
|
||||
BUILD_DIR_GAMES_64 := $(BUILD_DIR_64)/games
|
||||
BUILD_DIR_ZIP := $(BUILD_DIR)/zip
|
||||
|
||||
DOC_DIR := doc
|
||||
|
||||
DIST_DIR := dist
|
||||
|
||||
# Add "-D[option]=[value]" here as necessary
|
||||
MESON_OPTIONS :=
|
||||
# For options that shouldn't be committed
|
||||
-include MesonLocalOptions.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Targets
|
||||
# -----------------------------------------------------------------------------
|
||||
@ -19,9 +26,9 @@ include Package.mk
|
||||
|
||||
.PHONY: build # Build the project
|
||||
build:
|
||||
$(V)meson --cross cross-mingw-32.txt $(BUILD_DIR_32)
|
||||
$(V)meson setup $(MESON_OPTIONS) --cross cross-mingw-32.txt $(BUILD_DIR_32)
|
||||
$(V)ninja -C $(BUILD_DIR_32)
|
||||
$(V)meson --cross cross-mingw-64.txt $(BUILD_DIR_64)
|
||||
$(V)meson setup $(MESON_OPTIONS) --cross cross-mingw-64.txt $(BUILD_DIR_64)
|
||||
$(V)ninja -C $(BUILD_DIR_64)
|
||||
|
||||
.PHONY: dist # Build and create a zip distribution package
|
||||
|
110
Package.mk
110
Package.mk
@ -3,9 +3,9 @@ $(BUILD_DIR_ZIP)/chuni.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/chuni
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/chuni/DEVICE
|
||||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_32)/chunihook/chunihook.dll \
|
||||
$(BUILD_DIR_GAMES_32)/chunihook/chunihook.dll \
|
||||
$(DIST_DIR)/chuni/segatools.ini \
|
||||
$(DIST_DIR)/chuni/start.bat \
|
||||
$(DIST_DIR)/chuni/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/chuni
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -18,9 +18,9 @@ $(BUILD_DIR_ZIP)/cxb.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/cxb
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/cxb/DEVICE
|
||||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_32)/cxbhook/cxbhook.dll \
|
||||
$(BUILD_DIR_GAMES_32)/cxbhook/cxbhook.dll \
|
||||
$(DIST_DIR)/cxb/segatools.ini \
|
||||
$(DIST_DIR)/cxb/start.bat \
|
||||
$(DIST_DIR)/cxb/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/cxb
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -33,9 +33,9 @@ $(BUILD_DIR_ZIP)/diva.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/divahook/divahook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/divahook/divahook.dll \
|
||||
$(DIST_DIR)/diva/segatools.ini \
|
||||
$(DIST_DIR)/diva/start.bat \
|
||||
$(DIST_DIR)/diva/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/diva
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -48,9 +48,9 @@ $(BUILD_DIR_ZIP)/carol.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol/DEVICE
|
||||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_32)/carolhook/carolhook.dll \
|
||||
$(BUILD_DIR_GAMES_32)/carolhook/carolhook.dll \
|
||||
$(DIST_DIR)/carol/segatools.ini \
|
||||
$(DIST_DIR)/carol/start.bat \
|
||||
$(DIST_DIR)/carol/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/carol
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -63,9 +63,9 @@ $(BUILD_DIR_ZIP)/idz.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/idz
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/idz/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/idzhook/idzhook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/idzhook/idzhook.dll \
|
||||
$(DIST_DIR)/idz/segatools.ini \
|
||||
$(DIST_DIR)/idz/start.bat \
|
||||
$(DIST_DIR)/idz/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/idz
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -78,9 +78,9 @@ $(BUILD_DIR_ZIP)/fgo.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/fgo
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/fgo/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/fgohook/fgohook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/fgohook/fgohook.dll \
|
||||
$(DIST_DIR)/fgo/segatools.ini \
|
||||
$(DIST_DIR)/fgo/start.bat \
|
||||
$(DIST_DIR)/fgo/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/fgo
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -93,10 +93,10 @@ $(BUILD_DIR_ZIP)/idac.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/idac
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/idac/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/idachook/idachook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/idachook/idachook.dll \
|
||||
$(DIST_DIR)/idac/segatools.ini \
|
||||
$(DIST_DIR)/idac/config_hook.json \
|
||||
$(DIST_DIR)/idac/start.bat \
|
||||
$(DIST_DIR)/idac/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/idac
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -109,10 +109,10 @@ $(BUILD_DIR_ZIP)/swdc.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/swdc
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/swdc/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/swdchook/swdchook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/swdchook/swdchook.dll \
|
||||
$(DIST_DIR)/swdc/segatools.ini \
|
||||
$(DIST_DIR)/swdc/config_hook.json \
|
||||
$(DIST_DIR)/swdc/start.bat \
|
||||
$(DIST_DIR)/swdc/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/swdc
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -125,9 +125,9 @@ $(BUILD_DIR_ZIP)/mercury.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/mercuryhook/mercuryhook.dll \
|
||||
$(DIST_DIR)/mercury/segatools.ini \
|
||||
$(DIST_DIR)/mercury/start.bat \
|
||||
$(DIST_DIR)/mercury/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/mercury
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -141,11 +141,11 @@ $(BUILD_DIR_ZIP)/chusan.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/chusan/DEVICE
|
||||
$(V)cp $(DIST_DIR)/chusan/segatools.ini \
|
||||
$(DIST_DIR)/chusan/config_hook.json \
|
||||
$(DIST_DIR)/chusan/start.bat \
|
||||
$(DIST_DIR)/chusan/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/chusan
|
||||
$(V)cp $(BUILD_DIR_32)/chusanhook/chusanhook.dll \
|
||||
$(V)cp $(BUILD_DIR_GAMES_32)/chusanhook/chusanhook.dll \
|
||||
$(BUILD_DIR_ZIP)/chusan/chusanhook_x86.dll
|
||||
$(V)cp $(BUILD_DIR_64)/chusanhook/chusanhook.dll \
|
||||
$(V)cp $(BUILD_DIR_GAMES_64)/chusanhook/chusanhook.dll \
|
||||
$(BUILD_DIR_ZIP)/chusan/chusanhook_x64.dll
|
||||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_ZIP)/chusan/inject_x86.exe
|
||||
@ -162,9 +162,9 @@ $(BUILD_DIR_ZIP)/mu3.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/mu3
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/mu3/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/mu3hook/mu3hook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/mu3hook/mu3hook.dll \
|
||||
$(DIST_DIR)/mu3/segatools.ini \
|
||||
$(DIST_DIR)/mu3/start.bat \
|
||||
$(DIST_DIR)/mu3/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/mu3
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -177,9 +177,9 @@ $(BUILD_DIR_ZIP)/mai2.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/mai2
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/mai2/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/mai2hook/mai2hook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/mai2hook/mai2hook.dll \
|
||||
$(DIST_DIR)/mai2/segatools.ini \
|
||||
$(DIST_DIR)/mai2/start.bat \
|
||||
$(DIST_DIR)/mai2/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/mai2
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -192,10 +192,10 @@ $(BUILD_DIR_ZIP)/cm.zip:
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/cm
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/cm/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/cmhook/cmhook.dll \
|
||||
$(BUILD_DIR_GAMES_64)/cmhook/cmhook.dll \
|
||||
$(DIST_DIR)/cm/config_hook.json \
|
||||
$(DIST_DIR)/cm/segatools.ini \
|
||||
$(DIST_DIR)/cm/start.bat \
|
||||
$(DIST_DIR)/cm/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/cm
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
@ -203,6 +203,59 @@ $(BUILD_DIR_ZIP)/cm.zip:
|
||||
$(V)strip $(BUILD_DIR_ZIP)/cm/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/cm ; zip -r ../cm.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/tokyo.zip:
|
||||
$(V)echo ... $@
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_GAMES_64)/tokyohook/tokyohook.dll \
|
||||
$(DIST_DIR)/tokyo/config_hook.json \
|
||||
$(DIST_DIR)/tokyo/segatools.ini \
|
||||
$(DIST_DIR)/tokyo/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/tokyo
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
$(BUILD_DIR_ZIP)/tokyo/DEVICE
|
||||
$(V)strip $(BUILD_DIR_ZIP)/tokyo/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/tokyo ; zip -r ../tokyo.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/kemono.zip:
|
||||
$(V)echo ... $@
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono/DEVICE
|
||||
$(V)cp $(DIST_DIR)/kemono/segatools.ini \
|
||||
$(DIST_DIR)/kemono/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/kemono
|
||||
$(V)cp $(BUILD_DIR_GAMES_32)/kemonohook/kemonohook.dll \
|
||||
$(BUILD_DIR_ZIP)/kemono/kemonohook_x86.dll
|
||||
$(V)cp $(BUILD_DIR_GAMES_64)/kemonohook/kemonohook.dll \
|
||||
$(BUILD_DIR_ZIP)/kemono/kemonohook_x64.dll
|
||||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_ZIP)/kemono/inject_x86.exe
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_ZIP)/kemono/inject_x64.exe
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
$(BUILD_DIR_ZIP)/kemono/DEVICE
|
||||
for x in exe dll; do strip $(BUILD_DIR_ZIP)/kemono/*.$$x; done
|
||||
$(V)cd $(BUILD_DIR_ZIP)/kemono ; zip -r ../kemono.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/apm3.zip:
|
||||
$(V)echo ... $@
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/apm3
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/apm3/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_GAMES_64)/apm3hook/apm3hook.dll \
|
||||
$(DIST_DIR)/apm3/segatools.ini \
|
||||
$(DIST_DIR)/apm3/launch.bat \
|
||||
$(DIST_DIR)/apm3/config_hook.json \
|
||||
$(BUILD_DIR_ZIP)/apm3
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
$(BUILD_DIR_ZIP)/apm3/DEVICE
|
||||
$(V)strip $(BUILD_DIR_ZIP)/apm3/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/apm3 ; zip -r ../apm3.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/doc.zip: \
|
||||
$(DOC_DIR)/config \
|
||||
$(DOC_DIR)/chunihook.md \
|
||||
@ -225,7 +278,10 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
|
||||
$(BUILD_DIR_ZIP)/mu3.zip \
|
||||
$(BUILD_DIR_ZIP)/mai2.zip \
|
||||
$(BUILD_DIR_ZIP)/cm.zip \
|
||||
$(BUILD_DIR_ZIP)/tokyo.zip \
|
||||
$(BUILD_DIR_ZIP)/fgo.zip \
|
||||
$(BUILD_DIR_ZIP)/kemono.zip \
|
||||
$(BUILD_DIR_ZIP)/apm3.zip \
|
||||
CHANGELOG.md \
|
||||
README.md \
|
||||
|
||||
|
44
README.md
44
README.md
@ -1,33 +1,39 @@
|
||||
# Segatools
|
||||
|
||||
Version: `2023-11-22`
|
||||
Version: `2025-07-27`
|
||||
|
||||
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
||||
|
||||
## List of supported games
|
||||
|
||||
* Chunithm
|
||||
* [Chunithm (Plus)](doc/chunihook.md)
|
||||
* [Chunithm Air (Plus)](doc/chunihook.md)
|
||||
* [Chunithm Star (Plus)](doc/chunihook.md)
|
||||
* [Chunithm Amazon (Plus)](doc/chunihook.md)
|
||||
* [Chunithm Crystal (Plus)](doc/chunihook.md)
|
||||
* Chunithm SUN
|
||||
* Card Maker
|
||||
* starting from Card Maker
|
||||
* CHUNITHM
|
||||
* up to [CHUNITHM PARADISE LOST](doc/chunihook.md)
|
||||
* starting from CHUNITHM NEW!!
|
||||
* crossbeats REV.
|
||||
* up to crossbeats REV. SUNRISE
|
||||
* Fate/Grand Order
|
||||
* Fate/Grand Order Arcade
|
||||
* Hatsune Miku: Project DIVA Arcade
|
||||
* up to Future Tone
|
||||
* Initial D
|
||||
* [Initial D Arcade Stage Zero](doc/idzhook.md)
|
||||
* Initial D THE ARCADE
|
||||
* SEGA World Drivers Championship
|
||||
* up to SEGA World Drivers Championship 2019
|
||||
* Fate/Grand Order
|
||||
* Fate/Grand Order Arcade
|
||||
* ONGEKI
|
||||
* up to bright MEMORY
|
||||
* maimai DX
|
||||
* up to maimai DX FESTiVAL PLUS
|
||||
* Card Maker
|
||||
* up to Card Maker 1.35
|
||||
* Wacca
|
||||
* up to WACCA Reverse
|
||||
* starting from maimai DX
|
||||
* Mario & Sonic
|
||||
* Mario & Sonic at the Tokyo 2020 Olympics Arcade
|
||||
* O.N.G.E.K.I.
|
||||
* starting from O.N.G.E.K.I.
|
||||
* SEGA World Drivers Championship
|
||||
* SEGA World Drivers Championship 2019
|
||||
* WACCA
|
||||
* starting from WACCA
|
||||
* Kemono Friends
|
||||
* Kemono Friends 3: Planet Tours
|
||||
* ALL.Net P-ras MULTI Version 3
|
||||
* starting from ALL.Net P-ras MULTI Version 3 1.01
|
||||
|
||||
## End-users
|
||||
|
||||
|
70
board/vfd.c
70
board/vfd.c
@ -1,70 +0,0 @@
|
||||
/* This is some sort of LCD display found on various cabinets. It is driven
|
||||
directly by amdaemon, and it has something to do with displaying the status
|
||||
of electronic payments.
|
||||
|
||||
Part number in schematics is "VFD GP1232A02A FUTABA".
|
||||
|
||||
Little else about this board is known. Black-holing the RS232 comms that it
|
||||
receives seems to be sufficient for the time being. */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/vfd.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/uart.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp);
|
||||
|
||||
static struct uart vfd_uart;
|
||||
static uint8_t vfd_written[512];
|
||||
static uint8_t vfd_readable[512];
|
||||
|
||||
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
uart_init(&vfd_uart, port_no);
|
||||
vfd_uart.written.bytes = vfd_written;
|
||||
vfd_uart.written.nbytes = sizeof(vfd_written);
|
||||
vfd_uart.readable.bytes = vfd_readable;
|
||||
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
||||
|
||||
dprintf("VFD: hook enabled.\n");
|
||||
|
||||
return iohook_push_handler(vfd_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (!uart_match_irp(&vfd_uart, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&vfd_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("VFD TX:\n");
|
||||
dump_iobuf(&vfd_uart.written);
|
||||
vfd_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
10
board/vfd.h
10
board/vfd.h
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct vfd_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
|
||||
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no);
|
@ -1,95 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/dll.h"
|
||||
#include "hooklib/path.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static void dll_hook_insert_hooks(HMODULE target);
|
||||
|
||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
|
||||
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
|
||||
|
||||
static const struct hook_symbol unity_kernel32_syms[] = {
|
||||
{
|
||||
.name = "LoadLibraryW",
|
||||
.patch = my_LoadLibraryW,
|
||||
.link = (void **) &next_LoadLibraryW,
|
||||
},
|
||||
};
|
||||
|
||||
static const wchar_t *target_modules[] = {
|
||||
L"mono.dll",
|
||||
L"cri_ware_unity.dll",
|
||||
};
|
||||
static const size_t target_modules_len = _countof(target_modules);
|
||||
|
||||
void unity_hook_init(void)
|
||||
{
|
||||
dll_hook_insert_hooks(NULL);
|
||||
}
|
||||
|
||||
static void dll_hook_insert_hooks(HMODULE target)
|
||||
{
|
||||
hook_table_apply(
|
||||
target,
|
||||
"kernel32.dll",
|
||||
unity_kernel32_syms,
|
||||
_countof(unity_kernel32_syms));
|
||||
}
|
||||
|
||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
|
||||
{
|
||||
const wchar_t *name_end;
|
||||
const wchar_t *target_module;
|
||||
bool already_loaded;
|
||||
HMODULE result;
|
||||
size_t name_len;
|
||||
size_t target_module_len;
|
||||
|
||||
if (name == NULL) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if the module is already loaded
|
||||
already_loaded = GetModuleHandleW(name) != NULL;
|
||||
|
||||
// Must call the next handler so the DLL reference count is incremented
|
||||
result = next_LoadLibraryW(name);
|
||||
|
||||
if (!already_loaded && result != NULL) {
|
||||
name_len = wcslen(name);
|
||||
|
||||
for (size_t i = 0; i < target_modules_len; i++) {
|
||||
target_module = target_modules[i];
|
||||
target_module_len = wcslen(target_module);
|
||||
|
||||
// Check if the newly loaded library is at least the length of
|
||||
// the name of the target module
|
||||
if (name_len < target_module_len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
name_end = &name[name_len - target_module_len];
|
||||
|
||||
// Check if the name of the newly loaded library is one of the
|
||||
// modules the path hooks should be injected into
|
||||
if (_wcsicmp(name_end, target_module) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dprintf("Unity: Loaded %S\n", target_module);
|
||||
|
||||
dll_hook_insert_hooks(result);
|
||||
path_hook_insert_hooks(result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
void unity_hook_init(void);
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "util/crc.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
|
||||
struct aime_io_config {
|
||||
wchar_t aime_path[MAX_PATH];
|
||||
@ -68,7 +69,6 @@ static void aime_io_config_read(
|
||||
cfg->felica_path,
|
||||
_countof(cfg->felica_path),
|
||||
filename);
|
||||
// dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError());
|
||||
|
||||
cfg->felica_gen = GetPrivateProfileIntW(
|
||||
L"aime",
|
||||
@ -223,7 +223,7 @@ uint16_t aime_io_get_api_version(void)
|
||||
|
||||
HRESULT aime_io_init(void)
|
||||
{
|
||||
aime_io_config_read(&aime_io_cfg, L".\\segatools.ini");
|
||||
aime_io_config_read(&aime_io_cfg, get_config_path());
|
||||
|
||||
return S_OK;
|
||||
}
|
@ -3,7 +3,6 @@ aimeio_lib = static_library(
|
||||
name_prefix : '',
|
||||
include_directories: inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
link_with : [
|
||||
util_lib,
|
||||
],
|
@ -1,10 +1,11 @@
|
||||
#include <windows.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "amex/ds.h"
|
||||
@ -19,13 +20,11 @@
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
#define DS_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define DS_IOCTL_SETUP CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define DS_IOCTL_READ_SECTOR CTL_CODE(0x8000, 0x804, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
enum {
|
||||
DS_IOCTL_GET_ABI_VERSION = 0x80006000,
|
||||
DS_IOCTL_SETUP = 0x80006004,
|
||||
DS_IOCTL_READ_SECTOR = 0x80006010,
|
||||
};
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ds_eeprom {
|
||||
uint32_t crc32;
|
@ -6,7 +6,7 @@
|
||||
#include <winnt.h>
|
||||
#endif
|
||||
#include <devioctl.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@ -20,9 +20,7 @@
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum {
|
||||
EEPROM_IOCTL_GET_ABI_VERSION = 0x80006000,
|
||||
};
|
||||
#define EEPROM_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
static HRESULT eeprom_handle_irp(struct irp *irp);
|
||||
static HRESULT eeprom_handle_open(struct irp *irp);
|
@ -1,5 +1,5 @@
|
||||
#include <windows.h>
|
||||
#include <ntstatus.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
@ -13,12 +13,10 @@
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum {
|
||||
GPIO_IOCTL_SET_LEDS = 0x8000A004,
|
||||
GPIO_IOCTL_GET_PSW = 0x80006008,
|
||||
GPIO_IOCTL_GET_DIPSW = 0x8000600C,
|
||||
GPIO_IOCTL_DESCRIBE = 0x80006014,
|
||||
};
|
||||
#define GPIO_IOCTL_SET_LEDS CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_WRITE_ACCESS)
|
||||
#define GPIO_IOCTL_GET_PSW CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define GPIO_IOCTL_GET_DIPSW CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define GPIO_IOCTL_DESCRIBE CTL_CODE(0x8000, 0x805, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
enum {
|
||||
GPIO_TYPE_NONE = 0,
|
@ -4,6 +4,7 @@
|
||||
#include <winternl.h>
|
||||
|
||||
#include <ntstatus.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
@ -21,11 +22,9 @@
|
||||
#include "util/dump.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum {
|
||||
JVS_IOCTL_HELLO = 0x80006004,
|
||||
JVS_IOCTL_SENSE = 0x8000600C,
|
||||
JVS_IOCTL_TRANSACT = 0x8000E008,
|
||||
};
|
||||
#define JVS_IOCTL_HELLO CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define JVS_IOCTL_TRANSACT CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define JVS_IOCTL_SENSE CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
static HRESULT jvs_handle_irp(struct irp *irp);
|
||||
static HRESULT jvs_handle_open(struct irp *irp);
|
||||
@ -185,14 +184,14 @@ static HRESULT jvs_ioctl_sense(struct irp *irp)
|
||||
|
||||
static HRESULT jvs_ioctl_transact(struct irp *irp)
|
||||
{
|
||||
#if 0
|
||||
#if defined(LOG_JVS)
|
||||
dprintf("\nJVS Port: Outbound frame:\n");
|
||||
dump_const_iobuf(&irp->write);
|
||||
#endif
|
||||
|
||||
jvs_bus_transact(jvs_root, irp->write.bytes, irp->write.nbytes, &irp->read);
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_JVS)
|
||||
dprintf("JVS Port: Inbound frame:\n");
|
||||
dump_iobuf(&irp->read);
|
||||
dprintf("\n");
|
@ -2,7 +2,6 @@ amex_lib = static_library(
|
||||
'amex',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
],
|
@ -6,7 +6,7 @@
|
||||
#include <winnt.h>
|
||||
#endif
|
||||
#include <devioctl.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@ -20,9 +20,7 @@
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum {
|
||||
SRAM_IOCTL_GET_ABI_VERSION = 0x80006000,
|
||||
};
|
||||
#define SRAM_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
static HRESULT sram_handle_irp(struct irp *irp);
|
||||
static HRESULT sram_handle_open(struct irp *irp);
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "aimeio/aimeio.h"
|
||||
|
@ -72,8 +72,18 @@ void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
|
||||
|
||||
aime_dll_config_load(&cfg->dll, filename);
|
||||
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"aime", L"portNo", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"aime", L"highBaud", 1, filename);
|
||||
cfg->gen = GetPrivateProfileIntW(L"aime", L"gen", 0, filename);
|
||||
cfg->proxy_flag = GetPrivateProfileIntW(L"aime", L"proxyFlag", 2, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"aime",
|
||||
L"authdataPath",
|
||||
L"DEVICE\\authdata.bin",
|
||||
cfg->authdata_path,
|
||||
_countof(cfg->authdata_path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
|
||||
@ -90,4 +100,14 @@ void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename)
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"vfd", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"vfd", L"portNo", 0, filename);
|
||||
cfg->utf_conversion = GetPrivateProfileIntW(L"vfd", L"utfConversion", 0, filename);
|
||||
}
|
||||
|
||||
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"ffb", L"enable", 1, filename);
|
||||
}
|
@ -6,7 +6,9 @@
|
||||
#include "board/io4.h"
|
||||
#include "board/sg-reader.h"
|
||||
#include "board/vfd.h"
|
||||
#include "board/ffb.h"
|
||||
|
||||
void aime_config_load(struct aime_config *cfg, const wchar_t *filename);
|
||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);
|
||||
void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename);
|
||||
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename);
|
235
common/board/ffb.c
Normal file
235
common/board/ffb.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
Force Feedback Board (FFB)
|
||||
|
||||
This board is used by many SEGA games to provide force feedback to the player.
|
||||
It is driven by the game software over a serial connection and is used by many
|
||||
games such as SEGA World Drivers Championship, Initial D Arcade, ...
|
||||
|
||||
Part number in schematics is "838-15069 MOTOR DRIVE BD RS232/422 Board".
|
||||
|
||||
Some observations:
|
||||
The maximal strength for any effect is 127, except Damper which maxes out at 40.
|
||||
The period for rumble effects is in the range 0-40.
|
||||
*/
|
||||
|
||||
#include "board/ffb.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
#include "hooklib/uart.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
|
||||
// request format:
|
||||
// 0x?? - sync + command
|
||||
// 0x?? - direction/additional command
|
||||
// 0x?? - strength
|
||||
// 0x?? - checksum (sum of everything except the sync byte)
|
||||
|
||||
enum {
|
||||
FFB_CMD_TOGGLE = 0x80,
|
||||
FFB_CMD_CONSTANT_FORCE = 0x84,
|
||||
FFB_CMD_RUMBLE = 0x85,
|
||||
FFB_CMD_DAMPER = 0x86,
|
||||
};
|
||||
|
||||
struct ffb_hdr {
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
union ffb_req_any {
|
||||
struct ffb_hdr hdr;
|
||||
uint8_t bytes[3];
|
||||
};
|
||||
|
||||
static HRESULT ffb_handle_irp(struct irp *irp);
|
||||
|
||||
static HRESULT ffb_req_dispatch(const union ffb_req_any *req);
|
||||
static HRESULT ffb_req_toggle(const uint8_t *bytes);
|
||||
static HRESULT ffb_req_constant_force(const uint8_t *bytes);
|
||||
static HRESULT ffb_req_rumble(const uint8_t *bytes);
|
||||
static HRESULT ffb_req_damper(const uint8_t *bytes);
|
||||
|
||||
static const struct ffb_ops *ffb_ops;
|
||||
static struct uart ffb_uart;
|
||||
|
||||
static bool ffb_started;
|
||||
static HRESULT ffb_start_hr;
|
||||
static uint8_t ffb_written[4];
|
||||
static uint8_t ffb_readable[4];
|
||||
|
||||
/* Static variables to store maximum strength values */
|
||||
static uint8_t max_constant_force = 0;
|
||||
static uint8_t max_rumble = 0;
|
||||
static uint8_t max_period = 0;
|
||||
static uint8_t max_damper = 0;
|
||||
|
||||
HRESULT ffb_hook_init(
|
||||
const struct ffb_config *cfg,
|
||||
const struct ffb_ops *ops,
|
||||
unsigned int port_no)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
ffb_ops = ops;
|
||||
|
||||
uart_init(&ffb_uart, port_no);
|
||||
ffb_uart.written.bytes = ffb_written;
|
||||
ffb_uart.written.nbytes = sizeof(ffb_written);
|
||||
ffb_uart.readable.bytes = ffb_readable;
|
||||
ffb_uart.readable.nbytes = sizeof(ffb_readable);
|
||||
|
||||
dprintf("FFB: hook enabled.\n");
|
||||
|
||||
return iohook_push_handler(ffb_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT ffb_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (!uart_match_irp(&ffb_uart, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&ffb_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
assert(&ffb_uart.written != NULL);
|
||||
assert(ffb_uart.written.bytes != NULL || ffb_uart.written.nbytes == 0);
|
||||
assert(ffb_uart.written.pos <= ffb_uart.written.nbytes);
|
||||
|
||||
// dprintf("FFB TX:\n");
|
||||
|
||||
hr = ffb_req_dispatch((const union ffb_req_any *) ffb_uart.written.bytes);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("FFB: Processing error: %x\n", (int)hr);
|
||||
}
|
||||
|
||||
// dump_iobuf(&ffb_uart.written);
|
||||
ffb_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_dispatch(const union ffb_req_any *req)
|
||||
{
|
||||
switch (req->hdr.cmd) {
|
||||
case FFB_CMD_TOGGLE:
|
||||
return ffb_req_toggle(req->bytes);
|
||||
case FFB_CMD_CONSTANT_FORCE:
|
||||
return ffb_req_constant_force(req->bytes);
|
||||
case FFB_CMD_RUMBLE:
|
||||
return ffb_req_rumble(req->bytes);
|
||||
case FFB_CMD_DAMPER:
|
||||
return ffb_req_damper(req->bytes);
|
||||
|
||||
/* There are some test mode specfic commands which doesn't seem to be used in
|
||||
game at all. The same is true for the initialization phase. */
|
||||
|
||||
default:
|
||||
dprintf("FFB: Unhandled command %02x\n", req->hdr.cmd);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_toggle(const uint8_t *bytes)
|
||||
{
|
||||
uint8_t activate = bytes[2];
|
||||
|
||||
if (activate == 0x01) {
|
||||
dprintf("FFB: Activated\n");
|
||||
} else {
|
||||
dprintf("FFB: Deactivated\n");
|
||||
}
|
||||
|
||||
if (ffb_ops->toggle != NULL) {
|
||||
ffb_ops->toggle(activate == 0x01);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_constant_force(const uint8_t *bytes)
|
||||
{
|
||||
// dprintf("FFB: Constant force\n");
|
||||
|
||||
uint8_t direction = bytes[1];
|
||||
uint8_t force = bytes[2];
|
||||
|
||||
if (direction == 0x0) {
|
||||
// Right
|
||||
force = 128 - force;
|
||||
}
|
||||
|
||||
// Update max strength if the current force is greater
|
||||
if (force > max_constant_force) {
|
||||
max_constant_force = force;
|
||||
}
|
||||
|
||||
// dprintf("FFB: Constant Force Strength: %d (Max: %d)\n", force, max_constant_force);
|
||||
if (ffb_ops->constant_force != NULL) {
|
||||
ffb_ops->constant_force(direction, force);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_rumble(const uint8_t *bytes)
|
||||
{
|
||||
// dprintf("FFB: Rumble\n");
|
||||
|
||||
uint8_t force = bytes[1];
|
||||
uint8_t period = bytes[2];
|
||||
|
||||
// Update max strength if the current force is greater
|
||||
if (force > max_rumble) {
|
||||
max_rumble = force;
|
||||
}
|
||||
|
||||
if (period > max_period) {
|
||||
max_period = period;
|
||||
}
|
||||
|
||||
// dprintf("FFB: Rumble Period: %d (Max %d), Strength: %d (Max: %d)\n", period, max_period, force, max_rumble);
|
||||
if (ffb_ops->rumble != NULL) {
|
||||
ffb_ops->rumble(force, period);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_damper(const uint8_t *bytes)
|
||||
{
|
||||
// dprintf("FFB: Damper\n");
|
||||
|
||||
uint8_t force = bytes[2];
|
||||
|
||||
// Update max strength if the current force is greater
|
||||
if (force > max_damper) {
|
||||
max_damper = force;
|
||||
}
|
||||
|
||||
// dprintf("FFB: Damper Strength: %d (Max: %d)\n", force, max_damper);
|
||||
if (ffb_ops->damper != NULL) {
|
||||
ffb_ops->damper(force);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
21
common/board/ffb.h
Normal file
21
common/board/ffb.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct ffb_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct ffb_ops {
|
||||
void (*toggle)(bool active);
|
||||
void (*constant_force)(uint8_t direction, uint8_t force);
|
||||
void (*rumble)(uint8_t force, uint8_t period);
|
||||
void (*damper)(uint8_t force);
|
||||
};
|
||||
|
||||
HRESULT ffb_hook_init(
|
||||
const struct ffb_config *cfg,
|
||||
const struct ffb_ops *ops,
|
||||
unsigned int port_no);
|
@ -16,6 +16,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/io3.h"
|
||||
|
||||
@ -79,6 +80,11 @@ static HRESULT io3_cmd_read_analogs(
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_read_rotarys(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_write_gpio(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
@ -116,6 +122,13 @@ static uint8_t io3_features[] = {
|
||||
|
||||
0x03, 8, 10, 0,
|
||||
|
||||
/* Feature : 0x04 : Rotary inputs
|
||||
Param1 : 4 : Number of rotary channels
|
||||
Param2 : 0 : N/A
|
||||
Param3 : 0 : N/A */
|
||||
|
||||
0x04, 4, 0, 0,
|
||||
|
||||
/* Feature : 0x12 : GPIO outputs
|
||||
Param1 : 3 : Number of ports (8 bits per port)
|
||||
Param2 : 0 : N/A
|
||||
@ -219,6 +232,9 @@ static HRESULT io3_cmd(
|
||||
case JVS_CMD_READ_ANALOGS:
|
||||
return io3_cmd_read_analogs(io3, req, resp);
|
||||
|
||||
case JVS_CMD_READ_ROTARYS:
|
||||
return io3_cmd_read_rotarys(io3, req, resp);
|
||||
|
||||
case JVS_CMD_WRITE_GPIO:
|
||||
return io3_cmd_write_gpio(io3, req, resp);
|
||||
|
||||
@ -375,7 +391,7 @@ static HRESULT io3_cmd_read_switches(
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_IO3)
|
||||
dprintf("JVS I/O: Read switches, np=%i, bpp=%i\n",
|
||||
req.num_players,
|
||||
req.bytes_per_player);
|
||||
@ -536,6 +552,60 @@ static HRESULT io3_cmd_read_analogs(
|
||||
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_read_rotarys(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
struct jvs_req_read_rotarys req;
|
||||
uint16_t rotarys[4];
|
||||
uint8_t i;
|
||||
HRESULT hr;
|
||||
|
||||
/* Read req */
|
||||
|
||||
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (req.nrotarys > _countof(rotarys)) {
|
||||
dprintf("JVS I/O: Invalid analog count %i\n", req.nrotarys);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
//dprintf("JVS I/O: Read rotarys, nrotarys=%i\n", req.nrotarys);
|
||||
|
||||
/* Write report byte */
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x01);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Write analogs */
|
||||
|
||||
memset(rotarys, 0, sizeof(rotarys));
|
||||
|
||||
if (io3->ops->read_rotarys != NULL) {
|
||||
io3->ops->read_rotarys(io3->ops_ctx, rotarys, req.nrotarys);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < req.nrotarys ; i++) {
|
||||
hr = iobuf_write_be16(resp_buf, rotarys[i]);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_write_gpio(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
@ -18,6 +18,7 @@ struct io3_ops {
|
||||
void (*write_gpio)(void *ctx, uint32_t state);
|
||||
void (*read_switches)(void *ctx, struct io3_switch_state *out);
|
||||
void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs);
|
||||
void (*read_rotarys)(void *ctx, uint16_t *rotaries, uint8_t nrotaries);
|
||||
void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out);
|
||||
};
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/guid.h"
|
||||
@ -48,7 +49,7 @@ static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
|
||||
struct io4_report_out {
|
||||
uint8_t report_id;
|
||||
uint8_t cmd;
|
||||
uint8_t payload[62];
|
||||
uint8_t payload[IO4_REPORT_OUT_PAYLOAD_LEN];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct io4_report_out) == 0x40, "IO4 OUT report size");
|
||||
@ -223,7 +224,11 @@ static HRESULT io4_handle_write(struct irp *irp)
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_GENERAL_OUTPUT:
|
||||
dprintf("USB I/O: GPIO Out\n");
|
||||
// dprintf("USB I/O: GPIO Out\n");
|
||||
|
||||
if (io4_ops->write_gpio != NULL) {
|
||||
return io4_ops->write_gpio(out.payload, IO4_REPORT_OUT_PAYLOAD_LEN);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
@ -3,6 +3,9 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define IO4_REPORT_OUT_PAYLOAD_LEN 62
|
||||
|
||||
enum {
|
||||
/* System buttons in button[0] */
|
||||
@ -24,6 +27,7 @@ struct io4_state {
|
||||
|
||||
struct io4_ops {
|
||||
HRESULT (*poll)(void *ctx, struct io4_state *state);
|
||||
HRESULT (*write_gpio)(uint8_t* payload, size_t len);
|
||||
};
|
||||
|
||||
HRESULT io4_hook_init(
|
81
common/board/led15070-cmd.h
Normal file
81
common/board/led15070-cmd.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/led15070-frame.h"
|
||||
|
||||
/* Command IDs */
|
||||
|
||||
enum {
|
||||
LED_15070_CMD_RESET = 0x10,
|
||||
LED_15070_CMD_SET_INPUT = 0x28, // No known use case
|
||||
LED_15070_CMD_SET_NORMAL_12BIT = 0x30, // TODO
|
||||
LED_15070_CMD_SET_NORMAL_8BIT = 0x31,
|
||||
LED_15070_CMD_SET_MULTI_FLASH_8BIT = 0x32,
|
||||
LED_15070_CMD_SET_MULTI_FADE_8BIT = 0x33,
|
||||
LED_15070_CMD_SET_PALETTE_7_NORMAL_LED = 0x34, // No known use case
|
||||
LED_15070_CMD_SET_PALETTE_6_FLASH_LED = 0x35, // No known use case
|
||||
LED_15070_CMD_SET_15DC_OUT = 0x36, // No known use case
|
||||
LED_15070_CMD_SET_15GS_OUT = 0x37, // No known use case
|
||||
LED_15070_CMD_SET_PSC_MAX = 0x38, // No known use case
|
||||
LED_15070_CMD_SET_FET_OUTPUT = 0x39,
|
||||
LED_15070_CMD_SET_GS_PALETTE = 0x3A,
|
||||
LED_15070_CMD_DC_UPDATE = 0x3B,
|
||||
LED_15070_CMD_GS_UPDATE = 0x3C,
|
||||
LED_15070_CMD_ROTATE = 0x3E, // No known use case, wtf is this?
|
||||
LED_15070_CMD_SET_DC_DATA = 0x3F,
|
||||
LED_15070_CMD_EEPROM_WRITE = 0x7B,
|
||||
LED_15070_CMD_EEPROM_READ = 0x7C,
|
||||
LED_15070_CMD_ACK_ON = 0x7D,
|
||||
LED_15070_CMD_ACK_OFF = 0x7E,
|
||||
LED_15070_CMD_BOARD_INFO = 0xF0,
|
||||
LED_15070_CMD_BOARD_STATUS = 0xF1,
|
||||
LED_15070_CMD_FW_SUM = 0xF2,
|
||||
LED_15070_CMD_PROTOCOL_VER = 0xF3,
|
||||
LED_15070_CMD_TO_BOOT_MODE = 0xFD,
|
||||
LED_15070_CMD_FW_UPDATE = 0xFE,
|
||||
};
|
||||
|
||||
/* Response codes */
|
||||
|
||||
enum {
|
||||
LED_15070_STATUS_OK = 0x01,
|
||||
LED_15070_STATUS_SUM_ERR = 0x02,
|
||||
LED_15070_STATUS_PARITY_ERR = 0x03,
|
||||
LED_15070_STATUS_FRAMING_ERR = 0x04,
|
||||
LED_15070_STATUS_OVERRUN_ERR = 0x05,
|
||||
LED_15070_STATUS_BUFFER_OVERFLOW = 0x06,
|
||||
};
|
||||
|
||||
enum {
|
||||
LED_15070_REPORT_OK = 0x01,
|
||||
LED_15070_REPORT_WAIT = 0x02,
|
||||
LED_15070_REPORT_ERR1 = 0x03,
|
||||
LED_15070_REPORT_ERR2 = 0x04,
|
||||
};
|
||||
|
||||
/* Request data structures */
|
||||
|
||||
struct led15070_req_any {
|
||||
struct led15070_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t payload[256];
|
||||
};
|
||||
|
||||
/* Response data structures */
|
||||
|
||||
struct led15070_resp_any {
|
||||
struct led15070_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
uint8_t data[32];
|
||||
};
|
||||
|
||||
struct led15070_resp_board_info {
|
||||
struct led15070_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
char board_num[8];
|
||||
uint8_t endcode; // Always 0xFF
|
||||
uint8_t fw_ver;
|
||||
};
|
194
common/board/led15070-frame.c
Normal file
194
common/board/led15070-frame.c
Normal file
@ -0,0 +1,194 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/led15070-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
static void led15070_frame_sync(struct iobuf *src);
|
||||
static HRESULT led15070_frame_accept(const struct iobuf *dest);
|
||||
static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
[0] Sync byte (0xE0)
|
||||
[1] Destination address
|
||||
[2] Source Address
|
||||
[3] Length of data/payload
|
||||
[4] Data/payload
|
||||
For requests (host to board):
|
||||
[0] Command
|
||||
... Payload
|
||||
For responses (board to host):
|
||||
[0] Status
|
||||
[1] Command
|
||||
[2] Report
|
||||
... Payload
|
||||
[n] Checksum: Sum of all prior bytes (excluding sync byte)
|
||||
|
||||
Byte stuffing:
|
||||
|
||||
0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */
|
||||
|
||||
static void led15070_frame_sync(struct iobuf *src)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < src->pos && src->bytes[i] != 0xE0 ; i++);
|
||||
|
||||
src->pos -= i;
|
||||
memmove(&src->bytes[0], &src->bytes[i], i);
|
||||
}
|
||||
|
||||
static HRESULT led15070_frame_accept(const struct iobuf *dest)
|
||||
{
|
||||
uint8_t checksum;
|
||||
size_t i;
|
||||
|
||||
if (dest->pos < 3 || dest->pos != dest->bytes[3] + 5) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
|
||||
for (i = 1 ; i < dest->pos - 1 ; i++) {
|
||||
checksum += dest->bytes[i];
|
||||
}
|
||||
|
||||
//dprintf("LED checksum %02x, expected %02x\n", checksum, dest->bytes[dest->pos - 1]);
|
||||
|
||||
if (checksum != dest->bytes[dest->pos - 1]) {
|
||||
return HRESULT_FROM_WIN32(ERROR_CRC);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src)
|
||||
{
|
||||
uint8_t byte;
|
||||
bool escape;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
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);
|
||||
|
||||
led15070_frame_sync(src);
|
||||
|
||||
dest->pos = 0;
|
||||
escape = false;
|
||||
|
||||
for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) {
|
||||
/* Step the FSM to unstuff another byte */
|
||||
|
||||
byte = src->bytes[i];
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
} else if (i == 0) {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
} else if (byte == 0xE0) {
|
||||
hr = E_FAIL;
|
||||
} else if (byte == 0xD0) {
|
||||
if (escape) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
escape = true;
|
||||
} else if (escape) {
|
||||
dest->bytes[dest->pos++] = byte + 1;
|
||||
escape = false;
|
||||
} else {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
|
||||
/* Try to accept the packet we've built up so far */
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = led15070_frame_accept(dest);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle FSM terminal state */
|
||||
|
||||
if (hr != S_FALSE) {
|
||||
/* Frame was either accepted or rejected, remove it from src */
|
||||
memmove(&src->bytes[0], &src->bytes[i], src->pos - i);
|
||||
src->pos -= i;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT led15070_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes)
|
||||
{
|
||||
const uint8_t *src;
|
||||
uint8_t checksum;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
assert(nbytes >= 3 && src[0] == 0xE0 && src[3] + 4 == nbytes);
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xE0;
|
||||
checksum = 0;
|
||||
// dprintf("%02x ", 0xe0);
|
||||
|
||||
for (i = 1 ; i < nbytes ; i++) {
|
||||
byte = src[i];
|
||||
checksum += byte;
|
||||
// dprintf("%02x ", byte);
|
||||
|
||||
hr = led15070_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
// dprintf("%02x \n", checksum);
|
||||
|
||||
return led15070_frame_encode_byte(dest, checksum);
|
||||
}
|
||||
|
||||
static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte)
|
||||
{
|
||||
if (byte == 0xE0 || byte == 0xD0) {
|
||||
if (dest->pos + 2 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xD0;
|
||||
dest->bytes[dest->pos++] = byte - 1;
|
||||
} else {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
26
common/board/led15070-frame.h
Normal file
26
common/board/led15070-frame.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
LED_15070_FRAME_SYNC = 0xE0,
|
||||
};
|
||||
|
||||
struct led15070_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t dest_adr;
|
||||
uint8_t src_adr;
|
||||
uint8_t nbytes;
|
||||
};
|
||||
|
||||
HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src);
|
||||
|
||||
HRESULT led15070_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
1300
common/board/led15070.c
Normal file
1300
common/board/led15070.c
Normal file
File diff suppressed because it is too large
Load Diff
28
common/board/led15070.h
Normal file
28
common/board/led15070.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct led15070_config {
|
||||
bool enable;
|
||||
unsigned int port_no[2];
|
||||
char board_number[8];
|
||||
uint8_t fw_ver;
|
||||
uint16_t fw_sum;
|
||||
wchar_t eeprom_path[MAX_PATH];
|
||||
};
|
||||
|
||||
typedef HRESULT (*io_led_init_t)(void);
|
||||
typedef void (*io_led_set_fet_output_t)(uint8_t board, const uint8_t *rgb);
|
||||
typedef void (*io_led_dc_update_t)(uint8_t board, const uint8_t *rgb);
|
||||
typedef void (*io_led_gs_update_t)(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
HRESULT led15070_hook_init(
|
||||
const struct led15070_config *cfg,
|
||||
io_led_init_t _led_init,
|
||||
io_led_set_fet_output_t _led_set_fet_output,
|
||||
io_led_dc_update_t _led_dc_update,
|
||||
io_led_gs_update_t _led_gs_update,
|
||||
unsigned int port_no[2]);
|
@ -88,7 +88,7 @@ struct led15093_req_reset {
|
||||
struct led15093_req_set_timeout {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t count;
|
||||
uint16_t count;
|
||||
};
|
||||
|
||||
struct led15093_req_set_disable_response {
|
||||
@ -199,7 +199,7 @@ struct led15093_resp_board_info {
|
||||
char chip_num[5];
|
||||
uint8_t endcode; // Always 0xFF
|
||||
uint8_t fw_ver;
|
||||
uint8_t rx_buf;
|
||||
uint16_t rx_buf;
|
||||
};
|
||||
|
||||
struct led15093_resp_protocol_ver {
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
SEGA 837-15093-XX LED Controller Board emulator
|
||||
|
||||
|
||||
Supported variants:
|
||||
|
||||
837-15093
|
||||
@ -20,6 +20,7 @@
|
||||
#include <process.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "board/led15093-cmd.h"
|
||||
@ -106,9 +107,13 @@ static uint8_t led15093_host_adr = 1;
|
||||
static io_led_init_t led_init;
|
||||
static io_led_set_leds_t set_leds;
|
||||
|
||||
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
|
||||
io_led_set_leds_t _set_leds, unsigned int first_port, unsigned int num_boards, uint8_t board_adr, uint8_t host_adr)
|
||||
HRESULT led15093_hook_init(
|
||||
const struct led15093_config *cfg,
|
||||
io_led_init_t _led_init,
|
||||
io_led_set_leds_t _set_leds,
|
||||
unsigned int port_no[2])
|
||||
{
|
||||
unsigned int num_boards = 0;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(_led_init != NULL);
|
||||
@ -118,14 +123,24 @@ HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
if (cfg->port_no != 0) {
|
||||
first_port = cfg->port_no;
|
||||
for (int i = 0; i < led15093_nboards; i++)
|
||||
{
|
||||
if (cfg->port_no[i] != 0) {
|
||||
port_no[i] = cfg->port_no[i];
|
||||
}
|
||||
|
||||
if (port_no[i] != 0) {
|
||||
num_boards++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(num_boards != 0);
|
||||
|
||||
led15093_board_adr = num_boards;
|
||||
led15093_host_adr = num_boards == 2 ? 1 : 2;
|
||||
|
||||
led_init = _led_init;
|
||||
set_leds = _set_leds;
|
||||
led15093_board_adr = board_adr;
|
||||
led15093_host_adr = host_adr;
|
||||
|
||||
memcpy(led15093_board_num, cfg->board_number, sizeof(led15093_board_num));
|
||||
memcpy(led15093_chip_num, cfg->chip_number, sizeof(led15093_chip_num));
|
||||
@ -139,7 +154,7 @@ HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led
|
||||
|
||||
InitializeCriticalSection(&vb->lock);
|
||||
|
||||
uart_init(&vb->boarduart, first_port + i);
|
||||
uart_init(&vb->boarduart, port_no[i]);
|
||||
if (cfg->high_baudrate) {
|
||||
vb->boarduart.baud.BaudRate = 460800;
|
||||
} else {
|
||||
@ -208,7 +223,6 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
|
||||
_led15093_per_board_vars *v = &led15093_per_board_vars[board];
|
||||
struct uart *boarduart = &led15093_per_board_vars[board].boarduart;
|
||||
|
||||
/*
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
// Unfortunately the LED board UART gets opened and closed repeatedly
|
||||
|
||||
@ -235,30 +249,6 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
dprintf("LED 15093: Starting backend DLL\n");
|
||||
// int res = led_init();
|
||||
hr = led_init();
|
||||
|
||||
/*
|
||||
if (res != 0) {
|
||||
dprintf("LED 15093: Backend error, LED board disconnected: "
|
||||
"%d\n",
|
||||
res);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
*/
|
||||
if (FAILED(hr)) {
|
||||
dprintf("LED 15093: Backend error, LED board disconnected: "
|
||||
"%x\n",
|
||||
(int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(boarduart, irp);
|
||||
|
||||
@ -267,12 +257,12 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
#if 0
|
||||
#if defined(LOG_LED15093)
|
||||
dprintf("TX Buffer:\n");
|
||||
dump_iobuf(&boarduart->written);
|
||||
#endif
|
||||
|
||||
req_iobuf.bytes = (byte*)&req;
|
||||
req_iobuf.bytes = (uint8_t*)&req;
|
||||
req_iobuf.nbytes = sizeof(req.hdr) + sizeof(req.payload);
|
||||
req_iobuf.pos = 0;
|
||||
|
||||
@ -294,7 +284,7 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_LED15093)
|
||||
dprintf("Deframe Buffer:\n");
|
||||
dump_iobuf(&req_iobuf);
|
||||
#endif
|
||||
@ -687,16 +677,6 @@ static HRESULT led15093_req_set_imm_led(int board, const struct led15093_req_set
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
/*
|
||||
if (board == 0) {
|
||||
dprintf("board %d: red: %d, green: %d, blue: %d\n", board, req->data[0x96], req->data[0x97], req->data[0x98]);
|
||||
}
|
||||
else if (board == 1)
|
||||
{
|
||||
dprintf("board %d: red: %d, green: %d, blue: %d\n", board, req->data[0xb4], req->data[0xb5], req->data[0xb6]);
|
||||
}
|
||||
*/
|
||||
|
||||
// Return the current LED data, remove const qualifier
|
||||
set_leds(board, (uint8_t *) req->data);
|
||||
|
||||
@ -717,7 +697,7 @@ static HRESULT led15093_req_set_imm_led(int board, const struct led15093_req_set
|
||||
resp.status = v->status_code;
|
||||
if (req->cmd == LED_15093_CMD_SET_IMM_LED) {
|
||||
resp.cmd = LED_15093_CMD_SET_IMM_LED;
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// resp.cmd = LED_15093_CMD_SET_IMM_LED_LEGACY;
|
||||
// }
|
@ -8,7 +8,7 @@
|
||||
struct led15093_config {
|
||||
bool enable;
|
||||
bool high_baudrate;
|
||||
unsigned int port_no;
|
||||
unsigned int port_no[2];
|
||||
char board_number[8];
|
||||
char chip_number[5];
|
||||
char boot_chip_number[5];
|
||||
@ -20,5 +20,5 @@ typedef HRESULT (*io_led_init_t)(void);
|
||||
typedef void (*io_led_set_leds_t)(uint8_t board, uint8_t *rgb);
|
||||
|
||||
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
|
||||
io_led_set_leds_t _set_leds, unsigned int first_port, unsigned int num_boards, uint8_t board_adr, uint8_t host_adr);
|
||||
io_led_set_leds_t _set_leds, unsigned int port_no[2]);
|
||||
|
@ -2,7 +2,6 @@ board_lib = static_library(
|
||||
'board',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
],
|
||||
@ -25,6 +24,11 @@ board_lib = static_library(
|
||||
'led15093-frame.h',
|
||||
'led15093.c',
|
||||
'led15093.h',
|
||||
'led15070-cmd.h',
|
||||
'led15070-frame.c',
|
||||
'led15070-frame.h',
|
||||
'led15070.c',
|
||||
'led15070.h',
|
||||
'sg-cmd.c',
|
||||
'sg-cmd.h',
|
||||
'sg-frame.c',
|
||||
@ -42,5 +46,10 @@ board_lib = static_library(
|
||||
'slider-frame.h',
|
||||
'vfd.c',
|
||||
'vfd.h',
|
||||
'vfd-cmd.h',
|
||||
'vfd-frame.c',
|
||||
'vfd-frame.h',
|
||||
'ffb.c',
|
||||
'ffb.h'
|
||||
],
|
||||
)
|
@ -25,6 +25,13 @@ struct sg_res_header {
|
||||
uint8_t payload_len;
|
||||
};
|
||||
|
||||
/* struct to save the version string with its length
|
||||
to fix NUL terminator issues */
|
||||
struct version_info {
|
||||
const char *version;
|
||||
uint8_t length;
|
||||
};
|
||||
|
||||
typedef HRESULT (*sg_dispatch_fn_t)(
|
||||
void *ctx,
|
||||
const void *req,
|
@ -27,11 +27,11 @@ static HRESULT sg_led_cmd_set_color(
|
||||
const struct sg_led *led,
|
||||
const struct sg_led_req_set_color *req);
|
||||
|
||||
const char *sg_led_info[] = {
|
||||
"15084\xFF\x10\x00\x12",
|
||||
"000-00000\xFF\x11\x40",
|
||||
static const struct version_info led_version[] = {
|
||||
{"15084\xFF\x10\x00\x12", 9},
|
||||
{"000-00000\xFF\x11\x40", 12},
|
||||
// maybe the same?
|
||||
"000-00000\xFF\x11\x40"
|
||||
{"000-00000\xFF\x11\x40", 12}
|
||||
};
|
||||
|
||||
void sg_led_init(
|
||||
@ -156,10 +156,10 @@ static HRESULT sg_led_cmd_get_info(
|
||||
{
|
||||
sg_led_dprintf(led, "Get info\n");
|
||||
|
||||
unsigned int len = strlen(sg_led_info[led->gen - 1]);
|
||||
const struct version_info *fw = &led_version[led->gen - 1];
|
||||
|
||||
sg_res_init(&res->res, req, len);
|
||||
memcpy(res->payload, sg_led_info[led->gen - 1], len);
|
||||
sg_res_init(&res->res, req, fw->length);
|
||||
memcpy(res->payload, fw->version, fw->length);
|
||||
|
||||
return S_OK;
|
||||
}
|
@ -5,19 +5,21 @@
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, /* guess based on time sent */
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x50,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME = 0x51,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA = 0x55,
|
||||
SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_fw_version {
|
||||
@ -32,7 +34,7 @@ struct sg_nfc_res_get_hw_version {
|
||||
|
||||
struct sg_nfc_req_mifare_set_key {
|
||||
struct sg_req_header req;
|
||||
uint8_t key_a[6];
|
||||
uint8_t key[6];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_50 {
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -16,6 +17,7 @@
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
#include "util/slurp.h"
|
||||
|
||||
static HRESULT sg_nfc_dispatch(
|
||||
void *ctx,
|
||||
@ -60,21 +62,26 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
const struct sg_nfc_req_felica_encap *req,
|
||||
struct sg_nfc_res_felica_encap *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_send_hex_data(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static const char *hw_version[] = {
|
||||
"TN32MSEC003S H/W Ver3.0",
|
||||
"837-15286",
|
||||
"837-15396"
|
||||
static const struct version_info hw_version[] = {
|
||||
{"TN32MSEC003S H/W Ver3.0", 23},
|
||||
{"837-15286", 9},
|
||||
{"837-15396", 9}
|
||||
};
|
||||
|
||||
static const char *fw_version[] = {
|
||||
"TN32MSEC003S F/W Ver1.2",
|
||||
"\x94",
|
||||
"\x94"
|
||||
static const struct version_info fw_version[] = {
|
||||
{"TN32MSEC003S F/W Ver1.2", 23},
|
||||
{"\x94", 1},
|
||||
{"\x94", 1}
|
||||
};
|
||||
|
||||
void sg_nfc_init(
|
||||
@ -82,6 +89,8 @@ void sg_nfc_init(
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
void *ops_ctx)
|
||||
{
|
||||
assert(nfc != NULL);
|
||||
@ -91,6 +100,8 @@ void sg_nfc_init(
|
||||
nfc->ops_ctx = ops_ctx;
|
||||
nfc->addr = addr;
|
||||
nfc->gen = gen;
|
||||
nfc->proxy_flag = proxy_flag;
|
||||
nfc->authdata_path = authdata_path;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
@ -184,13 +195,17 @@ static HRESULT sg_nfc_dispatch(
|
||||
&req->felica_encap,
|
||||
&res->felica_encap);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE:
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME:
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA:
|
||||
case SG_NFC_CMD_SEND_HEX_DATA:
|
||||
return sg_nfc_cmd_send_hex_data(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_SELECT_TAG:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
||||
case SG_NFC_CMD_RADIO_ON:
|
||||
case SG_NFC_CMD_RADIO_OFF:
|
||||
case SG_NFC_CMD_SEND_HEX_DATA: // TODO: implement?
|
||||
case SG_NFC_CMD_TO_UPDATE_MODE:
|
||||
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
|
||||
|
||||
default:
|
||||
@ -217,11 +232,11 @@ static HRESULT sg_nfc_cmd_get_fw_version(
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_fw_version *res)
|
||||
{
|
||||
unsigned int len = strlen(fw_version[nfc->gen - 1]);
|
||||
const struct version_info *fw = &fw_version[nfc->gen - 1];
|
||||
|
||||
/* Dest version is not NUL terminated, this is intentional */
|
||||
sg_res_init(&res->res, req, len);
|
||||
memcpy(res->version, fw_version[nfc->gen - 1], len);
|
||||
sg_res_init(&res->res, req, fw->length);
|
||||
memcpy(res->version, fw->version, fw->length);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@ -231,11 +246,11 @@ static HRESULT sg_nfc_cmd_get_hw_version(
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_hw_version *res)
|
||||
{
|
||||
unsigned int len = strlen(hw_version[nfc->gen - 1]);
|
||||
const struct version_info *hw = &hw_version[nfc->gen - 1];
|
||||
|
||||
/* Dest version is not NUL terminated, this is intentional */
|
||||
sg_res_init(&res->res, req, len);
|
||||
memcpy(res->version, hw_version[nfc->gen - 1], len);
|
||||
sg_res_init(&res->res, req, hw->length);
|
||||
memcpy(res->version, hw->version, hw->length);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@ -306,6 +321,7 @@ static HRESULT sg_nfc_poll_aime(
|
||||
|
||||
mifare->type = 0x10;
|
||||
mifare->id_len = sizeof(mifare->uid);
|
||||
// mifare->uid = _byteswap_ulong(0x8FBECBFF);
|
||||
mifare->uid = _byteswap_ulong(0x01020304);
|
||||
|
||||
/* Initialize MIFARE IC emulator */
|
||||
@ -345,13 +361,13 @@ static HRESULT sg_nfc_poll_felica(
|
||||
felica->type = 0x20;
|
||||
felica->id_len = sizeof(felica->IDm) + sizeof(felica->PMm);
|
||||
felica->IDm = _byteswap_uint64(IDm);
|
||||
felica->PMm = _byteswap_uint64(felica_get_generic_PMm());
|
||||
felica->PMm = _byteswap_uint64(felica_get_amusement_ic_PMm());
|
||||
|
||||
/* Initialize FeliCa IC emulator */
|
||||
|
||||
nfc->felica.IDm = IDm;
|
||||
nfc->felica.PMm = felica_get_generic_PMm();
|
||||
nfc->felica.system_code = 0x0000;
|
||||
nfc->felica.PMm = felica_get_amusement_ic_PMm();
|
||||
nfc->felica.system_code = 0x88b4;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@ -373,18 +389,62 @@ static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
|
||||
sg_nfc_dprintf(nfc, "Read uid %08x block %i\n", uid, req->payload.block_no);
|
||||
|
||||
if (req->payload.block_no > 3) {
|
||||
if (req->payload.block_no > 14) {
|
||||
sg_nfc_dprintf(nfc, "MIFARE block number out of range\n");
|
||||
|
||||
return E_FAIL;
|
||||
} else if (req->payload.block_no >= 5){ // emoney auth encrypted
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
char* auth;
|
||||
long size = wslurp(nfc->authdata_path, &auth, false);
|
||||
if (size < 0){
|
||||
sg_nfc_dprintf(nfc, "Failed to read %ls: %lx!\n", nfc->authdata_path, GetLastError());
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
if (req->payload.block_no == 6){
|
||||
offset = 16;
|
||||
} else if (req->payload.block_no == 8){
|
||||
offset = 32;
|
||||
} else if (req->payload.block_no == 9){
|
||||
offset = 48;
|
||||
} else if (req->payload.block_no == 10){
|
||||
offset = 64;
|
||||
} else if (req->payload.block_no == 12){
|
||||
offset = 82;
|
||||
} else if (req->payload.block_no == 13){
|
||||
offset = 98;
|
||||
} else if (req->payload.block_no == 14){
|
||||
offset = 114;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16 && offset + i < size; i++){
|
||||
res->block[i] = auth[offset + i];
|
||||
}
|
||||
|
||||
free(auth);
|
||||
|
||||
} else if (req->payload.block_no == 4){ // emoney auth plain
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
res->block[0] = 0x54; // header
|
||||
res->block[1] = 0x43;
|
||||
res->block[2] = nfc->proxy_flag; // 2 or 3 depending on game (useProxy in env.json)
|
||||
res->block[3] = 0x01; // unknown flag
|
||||
|
||||
} else { // read all other blocks normally
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -420,7 +480,7 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
f_res.nbytes = sizeof(res->payload);
|
||||
f_res.pos = 1;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
dprintf("FELICA OUTBOUND:\n");
|
||||
dump_const_iobuf(&f_req);
|
||||
#endif
|
||||
@ -434,7 +494,7 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
sg_res_init(&res->res, &req->req, f_res.pos);
|
||||
res->payload[0] = f_res.pos;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
dprintf("FELICA INBOUND:\n");
|
||||
dump_iobuf(&f_res);
|
||||
#endif
|
||||
@ -442,6 +502,22 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_send_hex_data(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
/* Firmware checksum length? */
|
||||
if (req->payload_len == 0x2b) {
|
||||
/* The firmware is identical flag? */
|
||||
res->status = 0x20;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
@ -23,8 +23,10 @@ struct sg_nfc {
|
||||
void *ops_ctx;
|
||||
uint8_t addr;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
struct felica felica;
|
||||
struct mifare mifare;
|
||||
const wchar_t* authdata_path;
|
||||
};
|
||||
|
||||
void sg_nfc_init(
|
||||
@ -32,6 +34,8 @@ void sg_nfc_init(
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
void *ops_ctx);
|
||||
|
||||
void sg_nfc_transact(
|
@ -47,7 +47,7 @@ static struct sg_led sg_reader_led;
|
||||
|
||||
HRESULT sg_reader_hook_init(
|
||||
const struct aime_config *cfg,
|
||||
unsigned int port_no,
|
||||
unsigned int default_port_no,
|
||||
unsigned int gen,
|
||||
HINSTANCE self)
|
||||
{
|
||||
@ -66,6 +66,11 @@ HRESULT sg_reader_hook_init(
|
||||
return hr;
|
||||
}
|
||||
|
||||
unsigned int port_no = cfg->port_no;
|
||||
if (port_no == 0){
|
||||
port_no = default_port_no;
|
||||
}
|
||||
|
||||
if (cfg->gen != 0) {
|
||||
gen = cfg->gen;
|
||||
}
|
||||
@ -76,7 +81,7 @@ HRESULT sg_reader_hook_init(
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, NULL);
|
||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, cfg->proxy_flag, cfg->authdata_path, NULL);
|
||||
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, gen, NULL);
|
||||
|
||||
InitializeCriticalSection(&sg_reader_lock);
|
||||
@ -85,6 +90,7 @@ HRESULT sg_reader_hook_init(
|
||||
sg_reader_uart.baud.BaudRate = 38400;
|
||||
}
|
||||
|
||||
dprintf("NFC Assembly: enabling (port=%d)\n", port_no);
|
||||
uart_init(&sg_reader_uart, port_no);
|
||||
sg_reader_uart.written.bytes = sg_reader_written_bytes;
|
||||
sg_reader_uart.written.nbytes = sizeof(sg_reader_written_bytes);
|
||||
@ -115,14 +121,14 @@ static HRESULT sg_reader_handle_irp_locked(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
if (irp->op == IRP_OP_WRITE) {
|
||||
dprintf("WRITE:\n");
|
||||
dump_const_iobuf(&irp->write);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
if (irp->op == IRP_OP_READ) {
|
||||
dprintf("READ:\n");
|
||||
dump_iobuf(&sg_reader_uart.readable);
|
@ -9,12 +9,15 @@
|
||||
struct aime_config {
|
||||
struct aime_dll_config dll;
|
||||
bool enable;
|
||||
unsigned int port_no;
|
||||
bool high_baudrate;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
wchar_t authdata_path[MAX_PATH];
|
||||
};
|
||||
|
||||
HRESULT sg_reader_hook_init(
|
||||
const struct aime_config *cfg,
|
||||
unsigned int port_no,
|
||||
unsigned int default_port_no,
|
||||
unsigned int gen,
|
||||
HINSTANCE self);
|
123
common/board/vfd-cmd.h
Normal file
123
common/board/vfd-cmd.h
Normal file
@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
enum {
|
||||
VFD_CMD_GET_VERSION = 0x5B,
|
||||
VFD_CMD_RESET = 0x0B,
|
||||
VFD_CMD_CLEAR_SCREEN = 0x0C,
|
||||
VFD_CMD_SET_BRIGHTNESS = 0x20,
|
||||
VFD_CMD_SET_SCREEN_ON = 0x21,
|
||||
VFD_CMD_SET_H_SCROLL = 0x22,
|
||||
VFD_CMD_DRAW_IMAGE = 0x2E,
|
||||
VFD_CMD_SET_CURSOR = 0x30,
|
||||
VFD_CMD_SET_ENCODING = 0x32,
|
||||
VFD_CMD_SET_TEXT_WND = 0x40,
|
||||
VFD_CMD_SET_TEXT_SPEED = 0x41,
|
||||
VFD_CMD_WRITE_TEXT = 0x50,
|
||||
VFD_CMD_ENABLE_SCROLL = 0x51,
|
||||
VFD_CMD_DISABLE_SCROLL = 0x52,
|
||||
VFD_CMD_ROTATE = 0x5D,
|
||||
VFD_CMD_CREATE_CHAR = 0xA3,
|
||||
VFD_CMD_CREATE_CHAR2 = 0xA4,
|
||||
};
|
||||
|
||||
enum {
|
||||
VFD_ENC_GB2312 = 0,
|
||||
VFD_ENC_BIG5 = 1,
|
||||
VFD_ENC_SHIFT_JIS = 2,
|
||||
VFD_ENC_KSC5601 = 3,
|
||||
VFD_ENC_MAX = 3,
|
||||
};
|
||||
|
||||
struct vfd_req_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct vfd_req_any {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t payload[2054];
|
||||
};
|
||||
|
||||
struct vfd_req_board_info {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_resp_board_info { // \x0201.20\x03
|
||||
uint8_t unk1;
|
||||
char version[5];
|
||||
uint8_t unk2;
|
||||
};
|
||||
|
||||
struct vfd_req_reset {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_cls {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_brightness {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t brightness;
|
||||
};
|
||||
|
||||
struct vfd_req_power {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t power_state;
|
||||
};
|
||||
|
||||
struct vfd_req_hscroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t x_pos;
|
||||
};
|
||||
|
||||
struct vfd_req_draw {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
uint8_t image[2048];
|
||||
};
|
||||
|
||||
struct vfd_req_cursor {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
};
|
||||
|
||||
struct vfd_req_encoding {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_wnd {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
};
|
||||
|
||||
struct vfd_req_speed {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_scroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_rotate {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_req_create_char {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t type;
|
||||
uint8_t pixels[32];
|
||||
};
|
88
common/board/vfd-frame.c
Normal file
88
common/board/vfd-frame.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define SUPER_VERBOSE 1
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
REQUEST:
|
||||
[0] Sync byte (0x1A or 0x1B)
|
||||
[1] Packet ID
|
||||
[2...n-1] Data/payload
|
||||
|
||||
--- OR ---
|
||||
|
||||
if no sync byte is given, plain static text in the currently configured encoding is expected.
|
||||
|
||||
RESPONSE:
|
||||
This thing never responds, unless it's VFD_CMD_GET_VERSION
|
||||
*/
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src) {
|
||||
return src->bytes[src->pos] == VFD_SYNC_BYTE || src->bytes[src->pos] == VFD_SYNC_BYTE2;
|
||||
}
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes) {
|
||||
const uint8_t *src;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD: RX Buffer:\n");
|
||||
#endif
|
||||
|
||||
for (i = 1; i < nbytes; i++) {
|
||||
byte = src[i];
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("%02x ", byte);
|
||||
#endif
|
||||
|
||||
hr = vfd_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("\n");
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte) {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
|
||||
return S_OK;
|
||||
}
|
20
common/board/vfd-frame.h
Normal file
20
common/board/vfd-frame.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
VFD_SYNC_BYTE = 0x1B,
|
||||
VFD_SYNC_BYTE2 = 0x1A,
|
||||
};
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src);
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
399
common/board/vfd.c
Normal file
399
common/board/vfd.c
Normal file
@ -0,0 +1,399 @@
|
||||
/* This is some sort of LCD display found on various cabinets. It is driven
|
||||
directly by amdaemon, and it has something to do with displaying the status
|
||||
of electronic payments.
|
||||
|
||||
Part number in schematics is "VFD GP1232A02A FUTABA". */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/vfd.h"
|
||||
#include "board/vfd-cmd.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/uart.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
#define SUPER_VERBOSE 0
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp);
|
||||
|
||||
static struct uart vfd_uart;
|
||||
static uint8_t vfd_written[4096];
|
||||
static uint8_t vfd_readable[4096];
|
||||
|
||||
static int encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
|
||||
static bool utf_enabled;
|
||||
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, unsigned int default_port_no)
|
||||
{
|
||||
if (!cfg->enable){
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
utf_enabled = cfg->utf_conversion;
|
||||
|
||||
unsigned int port_no = cfg->port_no;
|
||||
if (port_no == 0){
|
||||
port_no = default_port_no;
|
||||
}
|
||||
|
||||
dprintf("VFD: enabling (port=%d)\n", port_no);
|
||||
uart_init(&vfd_uart, port_no);
|
||||
vfd_uart.written.bytes = vfd_written;
|
||||
vfd_uart.written.nbytes = sizeof(vfd_written);
|
||||
vfd_uart.readable.bytes = vfd_readable;
|
||||
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
||||
|
||||
return iohook_push_handler(vfd_handle_irp);
|
||||
}
|
||||
|
||||
|
||||
const char* get_encoding_name(int b){
|
||||
switch (b){
|
||||
case 0: return "gb2312";
|
||||
case 1: return "big5";
|
||||
case 2: return "shift-jis";
|
||||
case 3: return "ks_c_5601-1987";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void print_vfd_text(const char* str, int len){
|
||||
|
||||
if (utf_enabled){
|
||||
|
||||
wchar_t encoded[1024];
|
||||
memset(encoded, 0, 1024 * sizeof(wchar_t));
|
||||
|
||||
int codepage = 0;
|
||||
if (encoding == VFD_ENC_GB2312){
|
||||
codepage = 936;
|
||||
} else if (encoding == VFD_ENC_BIG5){
|
||||
codepage = 950;
|
||||
} else if (encoding == VFD_ENC_SHIFT_JIS){
|
||||
codepage = 932;
|
||||
} else if (encoding == VFD_ENC_KSC5601) {
|
||||
codepage = 949;
|
||||
}
|
||||
|
||||
if (!MultiByteToWideChar(codepage, MB_USEGLYPHCHARS, str, len, encoded, 1024)){
|
||||
dprintf("VFD: Text conversion failed: %ld", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("VFD: Text: %ls\n", encoded);
|
||||
} else {
|
||||
|
||||
dprintf("VFD: Text: %s\n", str);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (!uart_match_irp(&vfd_uart, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (irp->op == IRP_OP_OPEN){
|
||||
dprintf("VFD: Open\n");
|
||||
} else if (irp->op == IRP_OP_CLOSE){
|
||||
dprintf("VFD: Close\n");
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&vfd_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD TX:\n");
|
||||
dump_iobuf(&vfd_uart.written);
|
||||
#endif
|
||||
|
||||
struct const_iobuf reader;
|
||||
iobuf_flip(&reader, &vfd_uart.written);
|
||||
|
||||
struct iobuf* writer = &vfd_uart.readable;
|
||||
for (; reader.pos < reader.nbytes ; ){
|
||||
|
||||
if (vfd_frame_sync(&reader)) {
|
||||
|
||||
reader.pos++; // get the sync byte out of the way
|
||||
|
||||
uint8_t cmd;
|
||||
iobuf_read_8(&reader, &cmd);
|
||||
|
||||
if (cmd == VFD_CMD_GET_VERSION) {
|
||||
hr = vfd_handle_get_version(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_RESET) {
|
||||
hr = vfd_handle_reset(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CLEAR_SCREEN) {
|
||||
hr = vfd_handle_clear_screen(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_BRIGHTNESS) {
|
||||
hr = vfd_handle_set_brightness(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_SCREEN_ON) {
|
||||
hr = vfd_handle_set_screen_on(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_H_SCROLL) {
|
||||
hr = vfd_handle_set_h_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DRAW_IMAGE) {
|
||||
hr = vfd_handle_draw_image(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_CURSOR) {
|
||||
hr = vfd_handle_set_cursor(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_ENCODING) {
|
||||
hr = vfd_handle_set_encoding(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_WND) {
|
||||
hr = vfd_handle_set_text_wnd(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_SPEED) {
|
||||
hr = vfd_handle_set_text_speed(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_WRITE_TEXT) {
|
||||
hr = vfd_handle_write_text(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ENABLE_SCROLL) {
|
||||
hr = vfd_handle_enable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DISABLE_SCROLL) {
|
||||
hr = vfd_handle_disable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ROTATE) {
|
||||
hr = vfd_handle_rotate(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR) {
|
||||
hr = vfd_handle_create_char(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR2) {
|
||||
hr = vfd_handle_create_char2(&reader, writer, &vfd_uart);
|
||||
} else {
|
||||
dprintf("VFD: Unknown command 0x%x\n", cmd);
|
||||
dump_const_iobuf(&reader);
|
||||
hr = S_FALSE;
|
||||
}
|
||||
} else {
|
||||
|
||||
// if no sync byte is sent, we are just getting plain text...
|
||||
|
||||
if (reader.pos < reader.nbytes){
|
||||
int len = 0;
|
||||
|
||||
// read chars until we hit a new sync byte or the data ends
|
||||
while (reader.pos + len + 1 < reader.nbytes && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE2){
|
||||
len++;
|
||||
}
|
||||
|
||||
char* str = malloc(len);
|
||||
memset(str, 0, len);
|
||||
iobuf_read(&reader, str, len);
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
reader.pos += len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(hr)){
|
||||
return hr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vfd_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Get Version\n");
|
||||
|
||||
struct vfd_resp_board_info resp;
|
||||
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.unk1 = 2;
|
||||
strcpy(resp.version, "01.20");
|
||||
resp.unk2 = 1;
|
||||
|
||||
return vfd_frame_encode(writer, &resp, sizeof(resp));
|
||||
}
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Reset\n");
|
||||
|
||||
encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Clear Screen\n");
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 4){
|
||||
dprintf("VFD: Brightness, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Brightness, %d\n", b);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 1){
|
||||
dprintf("VFD: Screen Power, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Screen Power, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t x;
|
||||
iobuf_read_8(reader, &x);
|
||||
|
||||
dprintf("VFD: Horizontal Scroll, X=%d\n", x);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
int w, h;
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
uint8_t image[2048];
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
w = x1 - x0;
|
||||
h = y1 - y0;
|
||||
iobuf_read(reader, image, w*h);
|
||||
|
||||
dprintf("VFD: Draw image, %dx%d\n", w, h);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
|
||||
iobuf_read_be16(reader, &x);
|
||||
iobuf_read_8(reader, &y);
|
||||
|
||||
dprintf("VFD: Set Cursor, x=%d,y=%d\n", x, y);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Encoding, %d (%s)\n", b, get_encoding_name(b));
|
||||
|
||||
if (b < 0 || b > VFD_ENC_MAX){
|
||||
dprintf("Invalid encoding specified\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
encoding = b;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
|
||||
dprintf("VFD: Set Text Window, p0:%d,%d, p1:%d,%d\n", x0, y0, x1, y1);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Text Speed, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t len;
|
||||
iobuf_read_8(reader, &len);
|
||||
|
||||
char* str = malloc(len);
|
||||
iobuf_read(reader, str, len);
|
||||
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Enable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Disable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Rotate, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
char buf[32];
|
||||
|
||||
iobuf_read(reader, buf, 32);
|
||||
|
||||
dprintf("VFD: Create character, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b, b2;
|
||||
iobuf_read_8(reader, &b);
|
||||
iobuf_read_8(reader, &b2);
|
||||
char buf[16];
|
||||
|
||||
iobuf_read(reader, buf, 16);
|
||||
|
||||
dprintf("VFD: Create character, %d, %d\n", b, b2);
|
||||
return S_FALSE;
|
||||
}
|
13
common/board/vfd.h
Normal file
13
common/board/vfd.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct vfd_config {
|
||||
bool enable;
|
||||
unsigned int port_no;
|
||||
bool utf_conversion;
|
||||
};
|
||||
|
||||
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, unsigned int default_port_no);
|
||||
|
@ -14,4 +14,5 @@ void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename)
|
||||
cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename);
|
||||
cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename);
|
||||
cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename);
|
||||
cfg->dpiAware = GetPrivateProfileIntW(L"gfx", L"dpiAware", 1, filename);
|
||||
}
|
@ -224,9 +224,19 @@ static HRESULT STDMETHODCALLTYPE my_IDirect3D9_CreateDevice(
|
||||
gfx_util_frame_window(hwnd);
|
||||
}
|
||||
|
||||
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor);
|
||||
UINT max_adapter = IDirect3D9_GetAdapterCount(real);
|
||||
adapter = gfx_config.monitor;
|
||||
if (adapter >= max_adapter) {
|
||||
dprintf(
|
||||
"Gfx: Requested adapter %d but maximum is %d. Using primary monitor\n",
|
||||
gfx_config.monitor, max_adapter - 1
|
||||
);
|
||||
adapter = D3DADAPTER_DEFAULT;
|
||||
} else {
|
||||
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor);
|
||||
}
|
||||
|
||||
return IDirect3D9_CreateDevice(real, gfx_config.monitor, type, hwnd, flags, pp, pdev);
|
||||
return IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice(
|
147
common/gfxhook/gfx.c
Normal file
147
common/gfxhook/gfx.c
Normal file
@ -0,0 +1,147 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* Hook functions */
|
||||
|
||||
static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow);
|
||||
static HWND WINAPI hook_CreateWindowExA(
|
||||
DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int X,
|
||||
int Y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam
|
||||
);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static BOOL (WINAPI *next_ShowWindow)(HWND hWnd, int nCmdShow);
|
||||
static HWND (WINAPI *next_CreateWindowExA)(
|
||||
DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int X,
|
||||
int Y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam
|
||||
);
|
||||
|
||||
static struct gfx_config gfx_config;
|
||||
|
||||
static const struct hook_symbol gfx_hooks[] = {
|
||||
{
|
||||
.name = "ShowWindow",
|
||||
.patch = hook_ShowWindow,
|
||||
.link = (void **) &next_ShowWindow,
|
||||
}, {
|
||||
.name = "CreateWindowExA",
|
||||
.patch = hook_CreateWindowExA,
|
||||
.link = (void **) &next_CreateWindowExA,
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_hook_init(const struct gfx_config *cfg)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg->dpiAware) {
|
||||
if (SetProcessDPIAware()) {
|
||||
dprintf("Gfx: Game process set to DPI aware.\n");
|
||||
} else {
|
||||
dprintf("Gfx: Failed to set process DPI aware\n");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "user32.dll", gfx_hooks, _countof(gfx_hooks));
|
||||
}
|
||||
|
||||
static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow)
|
||||
{
|
||||
dprintf("Gfx: ShowWindow hook hit\n");
|
||||
|
||||
if (!gfx_config.framed && nCmdShow == SW_RESTORE) {
|
||||
nCmdShow = SW_SHOW;
|
||||
}
|
||||
|
||||
return next_ShowWindow(hWnd, nCmdShow);
|
||||
}
|
||||
|
||||
static HWND WINAPI hook_CreateWindowExA(
|
||||
DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int X,
|
||||
int Y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam
|
||||
)
|
||||
{
|
||||
RECT rect;
|
||||
|
||||
dprintf("Gfx: CreateWindowExA hook hit\n");
|
||||
|
||||
if (gfx_config.windowed)
|
||||
{
|
||||
if (gfx_config.framed)
|
||||
dwStyle |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
|
||||
else
|
||||
dwStyle = WS_POPUP;
|
||||
|
||||
rect.left = ((X == CW_USEDEFAULT) ? 0 : X);
|
||||
rect.top = ((Y == CW_USEDEFAULT) ? 0 : Y);
|
||||
rect.right = rect.left + nWidth;
|
||||
rect.bottom = rect.top + nHeight;
|
||||
|
||||
// Don't care if it's ok or not, since we are creating window and we can't just return a NULL
|
||||
AdjustWindowRect(&rect, dwStyle, !!hMenu);
|
||||
|
||||
X = ((X == CW_USEDEFAULT) ? X : rect.left);
|
||||
Y = ((Y == CW_USEDEFAULT) ? Y : rect.top);
|
||||
nWidth = rect.right - rect.left;
|
||||
nHeight = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
return next_CreateWindowExA(
|
||||
dwExStyle,
|
||||
lpClassName,
|
||||
lpWindowName,
|
||||
dwStyle,
|
||||
X,
|
||||
Y,
|
||||
nWidth,
|
||||
nHeight,
|
||||
hWndParent,
|
||||
hMenu,
|
||||
hInstance,
|
||||
lpParam
|
||||
);
|
||||
}
|
@ -7,6 +7,7 @@ struct gfx_config {
|
||||
bool windowed;
|
||||
bool framed;
|
||||
int monitor;
|
||||
bool dpiAware;
|
||||
};
|
||||
|
||||
void gfx_hook_init(const struct gfx_config *cfg);
|
77
common/gfxhook/gl.c
Normal file
77
common/gfxhook/gl.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
#include "gfxhook/gl.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/dll.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* Hook functions */
|
||||
|
||||
static void WINAPI hook_glutFullScreen(void);
|
||||
static void WINAPI hook_glutInitDisplayMode(unsigned int mode);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static void (WINAPI *next_glutFullScreen)(void);
|
||||
static void (WINAPI *next_glutInitDisplayMode)(unsigned int mode);
|
||||
|
||||
static struct gfx_config gfx_config;
|
||||
|
||||
static const struct hook_symbol glut_hooks[] = {
|
||||
{
|
||||
.name = "glutFullScreen",
|
||||
.patch = hook_glutFullScreen,
|
||||
.link = (void **) &next_glutFullScreen,
|
||||
}, {
|
||||
.name = "glutInitDisplayMode",
|
||||
.patch = hook_glutInitDisplayMode,
|
||||
.link = (void **) &next_glutInitDisplayMode,
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_gl_hook_init(const struct gfx_config *cfg, HINSTANCE self)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "glut32.dll", glut_hooks, _countof(glut_hooks));
|
||||
|
||||
if (self != NULL) {
|
||||
dll_hook_push(self, L"glut32.dll");
|
||||
}
|
||||
}
|
||||
|
||||
static void WINAPI hook_glutFullScreen(void)
|
||||
{
|
||||
dprintf("Gfx: glutFullScreen hook hit\n");
|
||||
|
||||
if (gfx_config.windowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
next_glutFullScreen();
|
||||
}
|
||||
|
||||
static void WINAPI hook_glutInitDisplayMode(unsigned int mode)
|
||||
{
|
||||
dprintf("Gfx: glutInitDisplayMode hook hit\n");
|
||||
|
||||
// GLUT adds a frame when going windowed
|
||||
if (gfx_config.windowed && !gfx_config.framed) {
|
||||
// GLUT_BORDERLESS
|
||||
mode |= 0x0800;
|
||||
}
|
||||
|
||||
next_glutInitDisplayMode(mode);
|
||||
}
|
3
common/gfxhook/gl.h
Normal file
3
common/gfxhook/gl.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void gfx_gl_hook_init(const struct gfx_config *cfg, HINSTANCE self);
|
@ -2,7 +2,6 @@ gfxhook_lib = static_library(
|
||||
'gfxhook',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
dxguid_lib,
|
||||
@ -22,6 +21,8 @@ gfxhook_lib = static_library(
|
||||
'dxgi.h',
|
||||
'gfx.c',
|
||||
'gfx.h',
|
||||
'gl.c',
|
||||
'gl.h',
|
||||
'util.c',
|
||||
'util.h',
|
||||
],
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "hooklib/config.h"
|
||||
@ -80,4 +81,6 @@ void printer_config_load(struct printer_config *cfg, const wchar_t *filename)
|
||||
cfg->printer_out_path,
|
||||
_countof(cfg->printer_out_path),
|
||||
filename);
|
||||
|
||||
cfg->wait_time = GetPrivateProfileIntW(L"printer", L"waitTime", 0, filename);
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/table.h"
|
@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
@ -13,6 +14,7 @@ static HCURSOR (*next_SetCursor)(HCURSOR hCursor);
|
||||
static BOOL my_SetCursorPos(int x, int y);
|
||||
static BOOL my_SetPhysicalCursorPos(int x, int y);
|
||||
static int my_ShowCursor(BOOL bShow);
|
||||
static int cursor_track = -1; // If no mouse is connected, this starts as -1
|
||||
|
||||
static const struct hook_symbol cursor_syms[] = {
|
||||
{
|
||||
@ -44,7 +46,7 @@ void cursor_hook_init()
|
||||
|
||||
static BOOL my_SetCursorPos(int x, int y)
|
||||
{
|
||||
dprintf("my_SetCursorPos Hit! x %d y %d\n", x, y);
|
||||
// dprintf("my_SetCursorPos Hit! x %d y %d\n", x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -57,7 +59,12 @@ static BOOL my_SetPhysicalCursorPos(int x, int y)
|
||||
static int my_ShowCursor(BOOL bShow)
|
||||
{
|
||||
dprintf("my_ShowCursor Hit!\n");
|
||||
return 0;
|
||||
if (bShow) {
|
||||
cursor_track++;
|
||||
} else {
|
||||
cursor_track--;
|
||||
}
|
||||
return cursor_track;
|
||||
}
|
||||
|
||||
static HCURSOR my_SetCursor(HCURSOR hCursor)
|
@ -3,15 +3,20 @@
|
||||
#include <windows.h>
|
||||
#include <windns.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <winhttp.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/hr.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/get_function_ordinal.h"
|
||||
|
||||
#include "hooklib/dns.h"
|
||||
|
||||
/* Latest w32headers does not include DnsQueryEx, so we'll have to "polyfill"
|
||||
@ -66,6 +71,29 @@ static int WSAAPI hook_getaddrinfo(
|
||||
const ADDRINFOA *pHints,
|
||||
ADDRINFOA **ppResult);
|
||||
|
||||
static HINTERNET WINAPI hook_WinHttpConnect(
|
||||
HINTERNET hSession,
|
||||
const wchar_t *pwszServerName,
|
||||
INTERNET_PORT nServerPort,
|
||||
DWORD dwReserved);
|
||||
|
||||
static bool WINAPI hook_WinHttpCrackUrl(
|
||||
const wchar_t *pwszUrl,
|
||||
DWORD dwUrlLength,
|
||||
DWORD dwFlags,
|
||||
LPURL_COMPONENTS lpUrlComponents);
|
||||
|
||||
static DWORD WINAPI hook_send(
|
||||
SOCKET s,
|
||||
const char* buf,
|
||||
int len,
|
||||
int flags);
|
||||
|
||||
static int WINAPI hook_connect(
|
||||
SOCKET s,
|
||||
const struct sockaddr *name,
|
||||
int namelen);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static DNS_STATUS (WINAPI *next_DnsQuery_A)(
|
||||
@ -95,6 +123,29 @@ static int (WSAAPI *next_getaddrinfo)(
|
||||
const ADDRINFOA *pHints,
|
||||
ADDRINFOA **ppResult);
|
||||
|
||||
static HINTERNET (WINAPI *next_WinHttpConnect)(
|
||||
HINTERNET hSession,
|
||||
const wchar_t *pwszServerName,
|
||||
INTERNET_PORT nServerPort,
|
||||
DWORD dwReserved);
|
||||
|
||||
static bool (WINAPI *next_WinHttpCrackUrl)(
|
||||
const wchar_t *pwszUrl,
|
||||
DWORD dwUrlLength,
|
||||
DWORD dwFlags,
|
||||
LPURL_COMPONENTS lpUrlComponents);
|
||||
|
||||
static DWORD (WINAPI *next_send)(
|
||||
SOCKET s,
|
||||
const char* buf,
|
||||
int len,
|
||||
int flags);
|
||||
|
||||
static int (__stdcall *next_connect)(
|
||||
SOCKET s,
|
||||
const struct sockaddr *name,
|
||||
int namelen);
|
||||
|
||||
static const struct hook_symbol dns_hook_syms_dnsapi[] = {
|
||||
{
|
||||
.name = "DnsQuery_A",
|
||||
@ -117,13 +168,45 @@ static const struct hook_symbol dns_hook_syms_ws2[] = {
|
||||
.ordinal = 176,
|
||||
.patch = hook_getaddrinfo,
|
||||
.link = (void **) &next_getaddrinfo,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hook_symbol dns_hook_syms_winhttp[] = {
|
||||
{
|
||||
.name = "WinHttpConnect",
|
||||
.patch = hook_WinHttpConnect,
|
||||
.link = (void **) &next_WinHttpConnect,
|
||||
}, {
|
||||
.name = "WinHttpCrackUrl",
|
||||
.patch = hook_WinHttpCrackUrl,
|
||||
.link = (void **) &next_WinHttpCrackUrl,
|
||||
}
|
||||
};
|
||||
|
||||
static struct hook_symbol http_hook_syms_ws2[] = {
|
||||
{
|
||||
.name = "send",
|
||||
.patch = hook_send,
|
||||
.link = (void **) &next_send
|
||||
},
|
||||
};
|
||||
|
||||
static struct hook_symbol port_hook_syms_ws2[] = {
|
||||
{
|
||||
.name = "connect",
|
||||
.patch = hook_connect,
|
||||
.link = (void **) &next_connect
|
||||
},
|
||||
};
|
||||
|
||||
static bool dns_hook_initted;
|
||||
static CRITICAL_SECTION dns_hook_lock;
|
||||
static struct dns_hook_entry *dns_hook_entries;
|
||||
static size_t dns_hook_nentries;
|
||||
static char received_title_url[255];
|
||||
static unsigned short startup_port;
|
||||
static unsigned short billing_port;
|
||||
static unsigned short aimedb_port;
|
||||
|
||||
static void dns_hook_init(void)
|
||||
{
|
||||
@ -134,17 +217,85 @@ static void dns_hook_init(void)
|
||||
dns_hook_initted = true;
|
||||
InitializeCriticalSection(&dns_hook_lock);
|
||||
|
||||
dns_hook_apply_hooks(NULL);
|
||||
}
|
||||
|
||||
void dns_hook_apply_hooks(HMODULE mod){
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
mod,
|
||||
"dnsapi.dll",
|
||||
dns_hook_syms_dnsapi,
|
||||
_countof(dns_hook_syms_dnsapi));
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
mod,
|
||||
"ws2_32.dll",
|
||||
dns_hook_syms_ws2,
|
||||
_countof(dns_hook_syms_ws2));
|
||||
|
||||
hook_table_apply(
|
||||
mod,
|
||||
"winhttp.dll",
|
||||
dns_hook_syms_winhttp,
|
||||
_countof(dns_hook_syms_winhttp));
|
||||
}
|
||||
|
||||
void http_hook_init(){
|
||||
for (size_t i = 0; i < _countof(http_hook_syms_ws2); ++i) {
|
||||
http_hook_syms_ws2[i].ordinal = get_function_ordinal("ws2_32.dll", http_hook_syms_ws2[i].name);
|
||||
}
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"ws2_32.dll",
|
||||
http_hook_syms_ws2,
|
||||
_countof(http_hook_syms_ws2));
|
||||
}
|
||||
|
||||
void port_hook_init(unsigned short _startup_port, unsigned short _billing_port, unsigned short _aimedb_port){
|
||||
startup_port = _startup_port;
|
||||
billing_port = _billing_port;
|
||||
aimedb_port = _aimedb_port;
|
||||
for (size_t i = 0; i < _countof(port_hook_syms_ws2); ++i) {
|
||||
port_hook_syms_ws2[i].ordinal = get_function_ordinal("ws2_32.dll", port_hook_syms_ws2[i].name);
|
||||
}
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"ws2_32.dll",
|
||||
port_hook_syms_ws2,
|
||||
_countof(port_hook_syms_ws2));
|
||||
}
|
||||
|
||||
// This function match domain and subdomains like *.naominet.jp.
|
||||
bool match_domain(const wchar_t* target, const wchar_t* pattern) {
|
||||
if (_wcsicmp(pattern, target) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int pattern_ptr_index = 0;
|
||||
int target_ptr_index = 0;
|
||||
|
||||
while (pattern[pattern_ptr_index] != '\0' && target[target_ptr_index] != '\0') {
|
||||
if (pattern[pattern_ptr_index] == '*') {
|
||||
pattern_ptr_index++; // Check next character for wildcard match.
|
||||
|
||||
while (pattern[pattern_ptr_index] != target[target_ptr_index]) {
|
||||
target_ptr_index++;
|
||||
|
||||
if (target[target_ptr_index] == '\0') return false;
|
||||
}
|
||||
}
|
||||
else if (pattern[pattern_ptr_index] != target[target_ptr_index]) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
pattern_ptr_index++;
|
||||
target_ptr_index++;
|
||||
}
|
||||
}
|
||||
|
||||
return pattern[pattern_ptr_index] == '\0' && target[target_ptr_index] == '\0';
|
||||
}
|
||||
|
||||
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src)
|
||||
@ -171,7 +322,7 @@ HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if(to_src != NULL) {
|
||||
if (to_src != NULL) {
|
||||
to = _wcsdup(to_src);
|
||||
|
||||
if (to == NULL) {
|
||||
@ -250,8 +401,8 @@ static DNS_STATUS WINAPI hook_DnsQuery_A(
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (_wcsicmp(wstr, pos->from) == 0) {
|
||||
if(pos->to == NULL) {
|
||||
if (match_domain(wstr, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
hr = HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||
|
||||
@ -314,8 +465,8 @@ static DNS_STATUS WINAPI hook_DnsQuery_W(
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (_wcsicmp(pszName, pos->from) == 0) {
|
||||
if(pos->to == NULL) {
|
||||
if (match_domain(pszName, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||
}
|
||||
@ -358,8 +509,8 @@ static DNS_STATUS WINAPI hook_DnsQueryEx(
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (_wcsicmp(pRequest->QueryName, pos->from) == 0) {
|
||||
if(pos->to == NULL) {
|
||||
if (match_domain(pRequest->QueryName, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||
}
|
||||
@ -425,8 +576,8 @@ static int WSAAPI hook_getaddrinfo(
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (_wcsicmp(wstr, pos->from) == 0) {
|
||||
if(pos->to == NULL) {
|
||||
if (match_domain(wstr, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
result = EAI_NONAME;
|
||||
|
||||
@ -460,3 +611,173 @@ end:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HINTERNET WINAPI hook_WinHttpConnect(
|
||||
HINTERNET hSession,
|
||||
const wchar_t *pwszServerName,
|
||||
INTERNET_PORT nServerPort,
|
||||
DWORD dwReserved)
|
||||
{
|
||||
const struct dns_hook_entry *pos;
|
||||
size_t i;
|
||||
|
||||
if (pwszServerName == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(pwszServerName, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pwszServerName = pos->to;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
|
||||
return next_WinHttpConnect(hSession, pwszServerName, nServerPort, dwReserved);
|
||||
}
|
||||
|
||||
// Hook to replace CXB title url
|
||||
static bool WINAPI hook_WinHttpCrackUrl(
|
||||
const wchar_t *pwszUrl,
|
||||
DWORD dwUrlLength,
|
||||
DWORD dwFlags,
|
||||
LPURL_COMPONENTS lpUrlComponents)
|
||||
{
|
||||
const struct dns_hook_entry *pos;
|
||||
size_t i;
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(pwszUrl, pos->from)) {
|
||||
wchar_t* toAddr = pos->to;
|
||||
wchar_t titleBuffer[255];
|
||||
|
||||
if (wcscmp(toAddr, L"title") == 0) {
|
||||
size_t wstr_c;
|
||||
mbstowcs_s(&wstr_c, titleBuffer, 255, received_title_url, strlen(received_title_url));
|
||||
toAddr = titleBuffer;
|
||||
}
|
||||
|
||||
bool result = next_WinHttpCrackUrl(
|
||||
toAddr,
|
||||
wcslen(toAddr),
|
||||
dwFlags,
|
||||
lpUrlComponents
|
||||
);
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return next_WinHttpCrackUrl(
|
||||
pwszUrl,
|
||||
dwUrlLength,
|
||||
dwFlags,
|
||||
lpUrlComponents
|
||||
);
|
||||
}
|
||||
|
||||
int WINAPI hook_connect(SOCKET s, const struct sockaddr *name, int namelen) {
|
||||
const struct sockaddr_in *n;
|
||||
struct sockaddr_in new_name;
|
||||
unsigned ip;
|
||||
unsigned short port, new_port;
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
n = (const struct sockaddr_in *)name;
|
||||
ip = n->sin_addr.S_un.S_addr;
|
||||
if (WSANtohs(s, n->sin_port, &port)) return SOCKET_ERROR;
|
||||
|
||||
if (port == 80 && startup_port) {
|
||||
new_port = startup_port;
|
||||
} else if (port == 8443 && billing_port) {
|
||||
new_port = billing_port;
|
||||
} else if (port == 22345 && aimedb_port) {
|
||||
new_port = aimedb_port;
|
||||
} else { // No match
|
||||
dprintf("TCP Connect: %u.%u.%u.%u:%hu\n", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff, port);
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return next_connect(
|
||||
s,
|
||||
name,
|
||||
namelen
|
||||
);
|
||||
}
|
||||
|
||||
// matched
|
||||
new_name = *n;
|
||||
if (WSAHtons(s, new_port, &new_name.sin_port)) return SOCKET_ERROR;
|
||||
|
||||
dprintf("TCP Connect: %u.%u.%u.%u:%hu, mapped to port %hu\n", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff, port, new_port);
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return next_connect(
|
||||
s,
|
||||
(const struct sockaddr *)&new_name,
|
||||
sizeof(new_name)
|
||||
);
|
||||
}
|
||||
|
||||
DWORD WINAPI hook_send(SOCKET s, const char* buf, int len, int flags) {
|
||||
if (strstr(buf, "HTTP/") != NULL) {
|
||||
char *new_buf = malloc(len + 1);
|
||||
if (new_buf == NULL) return SOCKET_ERROR;
|
||||
|
||||
memcpy(new_buf, buf, len);
|
||||
new_buf[len] = '\0';
|
||||
|
||||
char *host_start = strstr(new_buf, "Host: ");
|
||||
if (host_start != NULL) {
|
||||
char *host_end = strstr(host_start, "\r\n");
|
||||
if (host_end != NULL) {
|
||||
host_end += 2;
|
||||
int host_len = host_end - host_start;
|
||||
|
||||
char *host_value_start = host_start + 6;
|
||||
char *host_value_end = strstr(host_value_start, "\r\n");
|
||||
if (host_value_end != NULL) {
|
||||
int value_len = host_value_end - host_value_start;
|
||||
char* host_value = (char*)malloc(value_len + 1);
|
||||
strncpy(host_value, host_value_start, value_len);
|
||||
host_value[value_len] = '\0';
|
||||
|
||||
for (struct dns_hook_entry *entry = dns_hook_entries; entry && entry->from; entry++) {
|
||||
char from_value[256];
|
||||
wcstombs(from_value, entry->from, sizeof(from_value));
|
||||
|
||||
if (strcmp(host_value, from_value) == 0) {
|
||||
char to_value[256];
|
||||
wcstombs(to_value, entry->to, sizeof(to_value));
|
||||
snprintf(host_start, len - (host_start - new_buf), "Host: %s\r\n", to_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(host_value);
|
||||
}
|
||||
len = (int)strlen(new_buf);
|
||||
}
|
||||
}
|
||||
DWORD result = next_send(s, new_buf, len, flags);
|
||||
free(new_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
return next_send(s, buf, len, flags);
|
||||
}
|
@ -3,7 +3,9 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void http_hook_init();
|
||||
void port_hook_init(unsigned short _startup_port, unsigned short _billing_port, unsigned short _aimedb_port);
|
||||
// if to_src is NULL, all lookups for from_src will fail
|
||||
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src);
|
||||
|
||||
void dns_hook_apply_hooks(HMODULE mod);
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user