Compare commits

...

150 Commits

Author SHA1 Message Date
d9075bbfce Merge branch 'feature/openssl' into develop 2025-10-12 12:49:55 +02:00
6fe8744b0f common: add OpenSSL patch comments, bump capnhook 2025-10-12 12:42:06 +02:00
19cd7cb8df common: add OpenSSL Intel SHA ext hook 2025-10-05 11:01:13 +02:00
19c1a8e469 Merge pull request 'idac: Add configurable remapping for D-Pad Up/Down buttons.' (#77) from puniru/segatools:up_down into develop
Reviewed-on: TeamTofuShop/segatools#77
Reviewed-by: Dniel97 <dniel97@noreply.gitea.tendokyu.moe>
2025-08-27 20:39:40 +00:00
eb96660fdf idac: update config for up and down dpad buttons 2025-08-27 13:30:00 +09:00
83a9a49429 idac: allow changing up and down for custom boards 2025-08-22 15:03:19 +09:00
d423058cbd APM3: Fix amdaemon breakage when Unity Doorstop is present (#76)
Reviewed-on: TeamTofuShop/segatools#76
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2025-08-10 16:04:01 +00:00
55a3859891 [swdc] fix magnitude calculation 2025-08-06 19:40:04 +02:00
3f2cfb6a26 [apm3, idac] improved launch.bat, removed zinput 2025-08-01 17:58:21 +02:00
e74e2a0d47 [apm3, swdc, idac] DInput8 cleanup 2025-07-29 18:06:10 +02:00
122034f922 [fgo] improve keyboard bindings 2025-07-27 22:05:07 +02:00
bb0b023ec0 [apm3] hook video loading, bug fixes 2025-07-27 19:22:57 +02:00
bb01e131e9 [epay]: hook enabled by default 2025-07-27 18:06:58 +02:00
ded1375e88 Vfs: Hook .ini reader functions to fix DLI reading (#75)
Pretty simple, DLI reading (more commonly known as DownloadOrder) calls GetPrivateProfile* with a file path with E:\tmpDli*.ini. This fails right now.

I have only hooked the functions that appear in the latest amdaemon.

Reviewed-on: TeamTofuShop/segatools#75
Reviewed-by: Dniel97 <dniel97@noreply.gitea.tendokyu.moe>
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2025-07-24 09:16:22 +00:00
0006731536 [apm3] fix led hook, fix blocked reboot 2025-07-21 21:09:24 +02:00
ae168cdaf9 Merge branch 'feature/apm3' into develop 2025-07-20 18:49:15 +02:00
e974a76fe6 [apm3] add DInput and XInput support 2025-07-20 18:13:29 +02:00
e2e4b37e3f APMv3: add hook (#73)
This adds support for APMv3 I/O, menus and the launcher.

* Added a apm3hook dll and I/O based on the usual layout.
* Added C:\Mount\Apm to vfs.
* Added the relevant .dlls to unityhook.
* Added a hook for apmmount.dll that uses `CreateDosDevice` to mount decrypted data to the locations the launcher and games expect files to be. This will conflict with anything that is already at W:\ and X:\, but I do not have better solutions for this.
* `launch.bat` is a bit more involved as it simulates the launcher loop. It can be broken by alt+f4ing or closing the launcher with "X".
* An extra export was added, so rundll32 can be used to get rid of the dosdevices after the launcher was killed.
* Since all the games do everything via `X:\lib\apm.dll`, no game hooks were needed in testing, therefore, `game.bat` files can be used as is.
* Path hooks are applied correctly, so you can go correctly between games, launcher, sub system test mode and game test modes.

A setup guide (some stuff specific to my server) can be found here:
https://gmg.hopto.org:82/gmg/wiki/index.php/All.Net_P-ras_Multi_Menu

Tested with the 2 APM sample apps, Blazblue, Puyo, Guilty Gear and some weird unity puzzle game whose name I forgot.

![Apmv3System_yLRityJVpm.png](/attachments/3d645e71-81e6-42e6-acd4-63c537cda59e)
![puyoe_hJNhnJGFnd.png](/attachments/01664049-71fe-4c38-9c99-39649ab21e56)

Reviewed-on: TeamTofuShop/segatools#73
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2025-07-20 09:43:56 +00:00
f595af9686 [cm, mercury, mu3] update aime reader gen 2025-07-19 23:35:46 +02:00
03513e7b0c add aime scan button to default segatools.ini 2025-07-16 15:44:05 +02:00
cdb6815c5a add keychip id and pcbid to default segatools.ini 2025-06-19 19:28:10 +02:00
4644e36ccc Merge branch 'feature/cleanup' into develop 2025-06-19 18:18:30 +02:00
dbfc62b5d4 platform: fix dipsw settings not applying 2025-06-19 18:17:20 +02:00
24e8bc87a3 remove reference to nonexistant files 2025-04-17 14:06:55 -04:00
a65b43fe1a Merge branch 'feature/cleanup' of https://gitea.tendokyu.moe/teamtofushop/segatools into feature/cleanup 2025-04-17 14:05:45 -04:00
66a53dd2de refactor all common parts and games 2025-04-17 20:04:17 +02:00
ae3dd666f4 refactor all common parts and games 2025-04-17 19:40:40 +02:00
a6126bf290 Merge branch 'feature/thinca_auth' into develop 2025-04-17 19:18:03 +02:00
015097972a emoney: improce doc and add python script 2025-04-17 19:17:42 +02:00
67eda7458b emoney: Add Thinca authentication card stuff (#35)
This PR adds everything that's needed on the segatools side to add E-Money support regarding Thinca authentication cards.

I've also included set-up documentation (with a network side bonus which was as far as I could figure out so far, but I'm pretty certain no more changes to segatools will be needed)

Due to the nature of a custom protcol called TCAP that Thinca uses for networking (see docs), I can't fully test that everything works as I haven't yet bothered to figure that protocol out.

Tested with both APMv3 and FGO.

![https://puu.sh/KeqVj/ccf4bcccbb.png](https://puu.sh/KeqVj/ccf4bcccbb.png)

Reviewed-on: TeamTofuShop/segatools#35
Co-authored-by: Haruka <haruka@noreply.gitea.tendokyu.moe>
Co-committed-by: Haruka <haruka@noreply.gitea.tendokyu.moe>
2025-04-17 17:01:38 +00:00
b37e1105d0 misc: added a showcursor counter to more accuratly replicate actual function behavior 2025-04-16 22:10:19 -04:00
9a6c4939c2 system: fix sysfile patches not applying when compiling with msvc 2025-04-16 22:09:48 -04:00
39711a994a FGO: add keyboard input (#61)
Probably self-explanatory :p

Reviewed-on: TeamTofuShop/segatools#61
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2025-04-05 15:22:14 +00:00
61f95c3f2e GFX: add dpi-awareness switch for all games (#64)
Pretty simple, adds a new config setting to the gfx category, which defaults to enabled to disable DPI scaling if a scale higher than 100% is used, causing game windows to appear stretched and blurry.

Reviewed-on: TeamTofuShop/segatools#64
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2025-03-14 11:53:31 +00:00
70c3e2fe0f FGO: fix printer hook always being enabled (#60)
that moment when the printer says OK but it absolutely isn't supposed to be OK lmao

edit: I'm not sure why the msvc commits are also in all of these PRs, they have no effect though...
Reviewed-on: TeamTofuShop/segatools#60
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2025-03-09 16:05:03 +00:00
369fe28687 disable excess logging for carol touchscreen hook 2025-03-02 04:10:35 -05:00
3371f3f437 fix msvc build by removing ntstatus include from platform/system.c 2025-03-02 01:45:23 -05:00
a57542c2d2 Merge branch 'feature/code-cleanup' into develop 2025-03-02 00:36:27 +01:00
27116a7a41 idac, tokyo: improve dipsw cabinet id config 2025-03-02 00:36:13 +01:00
e850346b79 renamed start.bat to launch.bat 2025-03-02 00:25:15 +01:00
4d0ef54279 system: add dip switch label configurations 2025-03-02 00:23:53 +01:00
b8af67377c Merge branch 'feature/mai2-touch-led' into develop 2025-03-02 00:04:00 +01:00
4cb76dd1ee mai2: update all LED boards to use two boards 2025-03-02 00:01:45 +01:00
efe01d92a6 Fix MSVC build again, add support for standalone MSVC compiler (#59)
After switching away from VS, I realized the buildscript wouldn't detect the standalone MSVC compiler, because for whatever genius reason, MS installs that in the x86 program files directory...

Also fixes some duplicate definitions and a missing library that MSVC doesn't like
ah compilers...

Reviewed-on: TeamTofuShop/segatools#59
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2025-02-24 18:49:22 +00:00
004a2f6dcd docs: fix playformID and billingType config docs 2025-02-21 19:37:54 -05:00
a1611afffc Mai2: Add touch and led15070 hook (#55)
In this PR, I have added the `mai2` touch and `led15070` hooks to provide an example for handling custom peripherals. This change allows users to implement the touch and `led15070` logic by writing appropriate `mai2io` scripts.

#### **Touch Hook**:
- The touch hook simulates touch points based on keyboard combinations. For example, to trigger the A1 touch point, the user must press the A and 1 keys on the keyboard. Input for the 1p requires Caps Lock to be off, while 2p requires Caps Lock to be on.
- The hook allows for independent control of whether device simulation is enabled for "1p" and "2p" and whether keyboard input mapping is enabled.
- **Note**: The current touch hook is not yet functional as it requires modifications to the `capnhook` for proper completion of the `sinmai` hook.

#### **LED15070 Hook**:
- This hook implements basic device simulation. Peripherals requiring lighting data should complete the logic as needed.
- **Note**: The LED data refresh can flood the console logs, so I’ve added a `DEBUG` flag to control whether the debug logging is enabled or not.

#### **Other Changes**:
- In certain versions of `sinmai`, key inputs for 1p and 2p can be directly read from the keyboard without requiring simulation via the `amdaemon io4` hook. I’ve added a switch to control this behavior to prevent redundant input.
- **Benefit**: This ensures that key input is only read when `sinmai` is in the foreground.

If you'd like to learn more about the touch and `led15070` features, my research findings are available here:
[Mai2Touch](https://github.com/Sucareto/Mai2Touch)

Co-authored-by: Sucareto <28331534+Sucareto@users.noreply.github.com>
Reviewed-on: TeamTofuShop/segatools#55
Co-authored-by: Mahuyo <mahuyo@noreply.gitea.tendokyu.moe>
Co-committed-by: Mahuyo <mahuyo@noreply.gitea.tendokyu.moe>
2025-02-16 12:49:58 +00:00
1d63ab24d3 Move capnhook to TeamTofuShop fork, update revision 2025-02-09 04:52:27 -05:00
2f54183636 bump capnhook rev 2025-02-04 11:09:23 -05:00
402bf0f247 nusec: add full IOCTL list without handlers 2025-01-28 01:41:03 -05:00
4c20deb60a bump capnhook ver 2025-01-27 02:09:14 -05:00
96ee1afc2f Merge pull request 'Revert: Add automatically apply OpenSSL patch for Intel Gen 10+ CPUs' (#54) from Bottersnike/segatools:develop into develop
Reviewed-on: TeamTofuShop/segatools#54
2024-12-27 14:18:27 +00:00
0c28765bdd Revert: Add automatically apply OpenSSL patch for Intel Gen 10+ CPUs 2024-12-27 14:12:58 +00:00
96bf8cab81 aime: add portNo to config 2024-12-23 21:49:24 +01:00
a3120181be replace hardcoded enums with #define CTL_CODEs 2024-12-23 21:48:30 +01:00
80d4902cfc remove 5gb wasted space by removing precompiled headers 2024-12-23 21:04:51 +01:00
b4f5cdbe59 Merge pull request 'Add automatically apply OpenSSL patch for Intel Gen 10+ CPUs' (#43) from kagaminehaku/segatools:develop into develop
Reviewed-on: Dniel97/segatools#43
2024-12-23 19:43:47 +00:00
25431a9db1 Add "openssl" config key doc 2024-12-24 02:34:44 +07:00
a705ae8748 Merge pull request 'Add changeable config path' (#53) from GEEKiDoS/segatools:develop into develop
Reviewed-on: Dniel97/segatools#53
2024-12-23 18:04:12 +00:00
b52455339f Merge pull request 'dns: add port overriding support' (#52) from t12i/segatools:develop into develop
Reviewed-on: Dniel97/segatools#52
2024-12-23 17:54:38 +00:00
ff21223f06 Removed the unused lines 2024-12-17 10:06:41 +07:00
047733d122 format code 2024-12-16 11:18:00 +08:00
21bb965382 typo 2024-12-16 11:14:14 +08:00
11556a1332 add changeable config path 2024-12-16 11:09:38 +08:00
d8202e1df4 dns: add port overriding support 2024-12-12 02:28:02 +08:00
2d3d6fc2bb Skip the patch when already patched 2024-11-26 01:40:57 +07:00
6d8ffb46ef Merge pull request 'dns: fix msvc build' (#50) from Haruka/segatools:fixmsvc into develop
Reviewed-on: Dniel97/segatools#50
2024-11-17 13:42:53 +00:00
2069b1ea85 dns: fix msvc build 2024-11-14 13:03:13 +01:00
c80f903cf8 Fix build with Microsoft Visual C++, Fix gfxhook and felica issue (#48)
I just wanna say that It is a SHAME that a Windows ONLY project was not able to build without MINGW
Also where's the missing `3mpxsc.h` in diva hook?

This also fixes the window size issue from hook_CreateWindowExA in gfxhook
And Fixes felica issue as described in #45

Reviewed-on: Dniel97/segatools#48
Reviewed-by: Dniel97 <dniel97@noreply.gitea.tendokyu.moe>
Co-authored-by: GEEKiDoS <geek_ds@foxmail.com>
Co-committed-by: GEEKiDoS <geek_ds@foxmail.com>
2024-11-11 16:28:24 +00:00
ceb2b63e8b Modify host header in HTTP requests to bypass domain censorship in China. (#34)
Co-authored-by: Sanheiii <35133371+Sanheiii@users.noreply.github.com>
Reviewed-on: Dniel97/segatools#34
Co-authored-by: Sanhei <sanhei@noreply.gitea.tendokyu.moe>
Co-committed-by: Sanhei <sanhei@noreply.gitea.tendokyu.moe>
2024-11-11 16:24:33 +00:00
83840e0a87 dns: add new WAHLAP url blocked (#49)
Reviewed-on: Dniel97/segatools#49
Co-authored-by: zaphkito <zaphkito@noreply.gitea.tendokyu.moe>
Co-committed-by: zaphkito <zaphkito@noreply.gitea.tendokyu.moe>
2024-11-10 20:47:40 +00:00
e50d6d8ebc Merge pull request 'Throw fatal when vfs option configured but invalid' (#47) from Bottersnike/segatools:feat/vfs-validation into develop
Reviewed-on: Dniel97/segatools#47
2024-11-05 16:36:43 +00:00
e1a47cf365 Throw fatal when vfs option configured but invalid 2024-11-04 22:55:15 +00:00
8aef1cfa79 Change method set environment variable to current process only using "SetEnvironmentVariableW" 2024-11-05 00:48:21 +07:00
8fc24503c8 diva, fgo: added gfx, close #46 2024-11-03 23:00:43 +01:00
ebf0f0b428 Develop a new/better method to detect cpu using intrinsic functions (__cpuid and __cpuidex) 2024-11-02 00:26:31 +07:00
892eb2b859 idz, idac, swdc: fixed rumble effect 2024-10-29 22:06:07 +01:00
b80b9fbc19 Delete useless comment 2024-10-18 13:44:47 +07:00
cef3406691 Add switch for openssl patch in segatools.ini 2024-10-18 13:34:25 +07:00
97d2d6b9bc resolved camelCase and the " :" problem 2024-10-16 15:53:52 +07:00
f39b9ce3a0 resolve dniel97 comments 2024-10-16 15:01:39 +07:00
243bb778d1 Add automatically apply OpenSSL patch for Intel Gen 10+ CPUs 2024-10-16 04:08:54 +07:00
66317a0054 bump capnhook rev to include serial fixes 2024-10-11 07:32:22 +02:00
8c24e04900 Merge pull request 'printer: Add setting to configure "printing time"' (#39) from Haruka/segatools:printerdelay into develop
Reviewed-on: Dniel97/segatools#39
2024-10-04 12:53:26 +00:00
3bb9404a38 printer: add the default waitTime setting to config 2024-10-04 11:43:34 +02:00
6819963f06 Merge branch 'refs/heads/develop' into printerdelay 2024-10-03 12:12:59 +02:00
36849bd09a Merge branch 'feature/ffb' into develop 2024-09-30 23:23:23 +02:00
5f817c8a36 swdc: minor improvements 2024-09-30 23:17:37 +02:00
259b763a13 idz: add ffb and led emulation 2024-09-30 23:10:16 +02:00
2251585ef0 swdc: add ffb and led emulation 2024-09-30 20:23:28 +02:00
c06bb408e7 idac: add ffb emulation 2024-09-30 18:50:46 +02:00
53fb8c28ea Merge pull request 'kemono: only load I/O dll inside amdaemon' (#38) from Haruka/segatools:kemonofr64bit into develop
Reviewed-on: Dniel97/segatools#38
2024-09-28 13:36:06 +00:00
33452394e6 kemono: also only load aimeio dll in x64 process 2024-09-27 17:50:40 +02:00
4fa9abffe8 printer: add ability to delay printing 2024-09-27 11:06:18 +02:00
88a5bdcd14 kemono: only load I/O dll inside amdaemon 2024-09-26 11:52:00 +02:00
bb773a63ce Merge pull request 'Kemono Friends support / 32-bit CHC300 support' (#36) from Haruka/segatools:kemonofr into develop
Reviewed-on: Dniel97/segatools#36
2024-09-21 15:12:05 +00:00
25e79f87c2 Merge pull request 'felica: fix rare card scan error (correct PMm)' (#33) from zaphkito/segatools:develop into develop
Reviewed-on: Dniel97/segatools#33
2024-09-21 15:10:28 +00:00
79592514ba fgo: fix printer 2024-09-20 11:14:41 +02:00
cdfd3bf655 kemono: not sure why that went missing 2024-09-20 11:09:21 +02:00
f6c12fd230 kemono: Pre-generate printer firmware files 2024-09-19 13:46:49 +02:00
86556ed2c8 kemono: Update start.bat 2024-09-16 14:29:49 +02:00
9de48dd6ce kemono: flip declarations 2024-09-13 16:52:55 +02:00
d257887f6e kemono: fix packagefile again 2024-09-12 13:29:17 +02:00
3eef5dd209 kemono: fix LED board check error 2024-09-12 13:25:38 +02:00
599d5e3211 kemono: fix LED hooking, add button LEDs 2024-09-12 13:25:19 +02:00
f18d074c5f kemono: add missed declarations 2024-09-12 12:50:57 +02:00
6bd1bce419 kemono: mention in readme 2024-09-12 12:50:51 +02:00
96bdacfa7c kemono: remove old amdaemon workaround 2024-09-12 12:40:57 +02:00
d4bb7b6e0e kemono: correct keychip IP range 2024-09-12 12:40:10 +02:00
70ac873d11 kemono: add to package creation 2024-09-12 12:39:42 +02:00
068651b6fa kemono: add support 2024-09-11 13:31:23 +02:00
84e9ed3c9a felica: fix rare card scan error (cucorrect PMm)
from real aime card with official card reader
2024-08-31 13:57:18 +00:00
c827b4c212 Merge pull request 'add almost full vfd implementation' (#31) from Haruka/segatools:vfd into develop
Reviewed-on: Dniel97/segatools#31
Reviewed-by: Dniel97 <dniel97@noreply.gitea.tendokyu.moe>
2024-08-24 21:56:38 +00:00
26624f25b1 hide default vfd ports from configs 2024-08-24 11:14:13 +02:00
824bc9abda default vfd port number to zero (use game-specific port) 2024-08-23 17:24:47 +02:00
cc5b87b559 add vfd settings to docs 2024-08-23 17:23:59 +02:00
e6794807a6 add default port fallback for vfd 2024-08-23 17:20:05 +02:00
54cbbffae9 add almost full vfd implementation 2024-08-23 16:30:22 +02:00
ac0f9f0587 aime firmware fix, mu3 keybinding fix 2024-08-21 15:13:09 +02:00
0061158188 printer: changed filename for holo cards 2024-08-20 13:40:47 +02:00
c535f18e40 added support for tokyo 2024-08-20 13:32:35 +02:00
c91c7db3c7 renamed [gpio] dipsw settings to [system] 2024-08-20 10:48:08 +02:00
6a4cae1165 Merge pull request 'Add bounds checking for D3D9 adapter number' (#29) from Bottersnike/segatools:fix/adapter_range into develop
Reviewed-on: Dniel97/segatools#29
2024-08-19 13:54:58 +00:00
383039e16e Add bounds checking for D3D9 adapter number 2024-08-17 21:17:23 +01:00
37c26ecadb chuniio: Add OpeNITHM LED protocol support (#26)
This commit basically copy-pastes the last commits from https://dev.s-ul.net/VeroxZik/segatools/-/commits/master to add OpenITHM LED support. Doesn't need to edit segatools.ini because the relevant config lines are already there for some reason.

Tested with my OpenITHM controller and behaves exactly like the other fork.

Reviewed-on: Dniel97/segatools#26
Co-authored-by: d4nin3u <d4nin3u@gmail.com>
Co-committed-by: d4nin3u <d4nin3u@gmail.com>
2024-08-06 21:35:51 +00:00
686d57d3ee unity: timezone spoofing fixed, close #27 2024-08-06 11:14:27 +02:00
b9204d4765 unity: hopefully fixes timezone spoofing #27 2024-08-05 21:59:59 +02:00
5abc593b46 cm: added printer support 2024-08-05 20:53:56 +02:00
fe14630b3d unity: fixed option loading crash 2024-08-05 20:49:47 +02:00
92fe2751e7 Merge pull request 'dns: added WAHLAP billing DNS block' (#23) from zaphkito/segatools:develop into develop
Reviewed-on: Dniel97/segatools#23
2024-07-06 23:06:31 +00:00
8c839b0d4e dns: added WAHLAP billing DNS block
China have another company named universal service WACCA, but they use same PowerOn and Download Order domain with SEGA official, so we need express `sys-all.cn` is only used for WAHLAP, not all China SEGA games.
WAHLAP have a unused billing domain, just in case, we blocked it now
2024-07-03 18:04:21 +00:00
ccb655a12b Merge pull request 'Add configurable debug logging' (#22) from Bottersnike/segatools:develop into develop
Reviewed-on: Dniel97/segatools#22
2024-07-01 18:42:01 +00:00
ded89f6343 Make fixes based on #22 review 2024-07-01 19:28:23 +01:00
f3e31fc2ae improved doc 2024-06-30 19:37:04 +02:00
965126c68a idac: improved compatibility with newer versions 2024-06-30 14:23:20 +02:00
050951e56f idac: removed unused include 2024-06-24 17:15:29 +02:00
7e5e0f132e idac: 837-15070 board implementation 2024-06-23 21:21:57 +02:00
4e58d3b9a2 added game specific devices documentation 2024-06-23 21:04:08 +02:00
7d3cab256b Add configurable debug logging 2024-06-20 01:22:01 +01:00
b0f307f427 Fixed option loading, thanks @Hay1tsme, close #16 2024-06-09 00:50:54 +02:00
7aa996193c Merge pull request 'dns: added CHN DNS block' (#17) from zaphkito/segatools:develop into develop
Reviewed-on: Dniel97/segatools#17
2024-05-20 19:59:37 +00:00
9353c9872f dns: added CHN DNS block 2024-05-19 09:37:57 +00:00
d8b3d41809 mu3: hotfix for calling mu3_io_led_set_colors 2024-05-16 08:10:05 +02:00
3bfb046afc mu3, chusan: improved library doc 2024-05-15 21:42:15 +02:00
9fe98b227b mu3: added lights hook 2024-05-12 22:06:37 +02:00
b77ce7b457 io3: added basic rotary input support 2024-05-12 19:39:56 +02:00
517469a60c switched to new capnhook, updated unityhook, added LED 15093 to MU3 2024-05-12 19:37:30 +02:00
1069cfee26 Merge pull request 'dns: amlog hook & subdomain wildcard parse' (#14) from Yusen0727/segatools:fix/amlog-dns-hook into develop
Reviewed-on: Dniel97/segatools#14
2024-05-10 04:09:34 +00:00
d3a0faa530 Merge pull request 'unityhook: check for new entrypoint' (#15) from Yusen0727/segatools:fix/new-doorstop-entrypoint into develop
Reviewed-on: Dniel97/segatools#15
2024-05-10 04:04:01 +00:00
00b3d5b7bb unityhook: check for new entrypoint
The new entrypoint has been introduced in UnityDoorstop 4.1.0,
which is used by BepInEx 5.4.23.

This commit check for new entrypoint to support new version
of BepInEx.
2024-05-10 08:24:53 +08:00
04fcd0d09a dns: amlog hook & subdomain wildcard parse 2024-05-09 15:02:22 +08:00
608 changed files with 22131 additions and 6558 deletions

4
.clang-format Normal file
View File

@ -0,0 +1,4 @@
---
BasedOnStyle: Google
IndentWidth: 4
---

6
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -1,33 +1,39 @@
# Segatools
Version: `2024-03-13`
Version: `2025-07-27`
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
## List of supported games
* 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
* Hatsune Miku: Project DIVA Arcade
* up to Future Tone
* SEGA World Drivers Championship
* SEGA World Drivers Championship 2019
* Fate/Grand Order
* Fate/Grand Order Arcade
* O.N.G.E.K.I.
* starting from O.N.G.E.K.I.
* maimai DX
* starting from maimai DX
* Card Maker
* starting from Card Maker
* 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

View File

@ -1,124 +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];
UINT codepage;
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);
codepage = GetACP();
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;
}
uint8_t cmd = 0;
uint8_t str_1[512];
uint8_t str_2[512];
uint8_t str_1_len = 0;
uint8_t str_2_len = 0;
for (size_t i = 0; i < vfd_uart.written.pos; i++) {
if (vfd_uart.written.bytes[i] == 0x1B) {
i++;
cmd = vfd_uart.written.bytes[i];
if (cmd == 0x30) {
i += 3;
}
else if (cmd == 0x50) {
i++;
}
continue;
}
if (cmd == 0x30) {
str_1[str_1_len++] = vfd_uart.written.bytes[i];
}
else if (cmd == 0x50) {
str_2[str_2_len++] = vfd_uart.written.bytes[i];
}
}
if (str_1_len) {
str_1[str_1_len++] = '\0';
if (codepage != 932) {
WCHAR buffer[512];
MultiByteToWideChar(932, 0, (LPCSTR)str_1, str_1_len, buffer, str_1_len);
char str_recode[str_1_len * 3];
WideCharToMultiByte(codepage, 0, buffer, str_1_len, str_recode, str_1_len * 3, NULL, NULL);
dprintf("VFD: %s\n", str_recode);
}
else {
dprintf("VFD: %s\n", str_1);
}
}
if (str_2_len) {
str_2[str_2_len++] = '\0';
if (codepage != 932) {
WCHAR buffer[512];
MultiByteToWideChar(932, 0, (LPCSTR)str_2, str_2_len, buffer, str_2_len);
char str_recode[str_2_len * 3];
WideCharToMultiByte(codepage, 0, buffer, str_2_len, str_recode, str_2_len * 3, NULL, NULL);
dprintf("VFD: %s\n", str_recode);
} else {
dprintf("VFD: %s\n", str_2);
}
}
// dprintf("VFD TX:\n");
// dump_iobuf(&vfd_uart.written);
vfd_uart.written.pos = 0;
return hr;
}

View File

@ -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);

View File

@ -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];
@ -222,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;
}

View File

@ -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,
],

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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");

View File

@ -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'),
],

View File

@ -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);

View File

@ -1,6 +1,7 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include "aimeio/aimeio.h"

View File

@ -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);
}

View File

@ -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
View 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
View 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);

View File

@ -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,

View File

@ -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);
};

View File

@ -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;

View File

@ -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(

View 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;
};

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

28
common/board/led15070.h Normal file
View 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]);

View File

@ -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 {

View File

@ -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;
// }

View File

@ -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]);

View File

@ -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'
],
)

View File

@ -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 {

View File

@ -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,6 +62,11 @@ 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,
@ -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:
@ -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,

View File

@ -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(

View File

@ -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);

View File

@ -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
View 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
View 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
View 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
View 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
View 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);

View File

@ -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);
}

View File

@ -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
View 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
);
}

View File

@ -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
View 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
View File

@ -0,0 +1,3 @@
#pragma once
void gfx_gl_hook_init(const struct gfx_config *cfg, HINSTANCE self);

View File

@ -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',
],

View File

@ -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);
}

View File

@ -3,6 +3,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "hook/table.h"

View File

@ -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)

View File

@ -8,12 +8,14 @@
#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"
@ -81,6 +83,17 @@ static bool WINAPI hook_WinHttpCrackUrl(
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)(
@ -122,6 +135,17 @@ static bool (WINAPI *next_WinHttpCrackUrl)(
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",
@ -144,7 +168,7 @@ 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[] = {
@ -157,7 +181,22 @@ static const struct hook_symbol dns_hook_syms_winhttp[] = {
.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;
@ -165,6 +204,9 @@ 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)
{
@ -175,25 +217,87 @@ 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(
NULL,
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)
{
HRESULT hr;
@ -218,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) {
@ -297,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);
@ -361,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);
}
@ -405,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);
}
@ -472,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;
@ -526,8 +630,8 @@ static HINTERNET WINAPI hook_WinHttpConnect(
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(pwszServerName, pos->from) == 0) {
if(pos->to == NULL) {
if (match_domain(pwszServerName, pos->from)) {
if (pos->to == NULL) {
LeaveCriticalSection(&dns_hook_lock);
return NULL;
}
@ -558,11 +662,11 @@ static bool WINAPI hook_WinHttpCrackUrl(
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(pwszUrl, pos->from) == 0) {
if (match_domain(pwszUrl, pos->from)) {
wchar_t* toAddr = pos->to;
wchar_t titleBuffer[255];
if(wcscmp(toAddr, L"title") == 0) {
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;
@ -587,3 +691,93 @@ static bool WINAPI hook_WinHttpCrackUrl(
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);
}

View File

@ -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