Compare commits

..

230 Commits

Author SHA1 Message Date
12dbf7a90c Revert: fallback capnhook to 4633c51d58
ilufang's ERROR_IO_PENDING make serial device has serious delays and missing
before we fix it in capnhook, we need fallback it in segatools
2025-03-19 12:49:22 +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
25e954fb55 Merge pull request 'print vfd message instead of dump hex' (#13) from Sucareto/segatools:develop into develop
Reviewed-on: Dniel97/segatools#13
2024-04-29 19:48:35 +00:00
a8c6ac70e4 Merge pull request '[unity] Fix Unity config path' (#12) from beerpsi/segatools:fix/unity/wrong-config-path into develop
Reviewed-on: Dniel97/segatools#12
2024-04-29 19:41:53 +00:00
eb1ec0e261 idac: updated start.bat script 2024-04-29 21:34:30 +02:00
482a6e530a print vfd message 2024-04-30 02:19:10 +08:00
65173e1fa6 [unity] Fix Unity config path 2024-04-29 23:12:10 +07:00
4041844ea9 mai2/mu3/cm: Integrate UnityDoorstop with segatools (#11)
[UnityDoorstop](https://github.com/NeighTools/UnityDoorstop) is a tool to execute managed code (.NET DLLs) before Unity does, useful for modding frameworks such as BepInEx.

This PR integrates parts of its code into segatools, so loading BepInEx is as simple as adding 2 lines to `segatools.ini`:
```ini
[unity]
targetAssembly=BepInEx\core\BepInEx.Preloader.dll
```

This PR also factors out the Unity path redirection hooks to its own module.

Reviewed-on: Dniel97/segatools#11
Co-authored-by: beerpsi <beerpsi@duck.com>
Co-committed-by: beerpsi <beerpsi@duck.com>
2024-04-15 19:30:28 +00:00
47a65e5e51 fixed aime LED firmware 2024-03-17 14:20:13 +01:00
774a639bb7 cxb: fixed configs 2024-03-14 00:14:51 +01:00
097b74d849 cxb: server support added, bugfixes, thanks @Midorica 2024-03-13 21:40:25 +01:00
2590e749ca config bugfixes 2024-03-07 15:46:43 +01:00
9c66488906 added Move/Replace/Delete hooks
- fixes FGO not correctly switching `START UP MODE`
- fixes SWDC not correctly switching `CABINET SETTING`
2024-02-27 16:54:12 +01:00
f570869946 fixed AMFS path redirect with no E:/ drive present 2024-02-27 16:49:27 +01:00
629ded4018 added AimePay, E-MONEY DNS redirects 2024-02-25 19:03:05 +01:00
ca36a879cb updated README and added "AM Daemon" window name 2024-02-22 19:12:18 +01:00
ae199502e8 fixed amfs/appdata redirects 2024-02-22 12:02:32 +01:00
e40e1dffe3 improved all segatools.ini configs 2024-02-21 21:58:44 +01:00
56e971c6a4 changed default operator buttons to F1, F2 and F3 and fixed subnets 2024-02-06 13:01:26 +01:00
3e9303e043 fgo: ups forgot to add it to Package.mk 2024-02-06 12:34:39 +01:00
451a7ec49d added VFD toggle to config 2024-02-06 12:34:11 +01:00
dae3018411 doc: added develop dongle hint 2024-01-27 23:10:59 +01:00
cadf20040c swdc: fixed dinput buttons 2024-01-17 15:52:41 +01:00
cc0b6b0953 swdc: fixed steering wheel buttons, improved start.bat 2024-01-16 17:56:24 +01:00
0affc96e3e small optimizations 2024-01-16 17:54:06 +01:00
a8bd98706f Merge pull request 'fix(netenv): Print IP addresses properly' (#4) from beerpsi/segatools:fix/print-ip-addresses-properly into develop
Reviewed-on: Dniel97/segatools#4
2024-01-08 18:26:51 +00:00
aa2184f947 Merge branch 'develop' into fix/print-ip-addresses-properly 2024-01-07 18:36:04 +00:00
d0165b1eb0 fix(netenv): Print IP addresses properly 2024-01-08 01:34:55 +07:00
477dad2667 Merge branch 'chuniio' into develop 2023-12-27 19:29:55 +01:00
63320f8456 Merge pull request 'fix chunihook' (#2) from CrazyRedMachine/segatools:chuniio into chuniio
Reviewed-on: Dniel97/segatools#2
2023-12-27 15:14:07 +00:00
926493290b fix chunihook 2023-12-26 06:47:19 -05:00
f4a3a5f78d optimized meson.build to decrease filesize 2023-12-25 15:55:43 +01:00
f5f275c8e9 chuniio: fixed individual IR notes 2023-12-25 15:12:25 +01:00
b38dea23ac chu2to3: fixed docker compilation, bugfixes 2023-12-25 15:03:44 +01:00
16bbd87c73 Merge pull request 'Include chu2to3 engine' (#1) from CrazyRedMachine/segatools:chuniio into chuniio
Reviewed-on: Dniel97/segatools#1
2023-12-25 14:02:33 +00:00
ee414d122b Include chu2to3 engine 2023-12-24 08:33:30 -05:00
d4372fa5c2 chuniio: use HRESULT instead of int for chuni_io_led_init() 2023-12-21 00:45:41 +01:00
ac9b889d71 chusan/chuni: tower LEDs added 2023-12-19 14:40:02 +01:00
3bf223c04e chusan: fixed SP/CVT switching 2023-12-19 13:45:06 +01:00
8ebdf67d6e chuni/chusan: added LED output to DLLs (will break most DLLs)
Credits: somewhatlurker, skogaby
https://dev.s-ul.net/skogaby/segatools/-/blob/ongeki-15093/
2023-12-19 12:48:33 +01:00
ed042176d7 chusan: ledport changed to COM5, fixes VFD 2023-12-17 20:17:26 +01:00
ad154a83e5 chusan: fixed dipsw settings, added aimeio DLL loading 2023-12-17 19:43:00 +01:00
1cbc33d97d chuasn: fixed coin insert for custom chuniio 2023-12-17 09:26:30 +01:00
72db08ac93 idz, idac, swdc: added FFB centering spring feature 2023-12-11 19:18:25 +01:00
4ffcf25555 added freeplay setting to ALLS games 2023-12-03 22:22:17 +01:00
3dd6054a1e chusan, fgo, mu3: fixed LED 15093 board 2023-12-03 21:34:38 +01:00
793417e891 added new aime card reader generation
- Added new aime generation: 837-15286 and 837-15396
- New config setting `[aime] gen=3` for 837-15396
- Updated LED information for card reader
- Updated all games with the needed reader generation?
2023-12-03 18:45:42 +01:00
8c12853051 cm: disabled coin selector AS6DB 2023-11-30 00:02:00 +01:00
a3fd2fb926 Merge branch 'master' into develop 2023-11-29 23:07:24 +01:00
4dcf01f643 carol: somewhat-working touch board?? 2023-11-27 23:23:00 -05:00
8b1d0cfefa LED board improvements and cleanup 2023-11-25 23:00:05 +01:00
d86baab852 fgo: working FTDI hook by @OLEG
from: 88a0026f0b
2023-11-22 21:43:42 +01:00
b9fd59fd70 idz: added fullscreen patch by @Felix (again) 2023-11-20 18:04:45 +01:00
146fac9287 dns: block more All.NET requests 2023-11-12 21:16:02 +01:00
3cf5cbb793 added LED 15093-06 credits and docs 2023-11-12 19:14:42 +01:00
a2db39c58c fgo: fgohook finally added
Credits:
- Coburn
- domeori
- Mitsuhide
- OLEG
- rakisaionji
2023-11-11 23:05:11 +01:00
946ea7ef3b chusan: fixed LED configs 2023-11-11 22:41:04 +01:00
962e14dc9b ongeki: fix start.bat and segatools.ini 2023-11-05 21:51:36 -05:00
25562e37f9 nusec: fixes keychip not found error 0949 2023-11-02 23:32:13 +01:00
d521eeb43e idz, idac, swdc: Added separate pedals config, better XInput controls
- Configure separate pedals which are not part of the steering wheel (base)
 - It is now possible to have a different steering wheel, pedal and shifter brand all connected to 3 different USB ports
- XInput does not have a deadzone at the end of the max steering anymore and the `restrict` setting works as intended
2023-10-05 00:46:54 +02:00
6c45d0995b fix cursor being annoying by @OLEG
95273ccecb
2023-10-05 00:37:25 +02:00
a4bd570cfc chusan, mai2, mu3, swdc: removed emoney config 2023-10-05 00:35:55 +02:00
37793fc051 Merge branch 'master' into develop 2023-10-05 00:30:24 +02:00
5d04685c73 update gitignore 2023-09-19 10:33:30 -04:00
528ec4379c createprocess: add replace_all flag 2023-09-15 19:57:11 -04:00
5a4e947354 carol: use createprocess hook 2023-09-15 19:52:26 -04:00
f5a7e5b821 epay: added config because git is dumb 2023-09-15 19:58:46 +02:00
5ef0cf6181 Merge branch 'master' into develop 2023-09-15 19:56:48 +02:00
157f52da4c platform: add epay hook 2023-09-15 01:35:33 -04:00
0d83977073 hooklib: fill out my_CreateProcessA 2023-09-13 20:23:40 -04:00
dca84e08d0 hooklib: fix createprocess imports 2023-09-13 19:57:10 -04:00
2dbb4aec8c hooklib: add createprocess to meson 2023-09-13 19:54:22 -04:00
3d7d9fcaa5 hooklib: add createprocess hook skeleton 2023-09-13 17:54:40 -04:00
98d2ea1390 vfs: add hook for C:\Users\AppUser 2023-09-13 11:25:29 -04:00
31203daa09 idz, idac, swdc: fixed restrict config setting 2023-09-05 00:24:43 +02:00
80718104f6 swdc: updated config, fixed start.bat 2023-09-05 00:23:17 +02:00
eb2eef927a idac: added left and right button mapping 2023-09-04 23:31:52 +02:00
9bef9e49f8 swdc: added aime firm to config_hook.json 2023-09-04 02:05:37 +02:00
d5a551482b idac: mapped left and right thumbstick to dpad left and right 2023-09-04 02:04:42 +02:00
49f729c501 idz, idac, swdc: switched to [xinput] config instead of [io] 2023-09-04 02:03:31 +02:00
91f69beae6 idac: fixed config error 2023-08-30 02:24:02 +02:00
51b11469d0 added aimeGen as default instead of feliciaGen 2023-08-29 22:57:04 +02:00
f0dc51d01e idac: added config_hook.json 2023-08-29 20:03:33 +02:00
ca4a8bd84d idz, idac, swdc: Added deadzone config 2023-08-29 02:22:05 +02:00
f6e961d4f4 Removed -f from start.bat and fixed idz 2023-08-29 00:49:58 +02:00
a69a9b5917 swdc: disabled built in XInput 2023-08-20 16:21:55 +02:00
97234f26d7 cm, chusan, mai2, mu3, swdc: added emoney config 2023-08-15 22:02:38 +02:00
2277bf7526 all: fix accounting issue (my bad) 2023-08-15 20:23:28 +02:00
80d5fc4bb2 mu3: Added basic keyboard and mouse lever emulation support 2023-08-15 17:40:32 +02:00
608c9ac1a6 chusan, cm, mai2, mu3: Added dipSw support and highBaudrate AiMe 2023-08-15 17:28:19 +02:00
3dc2ec6e69 idac, idz, swdc: Fixed DInput brake/accel, added cubic steering 2023-08-15 17:20:27 +02:00
28ef2d719a dinput: fixed POV bug 2023-07-19 12:31:57 +02:00
600f795104 disable dipsw by default 2023-07-14 01:07:22 +02:00
e5d17b82b2 updated Package.mk for mai2, cm 2023-07-14 01:03:36 +02:00
0ee081ca1b mai2: segatools added 2023-07-14 01:00:28 +02:00
01be6ee33c cardmaker: hook, touch screen hook added
Thanks @domeori https://dev.s-ul.net/domeori/segatools/-/tree/mr-imports
2023-07-14 00:59:24 +02:00
2a6a8bf8b2 chusan: added chusanhook, led board fix, config added
credits go to @domeori https://dev.s-ul.net/domeori/segatools/-/tree/mr-imports
2023-07-14 00:58:02 +02:00
90a6f1be7c mu3: coin input added 2023-07-14 00:54:30 +02:00
ec072667b3 swdc: first segatools added 2023-07-14 00:52:50 +02:00
89195ed60b Merge branch 'idac_test' into develop 2023-07-14 00:43:38 +02:00
c27ef9674d idac: added dipswitch support (beta) 2023-07-14 00:41:23 +02:00
da97d23b51 idac: first segatools support 2023-06-29 11:27:52 +02:00
ee6675dd73 Merge branch 'master' into idac_test 2023-06-27 21:11:45 +02:00
4c67843f08 carol: add touch dll functions 2023-05-31 04:54:38 -04:00
02201dfba5 path: add hooks for PathFileExistsA/W 2023-05-24 01:08:08 -04:00
9113766c22 vfs: add D drive hooks 2023-05-24 01:07:56 -04:00
6fc2482c19 carol: add control board emulation, document touch board more 2023-04-28 04:25:47 -04:00
555784258a idac: test 2023-04-23 16:13:51 +02:00
74c8b312c5 carol: fix prints 2023-04-11 00:22:41 -04:00
ef00932c64 carol: fix ports 2023-04-11 00:20:51 -04:00
8c97dc09c0 carol: fix control board request struct 2023-03-30 03:55:40 -04:00
301a0e0ce7 update build system 2023-03-19 13:29:08 -04:00
5935e322e8 Merge pull request 'fix Makefile failing on strip' (#1) from Yellowberry/segatools:master into master
Reviewed-on: Hay1tsme/segatools#1
2023-02-19 18:59:59 +00:00
05e762d3ce fix Makefile failing on strip 2023-02-18 22:58:00 -07:00
442 changed files with 33276 additions and 1237 deletions

4
.clang-format Normal file
View File

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

19
.gitignore vendored
View File

@ -1,9 +1,26 @@
.*.swp
.vscode/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Suggested names for build dirs
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

@ -1,3 +1,4 @@
{
"editor.formatOnSave": false,
"mesonbuild.configureOnOpen": false,
}

View File

@ -5,15 +5,16 @@ V ?= @
BUILD_DIR := build
BUILD_DIR_32 := $(BUILD_DIR)/build32
BUILD_DIR_64 := $(BUILD_DIR)/build64
BUILD_DIR_DOCKER := $(BUILD_DIR)/docker
BUILD_DIR_ZIP := $(BUILD_DIR)/zip
DOC_DIR := doc
DIST_DIR := dist
DOCKER_CONTAINER_NAME := "segatools-build"
DOCKER_IMAGE_NAME := "segatools:build"
# Add "-D[option]=[value]" here as necessary
MESON_OPTIONS :=
# For options that shouldn't be committed
-include MesonLocalOptions.mk
# -----------------------------------------------------------------------------
# Targets
@ -23,9 +24,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
@ -42,15 +43,6 @@ zip: $(BUILD_DIR_ZIP)/segatools.zip
clean:
$(V)rm -rf $(BUILD_DIR) subprojects/capnhook
.PHONY: build-docker # Build the project in a docker container
build-docker:
$(V)docker rm -f $(DOCKER_CONTAINER_NAME) 2> /dev/null || true
$(V)docker build -t $(DOCKER_IMAGE_NAME) -f Dockerfile .
$(V)docker create --name $(DOCKER_CONTAINER_NAME) $(DOCKER_IMAGE_NAME)
$(V)rm -rf $(BUILD_DIR_DOCKER)
$(V)mkdir -p $(BUILD_DIR_DOCKER)
$(V)docker cp $(DOCKER_CONTAINER_NAME):/segatools/$(BUILD_DIR_ZIP) $(BUILD_DIR_DOCKER)
# -----------------------------------------------------------------------------
# Utility, combo and alias targets
# -----------------------------------------------------------------------------

View File

@ -5,7 +5,7 @@ $(BUILD_DIR_ZIP)/chuni.zip:
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_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 \
@ -20,7 +20,7 @@ $(BUILD_DIR_ZIP)/cxb.zip:
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_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 \
@ -35,7 +35,7 @@ $(BUILD_DIR_ZIP)/diva.zip:
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_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 \
@ -50,7 +50,7 @@ $(BUILD_DIR_ZIP)/carol.zip:
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_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 \
@ -65,7 +65,7 @@ $(BUILD_DIR_ZIP)/idz.zip:
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_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 \
@ -73,6 +73,53 @@ $(BUILD_DIR_ZIP)/idz.zip:
$(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip *
$(BUILD_DIR_ZIP)/fgo.zip:
$(V)echo ... $@
$(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 \
$(DIST_DIR)/fgo/segatools.ini \
$(DIST_DIR)/fgo/launch.bat \
$(BUILD_DIR_ZIP)/fgo
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/fgo/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/fgo/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/fgo ; zip -r ../fgo.zip *
$(BUILD_DIR_ZIP)/idac.zip:
$(V)echo ... $@
$(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 \
$(DIST_DIR)/idac/segatools.ini \
$(DIST_DIR)/idac/config_hook.json \
$(DIST_DIR)/idac/launch.bat \
$(BUILD_DIR_ZIP)/idac
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/idac/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/idac/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/idac ; zip -r ../idac.zip *
$(BUILD_DIR_ZIP)/swdc.zip:
$(V)echo ... $@
$(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 \
$(DIST_DIR)/swdc/segatools.ini \
$(DIST_DIR)/swdc/config_hook.json \
$(DIST_DIR)/swdc/launch.bat \
$(BUILD_DIR_ZIP)/swdc
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/swdc/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/swdc/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/swdc ; zip -r ../swdc.zip *
$(BUILD_DIR_ZIP)/mercury.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury
@ -80,7 +127,7 @@ $(BUILD_DIR_ZIP)/mercury.zip:
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_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 \
@ -88,6 +135,27 @@ $(BUILD_DIR_ZIP)/mercury.zip:
$(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip *
$(BUILD_DIR_ZIP)/chusan.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/chusan
$(V)mkdir -p $(BUILD_DIR_ZIP)/chusan/DEVICE
$(V)cp $(DIST_DIR)/chusan/segatools.ini \
$(DIST_DIR)/chusan/config_hook.json \
$(DIST_DIR)/chusan/launch.bat \
$(BUILD_DIR_ZIP)/chusan
$(V)cp $(BUILD_DIR_32)/chusanhook/chusanhook.dll \
$(BUILD_DIR_ZIP)/chusan/chusanhook_x86.dll
$(V)cp $(BUILD_DIR_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
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_ZIP)/chusan/inject_x64.exe
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/chusan/DEVICE
for x in exe dll; do strip $(BUILD_DIR_ZIP)/chusan/*.$$x; done
$(V)cd $(BUILD_DIR_ZIP)/chusan ; zip -r ../chusan.zip *
$(BUILD_DIR_ZIP)/mu3.zip:
$(V)echo ... $@
@ -96,7 +164,7 @@ $(BUILD_DIR_ZIP)/mu3.zip:
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_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 \
@ -104,6 +172,74 @@ $(BUILD_DIR_ZIP)/mu3.zip:
$(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.zip *
$(BUILD_DIR_ZIP)/mai2.zip:
$(V)echo ... $@
$(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 \
$(DIST_DIR)/mai2/segatools.ini \
$(DIST_DIR)/mai2/launch.bat \
$(BUILD_DIR_ZIP)/mai2
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/mai2/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/mai2/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mai2 ; zip -r ../mai2.zip *
$(BUILD_DIR_ZIP)/cm.zip:
$(V)echo ... $@
$(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 \
$(DIST_DIR)/cm/config_hook.json \
$(DIST_DIR)/cm/segatools.ini \
$(DIST_DIR)/cm/launch.bat \
$(BUILD_DIR_ZIP)/cm
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/cm/DEVICE
$(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_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_32)/kemonohook/kemonohook.dll \
$(BUILD_DIR_ZIP)/kemono/kemonohook_x86.dll
$(V)cp $(BUILD_DIR_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)/doc.zip: \
$(DOC_DIR)/config \
$(DOC_DIR)/chunihook.md \
@ -119,8 +255,16 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
$(BUILD_DIR_ZIP)/diva.zip \
$(BUILD_DIR_ZIP)/doc.zip \
$(BUILD_DIR_ZIP)/idz.zip \
$(BUILD_DIR_ZIP)/idac.zip \
$(BUILD_DIR_ZIP)/swdc.zip \
$(BUILD_DIR_ZIP)/mercury.zip \
$(BUILD_DIR_ZIP)/chusan.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 \
CHANGELOG.md \
README.md \

View File

@ -1,21 +1,37 @@
# Segatools
Version: `v005`
Version: `2024-09-30`
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
## List of supported games
* Chunithm
* [Chunithm (Plus)](doc/chunihook.md)
* [Chunithm Air (Plus)](doc/chunihook.md)
* [Chunithm Star (Plus)](doc/chunihook.md)
* [Chunithm Amazon (Plus)](doc/chunihook.md)
* [Chunithm Crystal (Plus)](doc/chunihook.md)
* 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)
* Wacca
* Wacca Lilly R (WIP)
* Initial D THE ARCADE
* maimai DX
* starting from maimai DX
* Mario & Sonic
* Mario & Sonic at the Tokyo 2020 Olympics Arcade
* O.N.G.E.K.I.
* starting from O.N.G.E.K.I.
* SEGA World Drivers Championship
* SEGA World Drivers Championship 2019
* WACCA
* starting from WACCA
* Kemono Friends
* Kemono Friends 3: Planet Tours
## End-users

View File

@ -12,11 +12,13 @@
#include "util/crc.h"
#include "util/dprintf.h"
#include "util/env.h"
struct aime_io_config {
wchar_t aime_path[MAX_PATH];
wchar_t felica_path[MAX_PATH];
bool felica_gen;
bool aime_gen;
uint8_t vk_scan;
};
@ -40,6 +42,11 @@ static HRESULT aime_io_generate_felica(
uint8_t *bytes,
size_t nbytes);
static HRESULT aime_io_generate_aime(
const wchar_t *path,
uint8_t *bytes,
size_t nbytes);
static void aime_io_config_read(
struct aime_io_config *cfg,
const wchar_t *filename)
@ -62,11 +69,16 @@ static void aime_io_config_read(
cfg->felica_path,
_countof(cfg->felica_path),
filename);
dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError());
cfg->felica_gen = GetPrivateProfileIntW(
L"aime",
L"felicaGen",
0,
filename);
cfg->aime_gen = GetPrivateProfileIntW(
L"aime",
L"aimeGen",
1,
filename);
@ -136,7 +148,7 @@ static HRESULT aime_io_generate_felica(
srand(time(NULL));
for (i = 0 ; i < nbytes ; i++) {
for (i = 0; i < nbytes; i++) {
bytes[i] = rand();
}
@ -151,7 +163,7 @@ static HRESULT aime_io_generate_felica(
return E_FAIL;
}
for (i = 0 ; i < nbytes ; i++) {
for (i = 0; i < nbytes; i++) {
fprintf(f, "%02X", bytes[i]);
}
@ -163,6 +175,47 @@ static HRESULT aime_io_generate_felica(
return S_OK;
}
static HRESULT aime_io_generate_aime(
const wchar_t *path,
uint8_t *bytes,
size_t nbytes)
{
size_t i;
FILE *f;
assert(path != NULL);
assert(bytes != NULL);
assert(nbytes > 0);
srand(time(NULL));
/* AiMe IDs should not start with 3, due to a missing check for BananaPass IDs */
do {
for (i = 0; i < nbytes; i++) {
bytes[i] = rand() % 10 << 4 | rand() % 10;
}
} while (bytes[0] >> 4 == 3);
f = _wfopen(path, L"w");
if (f == NULL) {
dprintf("AimeIO DLL: %S: fopen failed: %i\n", path, (int) errno);
return E_FAIL;
}
for (i = 0; i < nbytes; i++) {
fprintf(f, "%02x", bytes[i]);
}
fprintf(f, "\n");
fclose(f);
dprintf("AimeIO DLL: Generated random AiMe ID\n");
return S_OK;
}
uint16_t aime_io_get_api_version(void)
{
return 0x0100;
@ -170,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;
}
@ -210,6 +263,22 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
return S_OK;
}
/* Try generating AiMe IC (if enabled) */
if (aime_io_cfg.aime_gen) {
hr = aime_io_generate_aime(
aime_io_cfg.aime_path,
aime_io_aime_id,
sizeof(aime_io_aime_id));
if (FAILED(hr)) {
return hr;
}
aime_io_aime_id_present = true;
return S_OK;
}
/* Try FeliCa IC */
hr = aime_io_read_id_file(

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"
@ -18,6 +19,7 @@ struct aime_dll {
struct aime_dll_config {
wchar_t path[MAX_PATH];
bool path64;
};
extern struct aime_dll aime_dll;

View File

@ -8,19 +8,61 @@
#include "board/aime-dll.h"
#include "board/config.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "util/dprintf.h"
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
// Workaround for x64/x86 external IO dlls
// path32 for 32bit, path64 for 64bit
// for else.. is that possible? idk
if (cfg->path64) {
#if defined(ENV32BIT)
// Always empty, due to amdaemon being 64 bit in 32 bit mode
memset(cfg->path, 0, sizeof(cfg->path));
#elif defined(ENV64BIT)
GetPrivateProfileStringW(
L"aimeio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
#else
#error "Unknown environment"
#endif
} else {
GetPrivateProfileStringW(
L"aimeio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
}
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
@ -30,6 +72,9 @@ 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);
}
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
@ -39,3 +84,21 @@ void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
cfg->enable = GetPrivateProfileIntW(L"io4", L"enable", 1, filename);
}
void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
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

@ -5,6 +5,10 @@
#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
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
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"
@ -28,7 +29,7 @@ enum {
IO4_CMD_CLEAR_BOARD_STATUS = 0x03,
IO4_CMD_SET_GENERAL_OUTPUT = 0x04,
IO4_CMD_SET_PWM_OUTPUT = 0x05,
IO4_CMD_UNIMPLEMENTED = 0x41,
IO4_CMD_SET_UNIQUE_OUTPUT = 0x41,
IO4_CMD_UPDATE_FIRMWARE = 0x85,
};
@ -40,7 +41,7 @@ struct io4_report_in {
uint16_t buttons[2];
uint8_t system_status;
uint8_t usb_status;
uint8_t unknown[29];
uint8_t unique_input[29];
};
static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
@ -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;
@ -232,15 +237,15 @@ static HRESULT io4_handle_write(struct irp *irp)
return S_OK;
case IO4_CMD_SET_UNIQUE_OUTPUT:
// dprintf("USB I/O: Unique Out\n");
return S_OK;
case IO4_CMD_UPDATE_FIRMWARE:
dprintf("USB I/O: Update firmware..?\n");
return E_FAIL;
case IO4_CMD_UNIMPLEMENTED:
//dprintf("USB I/O: Unimplemented cmd 41\n");
return S_OK;
return E_FAIL;
default:
dprintf("USB I/O: Unknown command %02x\n", out.cmd);
@ -316,7 +321,7 @@ static HRESULT io4_async_poll(void *ctx, struct irp *irp)
/* Delay long enough for the instigating thread in amdaemon to be satisfied
that all queued-up reports have been drained. */
Sleep(1);
// Sleep(1);
/* Call into ops to poll the underlying inputs */

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(

81
board/led15070-cmd.h Normal file
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;
};

194
board/led15070-frame.c Normal file
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;
}

26
board/led15070-frame.h Normal file
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
board/led15070.c Normal file

File diff suppressed because it is too large Load Diff

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

222
board/led15093-cmd.h Normal file
View File

@ -0,0 +1,222 @@
#pragma once
#include "board/led15093-frame.h"
/* Command IDs */
enum {
LED_15093_CMD_RESET = 0x10,
LED_15093_CMD_SET_TIMEOUT = 0x11,
LED_15093_CMD_UNK1 = 0x12,
LED_15093_CMD_SET_DISABLE_RESPONSE = 0x14,
LED_15093_CMD_SET_ID = 0x18,
LED_15093_CMD_CLEAR_ID = 0x19,
LED_15093_CMD_SET_MAX_BRIGHT = 0x3F, // TODO
LED_15093_CMD_UPDATE_LED = 0x80,
LED_15093_CMD_SET_LED = 0x81,
LED_15093_CMD_SET_IMM_LED = 0x82,
LED_15093_CMD_SET_FADE_LED = 0x83,
LED_15093_CMD_SET_FADE_LEVEL = 0x84,
LED_15093_CMD_SET_FADE_SHIFT = 0x85,
LED_15093_CMD_SET_AUTO_SHIFT = 0x86,
LED_15093_CMD_GET_BOARD_INFO = 0xF0,
LED_15093_CMD_GET_BOARD_STATUS = 0xF1,
LED_15093_CMD_GET_FW_SUM = 0xF2,
LED_15093_CMD_GET_PROTOCOL_VER = 0xF3,
LED_15093_CMD_SET_BOOTMODE = 0xFD,
LED_15093_CMD_FW_UPDATE = 0xFE,
};
/* Response codes */
enum {
LED_15093_STATUS_OK = 0x01,
LED_15093_STATUS_ERR_SUM = 0x02,
LED_15093_STATUS_ERR_PARITY = 0x03,
LED_15093_STATUS_ERR_FRAMING = 0x04,
LED_15093_STATUS_ERR_OVERRUN = 0x05,
LED_15093_STATUS_ERR_BUFFER_OVERFLOW = 0x06,
};
enum {
LED_15093_REPORT_OK = 0x01,
LED_15093_REPORT_WAIT = 0x02,
LED_15093_REPORT_ERR1 = 0x03,
LED_15093_REPORT_ERR2 = 0x04,
};
/* Status bitmasks */
enum {
LED_15093_STATUS_UART_ERR_SUM = 0x01,
LED_15093_STATUS_UART_ERR_PARITY = 0x02,
LED_15093_STATUS_UART_ERR_FRAMING = 0x04,
LED_15093_STATUS_UART_ERR_OVERRUN = 0x08,
LED_15093_STATUS_UART_ERR_BUFFER_OVERFLOW = 0x10,
};
enum {
LED_15093_STATUS_BOARD_ERR_WDT = 0x01,
LED_15093_STATUS_BOARD_ERR_TIMEOUT = 0x02,
LED_15093_STATUS_BOARD_ERR_RESET = 0x04,
LED_15093_STATUS_BOARD_ERR_BOR = 0x08,
};
enum {
LED_15093_STATUS_CMD_ERR_BUSY = 0x01,
LED_15093_STATUS_CMD_ERR_UNKNOWN = 0x02,
LED_15093_STATUS_CMD_ERR_PARAM = 0x04,
LED_15093_STATUS_CMD_ERR_EXE = 0x08,
};
/* Status types for internal use */
enum {
LED_15093_STATUS_TYPE_BOARD = 1,
LED_15093_STATUS_TYPE_UART = 2,
LED_15093_STATUS_TYPE_CMD = 3,
};
/* Request data structures */
struct led15093_req_reset {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint8_t r_type;
};
struct led15093_req_set_timeout {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint16_t count;
};
struct led15093_req_set_disable_response {
struct led15093_req_hdr hdr;
uint8_t cmd;
bool sw;
};
struct led15093_req_set_id {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint8_t id;
};
struct led15093_req_set_led {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint8_t data[198];
};
struct led15093_req_set_fade_level {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint8_t depth;
uint8_t cycle;
};
struct led15093_req_set_fade_shift {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint8_t target;
};
struct led15093_req_set_auto_shift {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint8_t count;
uint8_t target;
};
struct led15093_req_get_board_status {
struct led15093_req_hdr hdr;
uint8_t cmd;
bool clear;
};
union led15093_req_any {
struct led15093_req_hdr hdr;
struct led15093_req_reset reset;
struct led15093_req_set_timeout set_timeout;
struct led15093_req_set_disable_response set_disable_response;
struct led15093_req_set_id set_id;
struct led15093_req_set_led set_led;
struct led15093_req_set_fade_level set_fade_level;
struct led15093_req_set_fade_shift set_fade_shift;
struct led15093_req_set_auto_shift set_auto_shift;
struct led15093_req_get_board_status get_board_status;
uint8_t payload[256];
};
/* Response data structures */
struct led15093_resp_any {
struct led15093_resp_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
uint8_t data[32];
};
struct led15093_resp_timeout {
struct led15093_resp_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
uint8_t count_upper;
uint8_t count_lower;
};
struct led15093_resp_fw_sum {
struct led15093_resp_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
uint8_t sum_upper;
uint8_t sum_lower;
};
struct led15093_resp_board_info_legacy {
struct led15093_resp_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
char board_num[8];
uint8_t lf; // 0x0A (ASCII LF)
char chip_num[5];
uint8_t endcode; // Always 0xFF
uint8_t fw_ver;
};
struct led15093_resp_board_info {
struct led15093_resp_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
char board_num[8];
uint8_t lf; // 0x0A (ASCII LF)
char chip_num[5];
uint8_t endcode; // Always 0xFF
uint8_t fw_ver;
uint16_t rx_buf;
};
struct led15093_resp_protocol_ver {
struct led15093_resp_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
uint8_t mode;
uint8_t major_ver;
uint8_t minor_ver;
};
struct led15093_resp_set_auto_shift {
struct led15093_resp_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
uint8_t count;
uint8_t target;
};

196
board/led15093-frame.c Normal file
View File

@ -0,0 +1,196 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "board/led15093-frame.h"
#include "hook/iobuf.h"
static void led15093_frame_sync(struct iobuf *src);
static HRESULT led15093_frame_accept(const struct iobuf *dest);
static HRESULT led15093_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 led15093_frame_sync(struct iobuf *src)
{
size_t i;
for (i = 0 ; i < src->pos && src->bytes[i] != LED_15093_FRAME_SYNC ; i++);
src->pos -= i;
memmove(&src->bytes[0], &src->bytes[i], i);
}
static HRESULT led15093_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 led15093_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);
led15093_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 == LED_15093_FRAME_SYNC) {
hr = E_FAIL;
} else if (byte == LED_15093_FRAME_ESC) {
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 = led15093_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 led15093_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] == LED_15093_FRAME_SYNC &&
src[3] + 4 == nbytes);
if (dest->pos >= dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = LED_15093_FRAME_SYNC;
checksum = 0;
// dprintf("%02x ", LED_15093_FRAME_SYNC);
for (i = 1 ; i < nbytes ; i++) {
byte = src[i];
checksum += byte;
// dprintf("%02x ", byte);
hr = led15093_frame_encode_byte(dest, byte);
if (FAILED(hr)) {
return hr;
}
}
// dprintf("%02x \n", checksum);
return led15093_frame_encode_byte(dest, checksum);
}
static HRESULT led15093_frame_encode_byte(struct iobuf *dest, uint8_t byte)
{
if (byte == LED_15093_FRAME_SYNC || byte == LED_15093_FRAME_ESC) {
if (dest->pos + 2 > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = LED_15093_FRAME_ESC;
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;
}

34
board/led15093-frame.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
enum {
LED_15093_FRAME_SYNC = 0xE0,
LED_15093_FRAME_ESC = 0xD0,
};
struct led15093_req_hdr {
uint8_t sync;
uint8_t dest_adr;
uint8_t src_adr;
uint8_t nbytes;
};
struct led15093_resp_hdr {
uint8_t sync;
uint8_t dest_adr;
uint8_t src_adr;
uint8_t nbytes;
};
HRESULT led15093_frame_decode(struct iobuf *dest, struct iobuf *src);
HRESULT led15093_frame_encode(
struct iobuf *dest,
const void *ptr,
size_t nbytes);

1129
board/led15093.c Normal file

File diff suppressed because it is too large Load Diff

24
board/led15093.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct led15093_config {
bool enable;
bool high_baudrate;
unsigned int port_no[2];
char board_number[8];
char chip_number[5];
char boot_chip_number[5];
uint8_t fw_ver;
uint16_t fw_sum;
};
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 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'),
],
@ -20,6 +19,16 @@ board_lib = static_library(
'io3.h',
'io4.c',
'io4.h',
'led15093-cmd.h',
'led15093-frame.c',
'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',
@ -37,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

@ -25,6 +25,13 @@ struct sg_res_header {
uint8_t payload_len;
};
/* struct to save the version string with its length
to fix NUL terminator issues */
struct version_info {
const char *version;
uint8_t length;
};
typedef HRESULT (*sg_dispatch_fn_t)(
void *ctx,
const void *req,

View File

@ -17,7 +17,7 @@ struct sg_led_res_reset {
struct sg_led_res_get_info {
struct sg_res_header res;
uint8_t payload[9];
char payload[12];
};
struct sg_led_req_set_color {

View File

@ -27,14 +27,18 @@ static HRESULT sg_led_cmd_set_color(
const struct sg_led *led,
const struct sg_led_req_set_color *req);
static const uint8_t sg_led_info[] = {
'1', '5', '0', '8', '4', 0xFF, 0x10, 0x00, 0x12,
static const struct version_info led_version[] = {
{"15084\xFF\x10\x00\x12", 9},
{"000-00000\xFF\x11\x40", 12},
// maybe the same?
{"000-00000\xFF\x11\x40", 12}
};
void sg_led_init(
struct sg_led *led,
uint8_t addr,
const struct sg_led_ops *ops,
unsigned int gen,
void *ctx)
{
assert(led != NULL);
@ -43,6 +47,7 @@ void sg_led_init(
led->ops = ops;
led->ops_ctx = ctx;
led->addr = addr;
led->gen = gen;
}
void sg_led_transact(
@ -150,8 +155,11 @@ static HRESULT sg_led_cmd_get_info(
struct sg_led_res_get_info *res)
{
sg_led_dprintf(led, "Get info\n");
sg_res_init(&res->res, req, sizeof(res->payload));
memcpy(res->payload, sg_led_info, sizeof(sg_led_info));
const struct version_info *fw = &led_version[led->gen - 1];
sg_res_init(&res->res, req, fw->length);
memcpy(res->payload, fw->version, fw->length);
return S_OK;
}

View File

@ -15,12 +15,14 @@ struct sg_led {
const struct sg_led_ops *ops;
void *ops_ctx;
uint8_t addr;
unsigned int gen;
};
void sg_led_init(
struct sg_led *led,
uint8_t addr,
const struct sg_led_ops *ops,
unsigned int gen,
void *ctx);
void sg_led_transact(

View File

@ -5,18 +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_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_A = 0x51,
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
SG_NFC_CMD_MIFARE_AUTHENTICATE_B = 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 {
@ -31,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

@ -60,15 +60,33 @@ static HRESULT sg_nfc_cmd_felica_encap(
const struct sg_nfc_req_felica_encap *req,
struct sg_nfc_res_felica_encap *res);
static HRESULT sg_nfc_cmd_send_hex_data(
struct sg_nfc *nfc,
const struct sg_req_header *req,
struct sg_res_header *res);
static HRESULT sg_nfc_cmd_dummy(
struct sg_nfc *nfc,
const struct sg_req_header *req,
struct sg_res_header *res);
static const struct version_info hw_version[] = {
{"TN32MSEC003S H/W Ver3.0", 23},
{"837-15286", 9},
{"837-15396", 9}
};
static const struct version_info fw_version[] = {
{"TN32MSEC003S F/W Ver1.2", 23},
{"\x94", 1},
{"\x94", 1}
};
void sg_nfc_init(
struct sg_nfc *nfc,
uint8_t addr,
const struct sg_nfc_ops *ops,
unsigned int gen,
void *ops_ctx)
{
assert(nfc != NULL);
@ -77,6 +95,7 @@ void sg_nfc_init(
nfc->ops = ops;
nfc->ops_ctx = ops_ctx;
nfc->addr = addr;
nfc->gen = gen;
}
#ifdef NDEBUG
@ -170,12 +189,17 @@ static HRESULT sg_nfc_dispatch(
&req->felica_encap,
&res->felica_encap);
case SG_NFC_CMD_MIFARE_AUTHENTICATE:
case SG_NFC_CMD_MIFARE_AUTHENTICATE_A:
case SG_NFC_CMD_MIFARE_AUTHENTICATE_B:
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_TO_UPDATE_MODE:
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
default:
@ -202,9 +226,11 @@ static HRESULT sg_nfc_cmd_get_fw_version(
const struct sg_req_header *req,
struct sg_nfc_res_get_fw_version *res)
{
const struct version_info *fw = &fw_version[nfc->gen - 1];
/* Dest version is not NUL terminated, this is intentional */
sg_res_init(&res->res, req, sizeof(res->version));
memcpy(res->version, "TN32MSEC003S F/W Ver1.2E", sizeof(res->version));
sg_res_init(&res->res, req, fw->length);
memcpy(res->version, fw->version, fw->length);
return S_OK;
}
@ -214,9 +240,11 @@ static HRESULT sg_nfc_cmd_get_hw_version(
const struct sg_req_header *req,
struct sg_nfc_res_get_hw_version *res)
{
const struct version_info *hw = &hw_version[nfc->gen - 1];
/* Dest version is not NUL terminated, this is intentional */
sg_res_init(&res->res, req, sizeof(res->version));
memcpy(res->version, "TN32MSEC003S H/W Ver3.0J", sizeof(res->version));
sg_res_init(&res->res, req, hw->length);
memcpy(res->version, hw->version, hw->length);
return S_OK;
}
@ -326,13 +354,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;
}
@ -401,7 +429,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
@ -415,7 +443,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
@ -423,6 +451,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

@ -22,6 +22,7 @@ struct sg_nfc {
const struct sg_nfc_ops *ops;
void *ops_ctx;
uint8_t addr;
unsigned int gen;
struct felica felica;
struct mifare mifare;
};
@ -30,6 +31,7 @@ void sg_nfc_init(
struct sg_nfc *nfc,
uint8_t addr,
const struct sg_nfc_ops *ops,
unsigned int gen,
void *ops_ctx);
void sg_nfc_transact(

View File

@ -47,7 +47,8 @@ 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)
{
HRESULT hr;
@ -65,11 +66,31 @@ HRESULT sg_reader_hook_init(
return hr;
}
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, NULL);
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, NULL);
unsigned int port_no = cfg->port_no;
if (port_no == 0){
port_no = default_port_no;
}
if (cfg->gen != 0) {
gen = cfg->gen;
}
if (gen < 1 || gen > 3) {
dprintf("NFC Assembly: Invalid reader generation: %u\n", gen);
return E_INVALIDARG;
}
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, NULL);
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, gen, NULL);
InitializeCriticalSection(&sg_reader_lock);
if (!cfg->high_baudrate) {
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);
@ -100,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,9 +9,13 @@
struct aime_config {
struct aime_dll_config dll;
bool enable;
unsigned int port_no;
bool high_baudrate;
unsigned int gen;
};
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
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
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
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);

View File

@ -2,17 +2,17 @@
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. */
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"
@ -21,14 +21,50 @@
#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[512];
static uint8_t vfd_readable[512];
static uint8_t vfd_written[4096];
static uint8_t vfd_readable[4096];
HRESULT vfd_hook_init(unsigned int port_no)
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);
@ -38,6 +74,48 @@ HRESULT vfd_hook_init(unsigned int port_no)
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;
@ -48,15 +126,274 @@ static HRESULT vfd_handle_irp(struct irp *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;
}

View File

@ -2,4 +2,12 @@
#include <windows.h>
HRESULT vfd_hook_init(unsigned int port_no);
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

@ -21,9 +21,18 @@ const struct dll_bind_sym carol_dll_syms[] = {
}, {
.sym = "carol_io_touch_init",
.off = offsetof(struct carol_dll, touch_init),
}, {
.sym = "carol_io_ledbd_init",
.off = offsetof(struct carol_dll, ledbd_init),
}, {
.sym = "carol_io_controlbd_init",
.off = offsetof(struct carol_dll, controlbd_init),
}, {
.sym = "carol_io_touch_start",
.off = offsetof(struct carol_dll, touch_start),
}, {
.sym = "carol_io_touch_stop",
.off = offsetof(struct carol_dll, touch_stop),
}
};

View File

@ -10,7 +10,10 @@ struct carol_dll {
void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams);
void (*jvs_read_coin_counter)(uint16_t *total);
HRESULT (*touch_init)();
HRESULT (*ledbd_init)();
HRESULT (*controlbd_init)();
void (*touch_start)(carol_io_touch_callback_t callback);
void (*touch_stop)();
};
struct carol_dll_config {

View File

@ -16,4 +16,7 @@ EXPORTS
carol_io_jvs_poll
carol_io_jvs_read_coin_counter
carol_io_touch_init
carol_io_ledbd_init
carol_io_controlbd_init
carol_io_touch_start
carol_io_touch_stop

View File

@ -58,6 +58,20 @@ void controlbd_config_load(
filename);
}
void ledbd_config_load(
struct ledbd_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(
L"ledbd",
L"enable",
1,
filename);
}
void carol_hook_config_load(
struct carol_hook_config *cfg,
@ -73,4 +87,5 @@ void carol_hook_config_load(
gfx_config_load(&cfg->gfx, filename);
touch_config_load(&cfg->touch, filename);
controlbd_config_load(&cfg->controlbd, filename);
ledbd_config_load(&cfg->ledbd, filename);
}

View File

@ -13,6 +13,7 @@
#include "gfxhook/gfx.h"
#include "carolhook/touch.h"
#include "carolhook/ledbd.h"
#include "carolhook/controlbd.h"
struct carol_hook_config {
@ -22,6 +23,7 @@ struct carol_hook_config {
struct carol_dll_config dll;
struct gfx_config gfx;
struct touch_config touch;
struct ledbd_config ledbd;
struct controlbd_config controlbd;
};

View File

@ -18,10 +18,18 @@
static HRESULT controlbd_handle_irp(struct irp *irp);
static HRESULT controlbd_handle_irp_locked(struct irp *irp);
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf);
static HRESULT controlbd_frame_dispatch(struct controlbd_req *dest);
static HRESULT controlbd_frame_decode(struct controlbd_req_any *dest, struct iobuf *src);
static HRESULT controlbd_set_header(struct controlbd_resp_hdr *resp, uint8_t cmd, uint8_t len);
static uint8_t calc_checksum(void *data, size_t len);
static HRESULT controlbd_req_nop(uint8_t cmd);
static HRESULT controlbd_req_dispatch(const struct controlbd_req_any *req);
static HRESULT controlbd_req_ack_any(uint8_t cmd);
static HRESULT controlbd_req_reset(void);
static HRESULT controlbd_req_get_board_info(void);
static HRESULT controlbd_req_firmware_checksum(void);
static HRESULT controlbd_req_polling(const struct controlbd_req_any *req);
const uint8_t CONTROLBD_SYNC_BYTE = 0xE0;
static CRITICAL_SECTION controlbd_lock;
static struct uart controlbd_uart;
@ -36,7 +44,7 @@ HRESULT controlbd_hook_init(const struct controlbd_config *cfg)
InitializeCriticalSection(&controlbd_lock);
uart_init(&controlbd_uart, 11);
uart_init(&controlbd_uart, 12);
controlbd_uart.written.bytes = controlbd_written_bytes;
controlbd_uart.written.nbytes = sizeof(controlbd_written_bytes);
controlbd_uart.readable.bytes = controlbd_readable_bytes;
@ -64,10 +72,49 @@ static HRESULT controlbd_handle_irp(struct irp *irp)
return hr;
}
static HRESULT controlbd_frame_decode(struct controlbd_req_any *req, struct iobuf *src)
{
uint8_t data_len = 0;
uint8_t checksum_pos = src->pos - 1;
uint8_t calculated_checksum = 0;
uint8_t checksum = 0;
if (src->pos < 6) {
dprintf("Control Board: Decode Error, request too short (pos is 0x%08X)\n", (int)src->pos);
return SEC_E_BUFFER_TOO_SMALL;
}
req->hdr.sync = src->bytes[0];
req->hdr.dest = src->bytes[1];
req->hdr.src = src->bytes[2];
req->hdr.len = src->bytes[3];
req->hdr.cmd = src->bytes[4];
data_len = req->hdr.len;
src->pos -= 5;
for (int i = 0; i < data_len; i++) {
if (src->pos == 0) {
break;
}
req->bytes[i] = src->bytes[i + 5];
src->pos --;
}
checksum = src->bytes[checksum_pos];
calculated_checksum = calc_checksum(req, checksum_pos);
if (checksum != calculated_checksum) {
dprintf("Control Board: Decode Error, checksum failure (expected 0x%02X, got 0x%02X)\n", calculated_checksum, checksum);
return HRESULT_FROM_WIN32(ERROR_CRC);
}
return S_OK;
}
static HRESULT controlbd_handle_irp_locked(struct irp *irp)
{
struct controlbd_req req;
HRESULT hr;
struct controlbd_req_any req;
assert(carol_dll.controlbd_init != NULL);
@ -76,7 +123,7 @@ static HRESULT controlbd_handle_irp_locked(struct irp *irp)
hr = carol_dll.controlbd_init();
if (FAILED(hr)) {
dprintf("Control Board: Backend DLL error: %x\n", (int) hr);
dprintf("Control Board: Backend DLL error: 0X%X\n", (int) hr);
return hr;
}
@ -89,90 +136,204 @@ static HRESULT controlbd_handle_irp_locked(struct irp *irp)
}
for (;;) {
#if 0
dprintf("Control Board: TX Buffer:\n");
dump_iobuf(&controlbd_uart.written);
if (controlbd_uart.written.bytes[0] == 0xE0) {
#if defined(LOG_CAROL_CONTROL_BD)
dprintf("Control Board: TX Buffer:\n");
dump_iobuf(&controlbd_uart.written);
#endif
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
if (FAILED(hr)) {
dprintf("Control Board: Deframe Error: %x\n", (int) hr);
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
if (FAILED(hr)) {
dprintf("Control Board: Deframe error: %x\n", (int) hr);
return hr;
}
hr = controlbd_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Control Board: Dispatch Error: 0X%X\n", (int) hr);
return hr;
}
#if defined(LOG_CAROL_CONTROL_BD)
dprintf("Control Board: RX Buffer:\n");
dump_iobuf(&controlbd_uart.readable);
#endif
controlbd_uart.written.pos = 0;
return hr;
}
hr = controlbd_frame_dispatch(&req);
if (FAILED(hr)) {
dprintf("Control Board: Dispatch Error: %x\n", (int) hr);
// The board has a LPC111x bootloader that gets ran over every boot
// this is to account for that.
char cmd[255];
strcpy_s(cmd, 255, (char *)controlbd_uart.written.bytes);
cmd[controlbd_uart.written.pos] = '\0';
return hr;
if (!strcmp(cmd, "?")) {
dprintf("Control Board: Bootloader Hello\n");
}
else if (!strcmp(cmd, "Synchronized\r\n")) {
iobuf_write(&controlbd_uart.readable, "Synchronized\r\nNG\r\n", 19);
// Set this to OK instead of NG to do an update
}
else if (!strcmp(cmd, "12000\r\n")) {
iobuf_write(&controlbd_uart.readable, "12000\r\nOK\r\n", 12);
}
else {
// Everything other then the two commands above just want 0\r\n
// appended to the request as the response. Given that it only checks sometimes,
// it's safe to just run over the readable buffer every response.
controlbd_uart.readable.pos = 0;
cmd[controlbd_uart.written.pos] = '0';
cmd[controlbd_uart.written.pos + 1] = '\r';
cmd[controlbd_uart.written.pos + 2] = '\n';
cmd[controlbd_uart.written.pos + 3] = '\0';
// dprintf("Control Board: Return %s\n", cmd);
iobuf_write(&controlbd_uart.readable, cmd, controlbd_uart.written.pos + 3);
}
controlbd_uart.written.pos = 0;
return hr;
}
}
static HRESULT controlbd_frame_dispatch(struct controlbd_req *req)
static HRESULT controlbd_req_dispatch(const struct controlbd_req_any *req)
{
switch (req->cmd) {
case CONTROLBD_CMD_UNK_11:
return controlbd_req_nop(req->cmd);
switch (req->hdr.cmd) {
case CONTROLBD_CMD_RESET:
return controlbd_req_reset();
case CONTROLBD_CMD_BDINFO:
return controlbd_req_get_board_info();
case CONTROLBD_CMD_FIRM_SUM:
return controlbd_req_firmware_checksum();
case CONTROLBD_CMD_TIMEOUT:
dprintf("Control Board: Acknowledge Timeout\n");
return controlbd_req_ack_any(req->hdr.cmd);
case CONTROLBD_CMD_PORT_SETTING:
dprintf("Control Board: Acknowledge Port Setting\n");
return controlbd_req_ack_any(req->hdr.cmd);
case CONTROLBD_CMD_INITIALIZE:
dprintf("Control Board: Acknowledge Initialize\n");
return controlbd_req_ack_any(req->hdr.cmd);
case CONTROLBD_CMD_POLLING:
return controlbd_req_polling(req);
default:
dprintf("Unhandled command %#02x\n", req->cmd);
return S_OK;
dprintf("Unhandled command 0x%02x\n", req->hdr.cmd);
return controlbd_req_ack_any(req->hdr.cmd);
}
}
static HRESULT controlbd_req_nop(uint8_t cmd)
static HRESULT controlbd_set_header(struct controlbd_resp_hdr *resp, uint8_t cmd, uint8_t len)
{
dprintf("Control Board: No-op cmd %#02x\n", cmd);
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0xE0;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x11;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x03;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x10;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x27;
resp->sync = CONTROLBD_SYNC_BYTE;
resp->dest = 0x01;
resp->src = 0x02;
resp->len = len;
resp->report = 0x01;
resp->cmd = cmd;
return S_OK;
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf)
static uint8_t calc_checksum(void *data, size_t len)
{
int initial_pos = iobuf->pos;
uint8_t check = 0;
uint8_t *stuff;
stuff = data;
uint8_t checksum = 0;
uint16_t tmp = 0;
dest->sync = iobuf->bytes[0];
iobuf->pos--;
dest->cmd = iobuf->bytes[1];
iobuf->pos--;
check += dest->cmd;
dest->checksum = iobuf->bytes[initial_pos - 1];
iobuf->pos--;
dest->data_length = initial_pos - 3; // sync, cmd, checksum
if (dest->data_length > 0) {
for (int i = 0; i < dest->data_length; i++) {
dest->data[i] = iobuf->bytes[i+2];
check += dest->data[i];
}
}
iobuf->pos -= dest->data_length;
if (dest->sync != 0xe0) {
dprintf("Control Board: Sync error, expected 0xe0, got %x\n", dest->sync);
return E_FAIL;
}
if (dest->checksum != check) {
dprintf("Control Board: Checksum error, expected %x, got %x\n", check, dest->checksum);
return E_FAIL;
for (int i = 1; i < len; i++) {
tmp = checksum + stuff[i];
checksum = tmp & 0xFF;
}
return S_OK;
}
return checksum;
}
static HRESULT controlbd_req_reset(void)
{
struct controlbd_resp_reset resp;
dprintf("Control Board: Reset\n");
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_RESET, 2);
resp.checksum = 0;
// No data, just ack
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_get_board_info(void)
{
struct controlbd_resp_bdinfo resp;
memset(&resp, 0, sizeof(resp));
dprintf("Control Board: Get Board Info\n");
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_BDINFO, 21);
resp.rev = 0x90;
resp.bfr_size = 0x0001;
resp.ack = 1;
strcpy_s(resp.bd_no, sizeof(resp.bd_no), "15312 ");
strcpy_s(resp.chip_no, sizeof(resp.chip_no), "6699 ");
resp.chip_no[5] = 0xFF;
resp.bd_no[8] = 0x0A;
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_firmware_checksum(void)
{
struct controlbd_resp_fw_checksum resp;
memset(&resp, 0, sizeof(resp));
dprintf("Control Board: Get Firmware Checksum\n");
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_FIRM_SUM, 5);
resp.ack = 1;
resp.fw_checksum = 0x1b36; // This could change with an update... oh well
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_polling(const struct controlbd_req_any *req)
{
struct controlbd_req_polling req_struct;
memset(&req_struct, 0, sizeof(req_struct));
memcpy_s(&req_struct, sizeof(req_struct), req, sizeof(req_struct));
struct controlbd_resp_polling resp;
memset(&resp, 0, sizeof(resp));
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_POLLING, 16);
// TODO: Figure out output (pen vibration, etc)
resp.ack = 1;
resp.unk7 = 3;
resp.unk8 = 1;
resp.unk9 = 1;
resp.btns_pressed = 0; // bit 1 is pen button, bit 2 is dodge
resp.coord_x = 0x0;
resp.coord_y = 0x0;
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_ack_any(uint8_t cmd)
{
struct controlbd_resp_any_ack resp;
memset(&resp, 0, sizeof(resp));
controlbd_set_header(&resp.hdr, cmd, 3);
resp.ack = 1;
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
@ -7,15 +9,140 @@
struct controlbd_config {
bool enable;
};
enum controlbd_cmd {
CONTROLBD_CMD_UNK_11 = 0x11
};
struct controlbd_req {
uint8_t sync; // First byte is the sync
uint8_t cmd; // Command byte
uint8_t data[256]; // Request body goes here
uint8_t checksum; // Final byte is all bytes added, except the sync
uint8_t data_length; // Size of the data including command byte
#pragma pack(push, 1)
struct controlbd_req_hdr {
uint8_t sync;
uint8_t dest; // unsure
uint8_t src; // unsure
uint8_t len; // length of the rest of the request minus checksum
uint8_t cmd;
};
struct controlbd_req_any {
struct controlbd_req_hdr hdr;
uint8_t bytes[255];
};
struct controlbd_req_reset {
struct controlbd_req_hdr hdr;
uint8_t payload;
uint8_t checksum;
};
struct controlbd_req_bdinfo {
struct controlbd_req_hdr hdr;
uint8_t checksum;
};
struct controlbd_req_fw_checksum {
struct controlbd_req_hdr hdr;
uint8_t checksum;
};
struct controlbd_req_timeout {
struct controlbd_req_hdr hdr;
uint8_t unknown;
uint8_t checksum;
};
struct controlbd_req_polling {
struct controlbd_req_hdr hdr;
uint8_t unknown[20];
uint8_t checksum;
};
struct controlbd_resp_hdr {
uint8_t sync;
uint8_t dest; // unsure
uint8_t src; // unsure
uint8_t len; // length of the rest of the request minus checksum
uint8_t report; // 0x01 for success, anything else for failure
uint8_t cmd;
};
struct controlbd_resp_any {
struct controlbd_resp_hdr hdr;
uint8_t bytes[255];
};
struct controlbd_resp_any_ack {
struct controlbd_resp_hdr hdr;
uint8_t ack;
uint8_t checksum;
};
struct controlbd_resp_reset {
struct controlbd_resp_hdr hdr;
uint8_t checksum;
};
struct controlbd_resp_bdinfo {
struct controlbd_resp_hdr hdr;
uint8_t ack;
char bd_no[9];
char chip_no[6];
uint8_t rev;
uint16_t bfr_size;
uint8_t checksum;
};
struct controlbd_resp_fw_checksum {
struct controlbd_resp_hdr hdr;
uint8_t ack;
uint16_t fw_checksum;
uint8_t checksum;
};
struct controlbd_resp_polling {
struct controlbd_resp_hdr hdr;
uint8_t ack;
uint8_t unk7;
uint8_t unk8;
uint8_t unk9;
uint8_t btns_pressed;
uint8_t blob[7];
int8_t coord_x;
int8_t coord_y;
uint8_t checksum;
};
enum {
PEN_BTN_PRESSED_BIT = 1,
DODGE_BTN_PRESSED_BIT = 2
};
enum {
CONTROLBD_CMD_RESET = 0x10,
CONTROLBD_CMD_TIMEOUT = 0x11,
CONTROLBD_CMD_RETRY = 0x12,
CONTROLBD_CMD_GETIN = 0x20,
CONTROLBD_CMD_GETADI = 0x21,
CONTROLBD_CMD_SETOUTPUT = 0x30,
CONTROLBD_CMD_INITIALIZE = 0x80,
CONTROLBD_CMD_POLLING = 0x81,
CONTROLBD_CMD_CUSTOM_PATTERN = 0x82,
CONTROLBD_CMD_DEBUG_CAROL = 0x83,
CONTROLBD_CMD_POLLING_GENERAL = 0x84,
CONTROLBD_CMD_CMD_STATUS = 0x90,
CONTROLBD_CMD_PORT_SETTING = 0x91,
CONTROLBD_CMD_PWM_DUTY = 0x92,
CONTROLBD_CMD_LED_SET = 0x93,
CONTROLBD_CMD_LED_REFRESH = 0x94,
CONTROLBD_CMD_DEBUG_UART = 0xB0,
CONTROLBD_CMD_DEBUG_I2C = 0xB1,
CONTROLBD_CMD_DEBUG_STATUS = 0xC0,
CONTROLBD_CMD_BDINFO = 0xF0,
CONTROLBD_CMD_FIRM_SUM = 0xF2,
CONTROLBD_CMD_PROTOCOL = 0xF3,
CONTROLBD_CMD_UPDATE = 0xFE,
};
#pragma pack(pop)
HRESULT controlbd_hook_init(const struct controlbd_config *cfg);

View File

@ -1,3 +1,31 @@
/*
"Wonderland Wars" (carol*) hook
Devices:
JVS: 837-14572 "Type 3" I/O Board
[Satellite]
USB: "WinTouch" Controller Board
^ (DIPSW2 ON, Version 5.xx.xx or above)
COM1: 3M Touch Systems 78-0011-2353-4 Touch Controller Board
^ (DIPSW2 OFF)
COM10: TN32MSEC003S "Gen 1" Aime Reader
OR
837-15286 "Gen 2" Aime Reader
^ (Version 1.6x.xx or above)
COM11: 837-15070-02 LED Controller Board
COM12: 837-15312 Pen Controller I/O Board
[Terminal]
COM10: 837-15286 "Gen 2" Aime Reader
*: SEGA's abbreviation for Lewis Carroll, author of Alice's Adventures in
Wonderland.
*/
#include <windows.h>
#include <stdlib.h>
@ -12,17 +40,20 @@
#include "carolhook/carol-dll.h"
#include "carolhook/jvs.h"
#include "carolhook/touch.h"
#include "carolhook/ledbd.h"
#include "carolhook/controlbd.h"
#include "carolhook/serial.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "hooklib/createprocess.h"
#include "hooklib/cursor.h"
#include "platform/platform.h"
#include "util/dprintf.h"
#include "util/env.h"
static HMODULE carol_hook_mod;
static process_entry_t carol_startup;
@ -30,21 +61,45 @@ static struct carol_hook_config carol_hook_cfg;
/*
COM Layout
01:(?) Touchscreen
01: Touchscreen
10: Aime reader
11: Control board
12(?): LED Board
11: LED board
12: Control Board
*/
static DWORD CALLBACK carol_pre_startup(void)
{
HRESULT hr;
HMODULE d3dc;
HMODULE dbghelp;
dprintf("--- Begin carol_pre_startup ---\n");
/* Pin the D3D shader compiler. This makes startup much faster. */
d3dc = LoadLibraryW(L"D3DCompiler_43.dll");
if (d3dc != NULL) {
dprintf("Pinned shader compiler, hMod=%p\n", d3dc);
} else {
dprintf("Failed to load shader compiler!\n");
}
/* Pin dbghelp so the path hooks apply to it. */
dbghelp = LoadLibraryW(L"dbghelp.dll");
if (dbghelp != NULL) {
dprintf("Pinned debug helper library, hMod=%p\n", dbghelp);
} else {
dprintf("Failed to load debug helper library!\n");
}
cursor_hook_init();
/* Config load */
carol_hook_config_load(&carol_hook_cfg, L".\\segatools.ini");
carol_hook_config_load(&carol_hook_cfg, get_config_path());
/* Hook Win32 APIs */
@ -74,16 +129,14 @@ static DWORD CALLBACK carol_pre_startup(void)
goto fail;
}
hr = sg_reader_hook_init(&carol_hook_cfg.aime, 10, carol_hook_mod);
hr = sg_reader_hook_init(&carol_hook_cfg.aime, 10, 1, carol_hook_mod);
if (FAILED(hr)) {
goto fail;
}
gfx_hook_init(&carol_hook_cfg.gfx);
gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod);
//serial_init();
gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod);
hr = touch_hook_init(&carol_hook_cfg.touch);
@ -91,15 +144,26 @@ static DWORD CALLBACK carol_pre_startup(void)
goto fail;
}
hr = ledbd_hook_init(&carol_hook_cfg.ledbd);
if (FAILED(hr)) {
goto fail;
}
hr = controlbd_hook_init(&carol_hook_cfg.controlbd);
if (FAILED(hr)) {
goto fail;
}
hr = createprocess_push_hook_a(".\\15312firm\\firmupdate_1113.exe", "inject -d -k carolhook.dll ", NULL, false);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
spike_hook_init(get_config_path());
dprintf("--- End carol_pre_startup ---\n");

168
carolhook/ledbd.c Normal file
View File

@ -0,0 +1,168 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "carolhook/carol-dll.h"
#include "carolhook/ledbd.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
#include "board/slider-frame.h"
#include "board/slider-cmd.h"
static HRESULT ledbd_handle_irp(struct irp *irp);
static HRESULT ledbd_handle_irp_locked(struct irp *irp);
static HRESULT ledbd_frame_dispatch(const union slider_req_any *dest);
static HRESULT ledbd_req_noop(uint8_t cmd);
static HRESULT ledbd_req_unk7c(uint8_t cmd);
static HRESULT ledbd_req_unkF0(uint8_t cmd);
static CRITICAL_SECTION ledbd_lock;
static struct uart ledbd_uart;
static uint8_t ledbd_written_bytes[520];
static uint8_t ledbd_readable_bytes[520];
HRESULT ledbd_hook_init(const struct ledbd_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&ledbd_lock);
uart_init(&ledbd_uart, 11);
ledbd_uart.written.bytes = ledbd_written_bytes;
ledbd_uart.written.nbytes = sizeof(ledbd_written_bytes);
ledbd_uart.readable.bytes = ledbd_readable_bytes;
ledbd_uart.readable.nbytes = sizeof(ledbd_readable_bytes);
dprintf("LED Board: Init\n");
return iohook_push_handler(ledbd_handle_irp);
}
static HRESULT ledbd_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&ledbd_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&ledbd_lock);
hr = ledbd_handle_irp_locked(irp);
LeaveCriticalSection(&ledbd_lock);
return hr;
}
static HRESULT ledbd_handle_irp_locked(struct irp *irp)
{
union slider_req_any req;
struct iobuf req_iobuf;
HRESULT hr;
assert(carol_dll.ledbd_init != NULL);
if (irp->op == IRP_OP_OPEN) {
dprintf("LED Board: Starting backend DLL\n");
hr = carol_dll.ledbd_init();
if (FAILED(hr)) {
dprintf("LED Board: Backend DLL error: 0X%X\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&ledbd_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if defined(LOG_CAROL_LED_BD)
dprintf("LED Board: TX Buffer:\n");
dump_iobuf(&ledbd_uart.written);
#endif
req_iobuf.bytes = req.bytes;
req_iobuf.nbytes = sizeof(req.bytes);
req_iobuf.pos = 0;
hr = slider_frame_decode(&req_iobuf, &ledbd_uart.written);
if (FAILED(hr)) {
dprintf("LED Board: Deframe Error: 0X%X\n", (int) hr);
return hr;
}
hr = ledbd_frame_dispatch(&req);
if (FAILED(hr)) {
dprintf("LED Board: Dispatch Error: 0X%X\n", (int) hr);
return hr;
}
return hr;
}
}
static HRESULT ledbd_frame_dispatch(const union slider_req_any *req)
{
switch (req->hdr.cmd) {
case LEDBD_CMD_UNK_10:
return ledbd_req_noop(req->hdr.cmd);
case LEDBD_CMD_UNK_7C:
return ledbd_req_unk7c(req->hdr.cmd);
case LEDBD_CMD_UNK_F0:
return ledbd_req_unkF0(req->hdr.cmd);
case LEDBD_CMD_UNK_30:
return ledbd_req_noop(req->hdr.cmd);
default:
//dprintf("Unhandled command 0x%02X\n", req->cmd);
return ledbd_req_noop(req->hdr.cmd);
}
}
static HRESULT ledbd_req_noop(uint8_t cmd)
{
//dprintf("LED Board: Noop cmd 0x%02X\n", cmd);
uint8_t resp[] = { 0xE0, 0x01, 0x11, 0x03, 0x01, 0x00, 0x01, 0x17 };
resp[5] = cmd;
resp[7] = 0x17 + cmd;
iobuf_write(&ledbd_uart.readable, resp, 8);
return S_OK;
}
static HRESULT ledbd_req_unk7c(uint8_t cmd)
{
//dprintf("LED Board: Cmd 0x7C\n");
uint8_t resp[] = { 0xE0, 0x01, 0x11, 0x04, 0x01, 0x7C, 0x01, 0x07, 0x9B };
iobuf_write(&ledbd_uart.readable, resp, 9);
return S_OK;
}
static HRESULT ledbd_req_unkF0(uint8_t cmd)
{
//dprintf("LED Board: Cmd 0xF0\n");
uint8_t resp[] = { 0xE0, 0x01, 0x11, 0x0A, 0x01, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E };
iobuf_write(&ledbd_uart.readable, resp, 16);
return S_OK;
}

28
carolhook/ledbd.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct ledbd_config {
bool enable;
};
enum ledbd_cmd {
LEDBD_CMD_UNK_10 = 0x10,
LEDBD_CMD_UNK_7C = 0x7C,
LEDBD_CMD_UNK_30 = 0x30,
LEDBD_CMD_UNK_F0 = 0xF0,
};
struct ledbd_req {
uint8_t sync; // Sync byte, always 0xE0
uint8_t dest; // command destination id?
uint8_t src; // command source id?
uint8_t data_len; // length of the proceeding data bytes
uint8_t cmd; // might be the command byte?
uint8_t data[255]; // rest of the data, len = data_len - 1
uint8_t checksum; // final byte is all bytes (excluding sync) added
};
HRESULT ledbd_hook_init(const struct ledbd_config *cfg);

View File

@ -4,7 +4,6 @@ shared_library(
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'carolhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
@ -32,7 +31,7 @@ shared_library(
'touch.h',
'controlbd.c',
'controlbd.h',
'serial.c',
'serial.h',
'ledbd.c',
'ledbd.h',
],
)

View File

@ -1,39 +0,0 @@
#include <windows.h>
#include <winbase.h>
#include "hook/table.h"
#include "util/dprintf.h"
static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB);
static BOOL (WINAPI *next_SetCommState)(HANDLE hFile, LPDCB lpDCB);
static void com_hook_insert_hooks(HMODULE target);
static const struct hook_symbol win32_hooks[] = {
{
.name = "SetCommState",
.patch = my_SetCommState,
.link = (void **) &next_SetCommState
}
};
void serial_init()
{
com_hook_insert_hooks(NULL);
dprintf("Serial: Spy init\n");
}
static void com_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
}
static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB)
{
dprintf("Serial: my_SetCommState with baudrate %ld\n", lpDCB->BaudRate);
return next_SetCommState(hFile, lpDCB);
}

View File

@ -1,5 +0,0 @@
#pragma once
#include <windows.h>
#include <winbase.h>
void serial_init();

View File

@ -8,26 +8,53 @@
#include "carolhook/carol-dll.h"
#include "carolhook/touch.h"
#include "hook/table.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
/**
* CMDS for touch thing
* CX -> Calibrate Extend, preform callibration
* MS -> Mode Stream, enters stream mode
* R -> Reset, resets the device
* RD -> Reset Default, Resets the device to factory
* Z -> Null, keepalive command
* NM -> Name, return the name of the device (not documented?)
* OI -> Output Identity, output unique device identity, SC followed by 4 characters
* UT -> Unit Type, returns controller unit type + status
*/
static HRESULT touch_handle_irp(struct irp *irp);
static HRESULT touch_handle_irp_locked(struct irp *irp);
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf);
static HRESULT handle_touch_ack_cmd(const struct touch_req *req);
static HRESULT handle_touch_name_cmd(const struct touch_req *req);
static HRESULT handle_touch_id_cmd(const struct touch_req *req);
static HRESULT handle_touch_unit_type_cmd(const struct touch_req *req);
static void touch_scan_auto(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y);
static CRITICAL_SECTION touch_lock;
static struct uart touch_uart;
static uint8_t touch_written_bytes[520];
static uint8_t touch_readable_bytes[520];
static uint8_t touch_written_bytes[528];
static uint8_t touch_readable_bytes[528];
static bool should_stream = false;
static bool last_pressed;
static uint8_t last_x1;
static uint8_t last_x2;
static uint8_t last_y1;
static uint8_t last_y2;
HRESULT touch_hook_init(const struct touch_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&touch_lock);
uart_init(&touch_uart, 1);
@ -69,9 +96,10 @@ static HRESULT touch_handle_irp_locked(struct irp *irp)
if (irp->op == IRP_OP_OPEN) {
dprintf("Touchscreen: Starting backend DLL\n");
hr = carol_dll.touch_init();
carol_dll.touch_start(touch_scan_auto);
if (FAILED(hr)) {
dprintf("Touchscreen: Backend DLL error: %x\n", (int) hr);
dprintf("Touchscreen: Backend DLL error: %X\n", (int) hr);
return hr;
}
@ -84,35 +112,142 @@ static HRESULT touch_handle_irp_locked(struct irp *irp)
}
for (;;) {
#if 1
#if defined(LOG_CAROL_TOUCH)
dprintf("Touchscreen: TX Buffer:\n");
dump_iobuf(&touch_uart.written);
#endif
hr = touch_frame_decode(&req, &touch_uart.written);
if (FAILED(hr)) {
dprintf("Touchscreen: Deframe Error: %x\n", (int) hr);
dprintf("Touchscreen: Deframe Error: %X\n", (int) hr);
return hr;
}
if (!strcmp("Z", (char *)req.cmd)) {
hr = handle_touch_ack_cmd(&req);
}
else if (!strcmp("OI", (char *)req.cmd)) {
hr = handle_touch_id_cmd(&req);
}
else if (!strcmp("UT", (char *)req.cmd)) {
hr = handle_touch_unit_type_cmd(&req);
}
else if (!strcmp("NM", (char *)req.cmd)) {
hr = handle_touch_name_cmd(&req);
}
else if (!strcmp("R", (char *)req.cmd)) {
carol_dll.touch_stop();
dprintf("Touch: Reset\n");
hr = handle_touch_ack_cmd(&req);
}
else {
dprintf("Touchscreen: Unhandled cmd %s\n", (char *)req.cmd);
hr = handle_touch_ack_cmd(&req);
}
return hr;
}
}
static HRESULT handle_touch_ack_cmd(const struct touch_req *req)
{
return iobuf_write(&touch_uart.readable, "\0010\015", 3);
}
static HRESULT handle_touch_name_cmd(const struct touch_req *req)
{
dprintf("Touch: Get Name\n");
return iobuf_write(&touch_uart.readable, "\001AD1000\015", 15);
}
static HRESULT handle_touch_id_cmd(const struct touch_req *req)
{
dprintf("Touch: Get ID\n");
return iobuf_write(&touch_uart.readable, "\001AD1000\015", 8);
}
static HRESULT handle_touch_unit_type_cmd(const struct touch_req *req)
{
dprintf("Touch: Get Unit Type\n");
return iobuf_write(&touch_uart.readable, "\001AD****00\015", 8);
}
static void touch_scan_auto(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y)
{
struct touch_auto_resp resp;
uint16_t tmp_x;
uint16_t tmp_y;
bool flg = false;
memset(&resp, 0, sizeof(resp));
resp.touches[0].status |= 1 << 7;
if (is_pressed) {
resp.touches[0].status |= (1 << 7) | (1 << 6);
resp.touches[0].touch_id = 1;
tmp_x = mouse_x & 0x7FFF;
tmp_y = mouse_y & 0x7FFF;
resp.touches[0].x1 = tmp_x & 0x7F;
resp.touches[0].x2 = (tmp_x >> 7) & 0x7F;
resp.touches[0].y1 = tmp_y & 0x7F;
resp.touches[0].y2 = (tmp_y >> 7) & 0x7F;
flg = resp.touches[0].x1 != last_x1 || resp.touches[0].x2 != last_x2 || resp.touches[0].y1 != last_y1 || resp.touches[0].y2 != last_y2;
#if 0
if (flg)
dprintf("Touch: Mouse down! x %02X %02X y: %02X %02X\n", resp.touches[0].x1, resp.touches[0].x2, resp.touches[0].y1, resp.touches[0].y2);
#endif
last_x1 = resp.touches[0].x1;
last_x2 = resp.touches[0].x2;
last_y1 = resp.touches[0].y1;
last_y2 = resp.touches[0].y2;
} else if (last_pressed) {
resp.touches[0].x1 = last_x1;
resp.touches[0].x2 = last_x2;
resp.touches[0].y1 = last_y1;
resp.touches[0].y2 = last_y2;
}
last_pressed = is_pressed;
EnterCriticalSection(&touch_lock);
iobuf_write(&touch_uart.readable, &resp, sizeof(resp));
LeaveCriticalSection(&touch_lock);
#if defined(LOG_CAROL_TOUCH)
dprintf("Touch: RX Buffer: (pos %08x)\n", (uint32_t)touch_uart.readable.pos);
dump_iobuf(&touch_uart.readable);
#endif
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf)
{
dest->cmd = iobuf->bytes[0];
iobuf->pos--;
dest->data_length = iobuf->pos;
dest->sync = iobuf->bytes[0];
memset(dest->cmd, 0, sizeof(dest->cmd));
size_t data_len = 0;
if (dest->data_length > 0) {
for (int i = 1; i < dest->data_length; i++) {
dest->data[i-1] = iobuf->bytes[i];
}
for (int i = 1; i < 255; i++) {
if (iobuf->bytes[i] == 0x0D) { break; }
dest->cmd[i - 1] = iobuf->bytes[i];
data_len++;
}
dest->tail = iobuf->bytes[data_len + 1];
dest->data_len = data_len;
iobuf->pos = 0;
if (dest->sync != 1 || dest->tail != 0x0D) {
dprintf("Touch: Data recieve error, sync: 0x%02X (expected 0x01) tail: 0x%02X (expected 0x0D)", dest->sync, dest->tail);
return E_FAIL;
}
iobuf->pos -= dest->data_length;
return S_OK;
}
}

View File

@ -4,14 +4,42 @@
#include <stdbool.h>
#include <stdint.h>
#pragma pack(push, 1)
struct touch_config {
bool enable;
unsigned int port_no;
char board_id[7];
char unit_type[9];
};
// Always starts with 0x01, always ends with 0x0D
struct touch_req {
uint8_t cmd; // First byte is the command byte
uint8_t data[256]; // rest of the data goes here
uint8_t data_length; // Size of the data including command byte
uint8_t sync; // Always 0x01
uint8_t cmd[256]; // rest of the data goes here
uint8_t tail; // Always 0x0D
size_t data_len; // length of data
};
struct touch_report {
uint8_t status;
uint8_t x1;
uint8_t x2;
uint8_t y1;
uint8_t y2;
uint8_t touch_id;
};
struct touch_auto_resp {
struct touch_report touches[10];
};
enum {
TOUCH_MODE_STREAM = 0x01,
TOUCH_MODE_DOWN_UP = 0x02,
TOUCH_MODE_INACTIVE = 0x03,
};
#pragma pack(pop)
HRESULT touch_hook_init(const struct touch_config *cfg);

View File

@ -7,10 +7,17 @@
#include "carolio/carolio.h"
#include "carolio/config.h"
#include "util/dprintf.h"
#include "util/env.h"
static unsigned int __stdcall carol_io_touch_thread_proc(void *ctx);
static bool carol_io_coin;
static uint16_t carol_io_coins;
static struct carol_io_config carol_io_cfg;
static bool carol_io_touch_stop_flag;
static HANDLE carol_io_touch_thread;
static bool carol_io_window_focus = false;
uint16_t carol_io_get_api_version(void)
{
@ -19,7 +26,7 @@ uint16_t carol_io_get_api_version(void)
HRESULT carol_io_jvs_init(void)
{
carol_io_config_load(&carol_io_cfg, L".\\segatools.ini");
carol_io_config_load(&carol_io_cfg, get_config_path());
return S_OK;
}
@ -73,7 +80,103 @@ HRESULT carol_io_touch_init()
return S_OK;
}
HRESULT carol_io_ledbd_init()
{
return S_OK;
}
HRESULT carol_io_controlbd_init()
{
return S_OK;
}
}
void carol_io_touch_start(carol_io_touch_callback_t callback)
{
if (carol_io_touch_thread != NULL) {
return;
}
carol_io_touch_stop_flag = false;
carol_io_touch_thread = (HANDLE) _beginthreadex(
NULL,
0,
carol_io_touch_thread_proc,
callback,
0,
NULL
);
}
void carol_io_touch_stop()
{
carol_io_touch_stop_flag = true;
}
void check_fg_wind(void)
{
HWND hwnd = GetForegroundWindow();
wchar_t window_class[MAX_PATH];
/* Unlike every other game, we can't use GetWindowText here. Why?
From MSDN:
"If the window does not have a caption, the return value is a null
string. This behavior is by design. It allows applications to call
GetWindowText without becoming unresponsive if the process that owns
the target window is not responding. However, if the target window
is not responding and it belongs to the calling application,
GetWindowText will cause the calling application to become
unresponsive."
Great, thanks Microsoft, very cool. Luckily Carol sets its class
name to the window title too so we can use that. */
GetClassNameW(hwnd, window_class, MAX_PATH);
if (wcscmp(window_class, L"WONDER Master")) {
if (carol_io_window_focus) {
dprintf("Carol IO: Window focus lost\n");
carol_io_window_focus = false;
}
} else if (!carol_io_window_focus) {
dprintf("Carol IO: Window focus regained\n");
carol_io_window_focus = true;
}
}
static unsigned int __stdcall carol_io_touch_thread_proc(void *ctx)
{
carol_io_touch_callback_t callback;
bool mouse_is_down = false;
uint16_t mX = 0;
uint16_t mY = 0;
POINT lpPoint;
HWND hwnd;
callback = ctx;
while (!carol_io_touch_stop_flag) {
check_fg_wind();
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) {
mouse_is_down = true;
if (GetCursorPos(&lpPoint)) {
hwnd = GetForegroundWindow();
if (ScreenToClient(hwnd, &lpPoint)) {
if (lpPoint.x < 0) lpPoint.x = 0;
if (lpPoint.y < 0) lpPoint.y = 0;
mX = (uint16_t)lpPoint.x;
mY = (uint16_t)lpPoint.y;
}
}
} else {
mouse_is_down = false;
}
callback(mouse_is_down, mX, mY);
Sleep(1);
}
return 0;
}

View File

@ -5,6 +5,8 @@
#include <stdbool.h>
#include <stdint.h>
typedef void (*carol_io_touch_callback_t)(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y);
/* Get the version of the Project carol IO API that this DLL supports. This
function should return a positive 16-bit integer, where the high byte is
the major version and the low byte is the minor version (as defined by the
@ -49,4 +51,10 @@ void carol_io_jvs_read_coin_counter(uint16_t *out);
HRESULT carol_io_touch_init();
HRESULT carol_io_controlbd_init();
HRESULT carol_io_ledbd_init();
HRESULT carol_io_controlbd_init();
void carol_io_touch_start(carol_io_touch_callback_t callback);
void carol_io_touch_stop();

View File

@ -14,7 +14,7 @@ void carol_io_config_load(
assert(cfg != NULL);
assert(filename != NULL);
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename);
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename);
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", VK_F1, filename);
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", VK_F2, filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", VK_F3, filename);
}

View File

@ -3,7 +3,6 @@ carolio_lib = static_library(
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'carolio.c',
'carolio.h',

View File

@ -30,9 +30,28 @@ const struct dll_bind_sym chuni_dll_syms[] = {
}, {
.sym = "chuni_io_slider_set_leds",
.off = offsetof(struct chuni_dll, slider_set_leds),
}, {
.sym = "chuni_io_led_init",
.off = offsetof(struct chuni_dll, led_init),
}, {
.sym = "chuni_io_led_set_colors",
.off = offsetof(struct chuni_dll, led_set_leds),
}
};
/* Helper function to determine upon dll_bind failure whether the required functions were found
NOTE: relies on symbols order declared above */
static HRESULT has_enough_symbols(uint16_t version, uint8_t count)
{
if ( version <= 0x0101 && count == 7 )
return S_OK;
if ( version >= 0x0102 && count == 9 )
return S_OK;
return E_FAIL;
}
struct chuni_dll chuni_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
@ -92,16 +111,24 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
}
sym = chuni_dll_syms;
const struct dll_bind_sym *init_sym = &sym[0];
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Chunithm IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
// Might still be ok depending on external dll API version
int bind_count = sym - init_sym;
if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
{
hr = S_OK;
} else {
dprintf("Chunithm IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
goto end;
}
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}

View File

@ -13,6 +13,8 @@ struct chuni_dll {
void (*slider_start)(chuni_io_slider_callback_t callback);
void (*slider_stop)(void);
void (*slider_set_leds)(const uint8_t *rgb);
HRESULT (*led_init)(void);
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
};
struct chuni_dll_config {

View File

@ -20,3 +20,5 @@ EXPORTS
chuni_io_slider_set_leds
chuni_io_slider_start
chuni_io_slider_stop
chuni_io_led_init
chuni_io_led_set_colors

View File

@ -3,6 +3,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "amex/amex.h"
#include "amex/config.h"
@ -43,6 +44,67 @@ void slider_config_load(struct slider_config *cfg, const wchar_t *filename)
cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename);
}
void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t tmpstr[16];
memset(cfg->board_number, ' ', sizeof(cfg->board_number));
memset(cfg->chip_number, ' ', sizeof(cfg->chip_number));
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
cfg->port_no[0] = GetPrivateProfileIntW(L"led15093", L"portNo1", 0, filename);
cfg->port_no[1] = GetPrivateProfileIntW(L"led15093", L"portNo2", 0, filename);
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xadf7, filename);
GetPrivateProfileStringW(
L"led15093",
L"boardNumber",
L"15093-06",
tmpstr,
_countof(tmpstr),
filename);
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
for (int i = n; i < sizeof(cfg->board_number); i++)
{
cfg->board_number[i] = ' ';
}
GetPrivateProfileStringW(
L"led15093",
L"chipNumber",
L"6710 ",
tmpstr,
_countof(tmpstr),
filename);
n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number));
for (int i = n; i < sizeof(cfg->chip_number); i++)
{
cfg->chip_number[i] = ' ';
}
GetPrivateProfileStringW(
L"led15093",
L"bootChipNumber",
L"6709 ",
tmpstr,
_countof(tmpstr),
filename);
n = wcstombs(cfg->boot_chip_number, tmpstr, sizeof(cfg->boot_chip_number));
for (int i = n; i < sizeof(cfg->boot_chip_number); i++)
{
cfg->boot_chip_number[i] = ' ';
}
}
void chuni_hook_config_load(
struct chuni_hook_config *cfg,
const wchar_t *filename)
@ -58,4 +120,5 @@ void chuni_hook_config_load(
gfx_config_load(&cfg->gfx, filename);
chuni_dll_config_load(&cfg->dll, filename);
slider_config_load(&cfg->slider, filename);
led15093_config_load(&cfg->led15093, filename);
}

View File

@ -6,6 +6,7 @@
#include "amex/amex.h"
#include "board/sg-reader.h"
#include "board/led15093.h"
#include "chunihook/chuni-dll.h"
#include "chunihook/slider.h"
@ -21,6 +22,7 @@ struct chuni_hook_config {
struct gfx_config gfx;
struct chuni_dll_config dll;
struct slider_config slider;
struct led15093_config led15093;
};
void chuni_dll_config_load(

View File

@ -1,9 +1,22 @@
/*
"CHUNITHM" (chuni) hook
Devices
JVS: 837-14572 "Type 3" I/O Board
COM1: 837-15330 Ground Slider
COM10: 837-15093-06 LED Controller Board
COM11: 837-15093-06 LED Controller Board
COM12: TN32MSEC003S "Gen 1" Aime Reader
*/
#include <windows.h>
#include <stdlib.h>
#include "amex/amex.h"
#include "board/led15093.h"
#include "board/sg-reader.h"
#include "chunihook/config.h"
@ -23,6 +36,7 @@
#include "platform/platform.h"
#include "util/dprintf.h"
#include "util/env.h"
static HMODULE chuni_hook_mod;
static process_entry_t chuni_startup;
@ -58,7 +72,7 @@ static DWORD CALLBACK chuni_pre_startup(void)
/* Config load */
chuni_hook_config_load(&chuni_hook_cfg, L".\\segatools.ini");
chuni_hook_config_load(&chuni_hook_cfg, get_config_path());
/* Hook Win32 APIs */
@ -96,7 +110,20 @@ static DWORD CALLBACK chuni_pre_startup(void)
goto fail;
}
hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, chuni_hook_mod);
if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
{
dprintf("IO DLL doesn't support led_init/led_set_leds, cannot start LED15093 hook\n");
} else {
unsigned int led_port_no[2] = {10, 11};
hr = led15093_hook_init(&chuni_hook_cfg.led15093,
chuni_dll.led_init, chuni_dll.led_set_leds, led_port_no);
if (FAILED(hr)) {
goto fail;
}
}
hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, 1, chuni_hook_mod);
if (FAILED(hr)) {
goto fail;
@ -104,7 +131,7 @@ static DWORD CALLBACK chuni_pre_startup(void)
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
spike_hook_init(get_config_path());
dprintf("--- End chuni_pre_startup ---\n");

View File

@ -101,13 +101,13 @@ static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out)
out->p1 = 0x0000;
out->p2 = 0x0000;
if (opbtn & 0x01) {
if (opbtn & CHUNI_IO_OPBTN_TEST) {
out->system = 0x80;
} else {
out->system = 0x00;
}
if (opbtn & 0x02) {
if (opbtn & CHUNI_IO_OPBTN_SERVICE) {
out->p1 |= 0x4000;
}

View File

@ -4,7 +4,6 @@ shared_library(
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'chunihook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),

View File

@ -98,7 +98,7 @@ static HRESULT slider_handle_irp_locked(struct irp *irp)
}
for (;;) {
#if 0
#if defined(LOG_CHUNI_SLIDER)
dprintf("TX Buffer:\n");
dump_iobuf(&slider_uart.written);
#endif
@ -117,7 +117,7 @@ static HRESULT slider_handle_irp_locked(struct irp *irp)
return hr;
}
#if 0
#if defined(LOG_CHUNI_SLIDER)
dprintf("Deframe Buffer:\n");
dump_iobuf(&req_iobuf);
#endif

358
chuniio/chu2to3.c Normal file
View File

@ -0,0 +1,358 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "chuniio/chu2to3.h"
#include "util/dprintf.h"
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
/* chuniio.dll dynamic loading */
HMODULE hinstLib;
typedef uint16_t (*chuni_io_get_api_version_t)(void);
typedef HRESULT (*chuni_io_jvs_init_t)(void);
typedef void (*chuni_io_jvs_poll_t)(uint8_t*, uint8_t*);
typedef void (*chuni_io_jvs_read_coin_counter_t)(uint16_t *);
typedef HRESULT (*chuni_io_slider_init_t)(void);
typedef void (*chuni_io_slider_set_leds_t)(const uint8_t *);
typedef void (*chuni_io_slider_start_t)(chuni_io_slider_callback_t);
typedef void (*chuni_io_slider_stop_t)(void);
typedef HRESULT (*chuni_io_led_init_t)(void);
typedef void (*chuni_io_led_set_colors_t)(uint8_t, uint8_t *);
chuni_io_get_api_version_t _chuni_io_get_api_version;
chuni_io_jvs_init_t _chuni_io_jvs_init;
chuni_io_jvs_poll_t _chuni_io_jvs_poll;
chuni_io_jvs_read_coin_counter_t _chuni_io_jvs_read_coin_counter;
chuni_io_slider_init_t _chuni_io_slider_init;
chuni_io_slider_set_leds_t _chuni_io_slider_set_leds;
chuni_io_slider_start_t _chuni_io_slider_start;
chuni_io_slider_stop_t _chuni_io_slider_stop;
chuni_io_led_init_t _chuni_io_led_init;
chuni_io_led_set_colors_t _chuni_io_led_set_colors;
/* SHMEM Handling */
#define BUF_SIZE 1024
#define SHMEM_WRITE(buf, size) CopyMemory((PVOID)g_pBuf, buf, size)
#define SHMEM_READ(buf, size) CopyMemory(buf,(PVOID)g_pBuf, size)
TCHAR g_shmem_name[]=TEXT("Local\\Chu2to3Shmem");
HANDLE g_hMapFile;
LPVOID g_pBuf;
#pragma pack(1)
typedef struct shared_data_s {
uint16_t coin_counter;
uint8_t opbtn;
uint8_t beams;
uint16_t version;
} shared_data_t;
shared_data_t g_shared_data;
bool shmem_create()
{
g_hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
g_shmem_name); // name of mapping object
if (g_hMapFile == NULL)
{
dprintf("shmem_create : Could not create file mapping object (%ld).\n",
GetLastError());
return 0;
}
g_pBuf = MapViewOfFile(g_hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (g_pBuf == NULL)
{
dprintf("shmem_create : Could not map view of file (%ld).\n",
GetLastError());
CloseHandle(g_hMapFile);
return 0;
}
return 1;
}
bool shmem_load()
{
g_hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
g_shmem_name); // name of mapping object
if (g_hMapFile == NULL)
{
dprintf("shmem_load : Could not open file mapping object (%ld).\n", GetLastError());
return 0;
}
g_pBuf = MapViewOfFile(g_hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (g_pBuf == NULL)
{
dprintf("shmem_load : Could not map view of file (%ld).\n", GetLastError());
CloseHandle(g_hMapFile);
return 0;
}
dprintf("shmem_load : shmem loaded succesfully.\n");
return 1;
}
void shmem_free()
{
UnmapViewOfFile(g_pBuf);
CloseHandle(g_hMapFile);
}
/* jvs polling thread (to forward info to x64 dll) */
static HANDLE jvs_poll_thread;
static bool jvs_poll_stop_flag;
static unsigned int __stdcall jvs_poll_thread_proc(void *ctx)
{
while (1) {
_chuni_io_jvs_read_coin_counter(&g_shared_data.coin_counter);
g_shared_data.opbtn = 0;
g_shared_data.beams = 0;
_chuni_io_jvs_poll(&g_shared_data.opbtn, &g_shared_data.beams);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
Sleep(1);
}
return 0;
}
uint16_t chu2to3_load_dll(const wchar_t *dllname)
{
#if defined(ENV64BIT)
/* x64 must just open the shmem and do nothing else */
int errcount = 0;
while (!shmem_load())
{
if (errcount >= 10)
return -1;
Sleep(5000);
errcount++;
}
Sleep(1000);
return S_OK;
#endif
/* this is the first function called so let's setup the chuniio forwarding */
hinstLib = LoadLibraryW(dllname);
if (hinstLib == NULL) {
dprintf("ERROR: unable to load %S (error %ld)\n",dllname, GetLastError());
return -1;
}
_chuni_io_get_api_version = (chuni_io_get_api_version_t)GetProcAddress(hinstLib, "chuni_io_get_api_version");
_chuni_io_jvs_init = (chuni_io_jvs_init_t)GetProcAddress(hinstLib, "chuni_io_jvs_init");
_chuni_io_jvs_poll = (chuni_io_jvs_poll_t)GetProcAddress(hinstLib, "chuni_io_jvs_poll");
_chuni_io_jvs_read_coin_counter = (chuni_io_jvs_read_coin_counter_t)GetProcAddress(hinstLib, "chuni_io_jvs_read_coin_counter");
_chuni_io_slider_init = (chuni_io_slider_init_t)GetProcAddress(hinstLib, "chuni_io_slider_init");
_chuni_io_slider_set_leds = (chuni_io_slider_set_leds_t)GetProcAddress(hinstLib, "chuni_io_slider_set_leds");
_chuni_io_slider_start = (chuni_io_slider_start_t)GetProcAddress(hinstLib, "chuni_io_slider_start");
_chuni_io_slider_stop = (chuni_io_slider_stop_t)GetProcAddress(hinstLib, "chuni_io_slider_stop");
_chuni_io_led_init = (chuni_io_led_init_t)GetProcAddress(hinstLib, "chuni_io_led_init");
_chuni_io_led_set_colors = (chuni_io_led_set_colors_t)GetProcAddress(hinstLib, "chuni_io_led_set_colors");
/* x86 has to create the shmem */
if (!shmem_create())
{
return -1;
}
return 0;
}
/* chuniio exports */
uint16_t chu2to3_io_get_api_version(void)
{
#if defined(ENV64BIT)
/* This might be called too soon so let's make sure x86 has time to write to the shmem */
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
int errcount = 0;
while (g_shared_data.version == 0)
{
if (errcount >= 3)
{
dprintf("CHU2TO3 X64: Couldn't retrieve api version from shmem, assuming 0x0100\n");
return 0x0100;
}
Sleep(5000);
errcount++;
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
}
dprintf("CHU2TO3 X64: api version is %04X\n", g_shared_data.version);
return g_shared_data.version;
#endif
if ( _chuni_io_get_api_version == NULL )
{
g_shared_data.version = 0x0100;
}
else
{
g_shared_data.version = _chuni_io_get_api_version();
}
dprintf("CHU2TO3: api version is %04X\n", g_shared_data.version);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
return g_shared_data.version;
}
HRESULT chu2to3_io_jvs_init(void)
{
#if defined(ENV64BIT)
/* x86 only */
return S_OK;
#endif
_chuni_io_jvs_init();
/* start jvs poll thread now that jvs_init is done */
if (jvs_poll_thread != NULL) {
return S_OK;
}
jvs_poll_thread = (HANDLE) _beginthreadex(NULL,
0,
jvs_poll_thread_proc,
NULL,
0,
NULL);
return S_OK;
}
void chu2to3_io_jvs_read_coin_counter(uint16_t *out)
{
#if defined(ENV32BIT)
/* x86 can perform the call and update shmem (although this call never happens) */
_chuni_io_jvs_read_coin_counter(&g_shared_data.coin_counter);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
return;
#endif
/* x64 must read value from shmem and update arg */
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
if (out == NULL) {
return;
}
*out = g_shared_data.coin_counter;
}
void chu2to3_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
{
#if defined(ENV32BIT)
/* x86 can perform the call and update shmem (although this call never happens) */
_chuni_io_jvs_poll(&g_shared_data.opbtn, &g_shared_data.beams);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
return;
#endif
/* x64 must read value from shmem and update args */
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
*opbtn = g_shared_data.opbtn;
*beams = g_shared_data.beams;
}
HRESULT chu2to3_io_slider_init(void)
{
#if defined(ENV64BIT)
/* x86 only */
return S_OK;
#endif
return _chuni_io_slider_init();
}
void chu2to3_io_slider_start(chuni_io_slider_callback_t callback)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
_chuni_io_slider_start(callback);
}
void chu2to3_io_slider_stop(void)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
_chuni_io_slider_stop();
}
void chu2to3_io_slider_set_leds(const uint8_t *rgb)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
_chuni_io_slider_set_leds(rgb);
}
HRESULT chu2to3_io_led_init(void)
{
#if defined(ENV64BIT)
/* x86 only */
return S_OK;
#endif
if (_chuni_io_led_init != NULL)
return _chuni_io_led_init();
return S_OK;
}
void chu2to3_io_led_set_colors(uint8_t board, uint8_t *rgb)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
if (_chuni_io_led_set_colors != NULL)
{
_chuni_io_led_set_colors(board, rgb);
}
}

26
chuniio/chu2to3.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
/*
CHU2TO3 CUSTOM IO API
This dll just mirrors chuniio dll binds but with a dynamic library loading and
a SHMEM system to let a single 32bit dll talk with x86 and x64 processes at once
*/
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
uint16_t chu2to3_io_get_api_version(void);
HRESULT chu2to3_io_jvs_init(void);
void chu2to3_io_jvs_poll(uint8_t *opbtn, uint8_t *beams);
void chu2to3_io_jvs_read_coin_counter(uint16_t *total);
HRESULT chu2to3_io_slider_init(void);
typedef void (*chuni_io_slider_callback_t)(const uint8_t *state);
void chu2to3_io_slider_start(chuni_io_slider_callback_t callback);
void chu2to3_io_slider_stop(void);
void chu2to3_io_slider_set_leds(const uint8_t *rgb);
HRESULT chu2to3_io_led_init(void);
void chu2to3_io_led_set_colors(uint8_t board, uint8_t *rgb);
uint16_t chu2to3_load_dll(const wchar_t *dllname);

View File

@ -3,9 +3,15 @@
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "chuniio/chuniio.h"
#include "chuniio/config.h"
#include "chuniio/ledoutput.h"
#include "util/dprintf.h"
#include "util/env.h"
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx);
@ -18,13 +24,23 @@ static struct chuni_io_config chuni_io_cfg;
uint16_t chuni_io_get_api_version(void)
{
return 0x0101;
return 0x0102;
}
HRESULT chuni_io_jvs_init(void)
{
chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini");
chuni_io_config_load(&chuni_io_cfg, get_config_path());
led_init_mutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (led_init_mutex == NULL)
{
return E_FAIL;
}
return S_OK;
}
@ -34,7 +50,7 @@ void chuni_io_jvs_read_coin_counter(uint16_t *out)
return;
}
if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) {
if (GetAsyncKeyState(chuni_io_cfg.vk_coin) & 0x8000) {
if (!chuni_io_coin) {
chuni_io_coin = true;
chuni_io_coins++;
@ -50,38 +66,52 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
{
size_t i;
if (GetAsyncKeyState(chuni_io_cfg.vk_test)) {
*opbtn |= 0x01; /* Test */
if (GetAsyncKeyState(chuni_io_cfg.vk_test) & 0x8000) {
*opbtn |= CHUNI_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(chuni_io_cfg.vk_service)) {
*opbtn |= 0x02; /* Service */
if (GetAsyncKeyState(chuni_io_cfg.vk_service) & 0x8000) {
*opbtn |= CHUNI_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(chuni_io_cfg.vk_ir)) {
if (chuni_io_hand_pos < 6) {
chuni_io_hand_pos++;
if (chuni_io_cfg.vk_ir_emu) {
// Use emulated AIR
if (GetAsyncKeyState(chuni_io_cfg.vk_ir_emu)) {
if (chuni_io_hand_pos < 6) {
chuni_io_hand_pos++;
}
} else {
if (chuni_io_hand_pos > 0) {
chuni_io_hand_pos--;
}
}
for (i = 0 ; i < 6 ; i++) {
if (chuni_io_hand_pos > i) {
*beams |= (1 << i);
}
}
} else {
if (chuni_io_hand_pos > 0) {
chuni_io_hand_pos--;
}
}
for (i = 0 ; i < 6 ; i++) {
if (chuni_io_hand_pos > i) {
*beams |= (1 << i);
// Use actual AIR
for (i = 0; i < 6; i++) {
if (GetAsyncKeyState(chuni_io_cfg.vk_ir[i]) & 0x8000) {
*beams |= (1 << i);
} else {
*beams &= ~(1 << i);
}
}
}
}
HRESULT chuni_io_slider_init(void)
{
return S_OK;
return led_output_init(&chuni_io_cfg); // because of slider LEDs
}
void chuni_io_slider_start(chuni_io_slider_callback_t callback)
{
BOOL status;
if (chuni_io_slider_thread != NULL) {
return;
}
@ -111,6 +141,7 @@ void chuni_io_slider_stop(void)
void chuni_io_slider_set_leds(const uint8_t *rgb)
{
led_output_update(2, rgb);
}
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
@ -136,3 +167,27 @@ static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
return 0;
}
HRESULT chuni_io_led_init(void)
{
return led_output_init(&chuni_io_cfg);
}
void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb)
{
#if 0
if (board == 0) {
dprintf("CHUNI LED: Left Air 1: red: %d, green: %d, blue: %d\n", rgb[0x96], rgb[0x97], rgb[0x98]);
dprintf("CHUNI LED: Left Air 2: red: %d, green: %d, blue: %d\n", rgb[0x99], rgb[0x9A], rgb[0x9B]);
dprintf("CHUNI LED: Left Air 3: red: %d, green: %d, blue: %d\n", rgb[0x9C], rgb[0x9D], rgb[0x9E]);
}
else if (board == 1)
{
dprintf("CHUNI LED: Right Air 1: red: %d, green: %d, blue: %d\n", rgb[0xB4], rgb[0xB5], rgb[0xB6]);
dprintf("CHUNI LED: Right Air 2: red: %d, green: %d, blue: %d\n", rgb[0xB7], rgb[0xB8], rgb[0xB9]);
dprintf("CHUNI LED: Right Air 3: red: %d, green: %d, blue: %d\n", rgb[0xBA], rgb[0xBB], rgb[0xBC]);
}
#endif
led_output_update(board, rgb);
}

View File

@ -8,6 +8,7 @@
- 0x0100: Initial API version (assumed if chuni_io_get_api_version is not
exported)
- 0x0101: Fix IR beam mappings
- 0x0102: Add air tower led and billboard support
*/
#include <windows.h>
@ -15,6 +16,12 @@
#include <stdbool.h>
#include <stdint.h>
enum {
CHUNI_IO_OPBTN_TEST = 0x01,
CHUNI_IO_OPBTN_SERVICE = 0x02,
CHUNI_IO_OPBTN_COIN = 0x04,
};
/* Get the version of the Chunithm IO API that this DLL supports. This
function should return a positive 16-bit integer, where the high byte is
the major version and the low byte is the minor version (as defined by the
@ -132,10 +139,46 @@ void chuni_io_slider_start(chuni_io_slider_callback_t callback);
void chuni_io_slider_stop(void);
/* Update the RGB lighting on the slider. A pointer to an array of 32 * 3 = 96
bytes is supplied. The illuminated areas on the touch slider are some
combination of rectangular regions and dividing lines between these regions
but the exact mapping of this lighting control buffer is still TBD.
bytes is supplied, organized in BRG format.
The first set of bytes is the right-most slider key, and from there the bytes
alternate between the dividers and the keys until the left-most key.
There are 31 illuminated sections in total.
Minimum API version: 0x0100 */
void chuni_io_slider_set_leds(const uint8_t *rgb);
/* Initialize LED emulation. This function will be called before any
other chuni_io_led_*() function calls.
All subsequent calls may originate from arbitrary threads and some may
overlap with each other. Ensuring synchronization inside your IO DLL is
your responsibility.
Minimum API version: 0x0102 */
HRESULT chuni_io_led_init(void);
/* Update the RGB LEDs. rgb is a pointer to an array of up to 63 * 3 = 189 bytes.
Chunithm uses two chains/boards with WS2811 protocol (each logical led corresponds to 3 physical leds).
board 0 is on the left side and board 1 on the right side of the cab
Board 0 has 53 LEDs:
[0]-[49]: snakes through left half of billboard (first column starts at top)
[50]-[52]: left side partition LEDs
Board 1 has 63 LEDs:
[0]-[59]: right half of billboard (first column starts at bottom)
[60]-[62]: right side partition LEDs
Board 2 is the slider and has 31 LEDs:
[0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
Each rgb value is comprised of 3 bytes in R,G,B order
NOTE: billboard strips have alternating direction (bottom to top, top to bottom, ...)
Minimum API version: 0x0102 */
void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb);

View File

@ -3,6 +3,8 @@
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "chuniio/config.h"
@ -17,20 +19,35 @@ static const int chuni_io_default_cells[] = {
'S', 'S', 'S', 'S',
};
static const int chuni_io_default_ir[] = {
'4', '5', '6', '7', '8', '9'
};
void chuni_io_config_load(
struct chuni_io_config *cfg,
const wchar_t *filename)
{
wchar_t key[16];
int i;
wchar_t port_input[6];
assert(cfg != NULL);
assert(filename != NULL);
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename);
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename);
cfg->vk_ir = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename);
// Technically it's io4 but leave this for compatibility with old configs.
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", VK_F1, filename);
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", VK_F2, filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", VK_F3, filename);
cfg->vk_ir_emu = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename);
for (i = 0 ; i < 6 ; i++) {
swprintf_s(key, _countof(key), L"ir%i", i + 1);
cfg->vk_ir[i] = GetPrivateProfileIntW(
L"ir",
key,
chuni_io_default_ir[i],
filename);
}
for (i = 0 ; i < 32 ; i++) {
swprintf_s(key, _countof(key), L"cell%i", i + 1);
@ -40,4 +57,27 @@ void chuni_io_config_load(
chuni_io_default_cells[i],
filename);
}
cfg->cab_led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
cfg->cab_led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
cfg->controller_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
cfg->controller_led_output_serial = GetPrivateProfileIntW(L"led", L"controllerLedOutputSerial", 0, filename);
cfg->controller_led_output_openithm = GetPrivateProfileIntW(L"led", L"controllerLedOutputOpeNITHM", 0, filename);
cfg->led_serial_baud = GetPrivateProfileIntW(L"led", L"serialBaud", 921600, filename);
GetPrivateProfileStringW(
L"led",
L"serialPort",
L"COM5",
port_input,
_countof(port_input),
filename);
// Sanitize the output path. If it's a serial COM port, it needs to be prefixed
// with `\\.\`.
wcsncpy(cfg->led_serial_port, L"\\\\.\\", 4);
wcsncat_s(cfg->led_serial_port, 11, port_input, 6);
}

View File

@ -2,13 +2,28 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct chuni_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_ir;
uint8_t vk_ir_emu;
uint8_t vk_ir[6];
uint8_t vk_cell[32];
// Which ways to output LED information are enabled
bool cab_led_output_pipe;
bool cab_led_output_serial;
bool controller_led_output_pipe;
bool controller_led_output_serial;
bool controller_led_output_openithm;
// The name of a COM port to output LED data on, in serial mode
wchar_t led_serial_port[12];
int32_t led_serial_baud;
};
void chuni_io_config_load(

22
chuniio/leddata.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#define LED_PACKET_FRAMING 0xE0
#define LED_PACKET_ESCAPE 0xD0
#define LED_NUM_MAX 66
#define LED_BOARDS_TOTAL 3
#define LED_OUTPUT_HEADER_SIZE 2
#define LED_OUTPUT_DATA_SIZE_MAX LED_NUM_MAX * 3 * 2 // max if every byte's escaped
#define LED_OUTPUT_TOTAL_SIZE_MAX LED_OUTPUT_HEADER_SIZE + LED_OUTPUT_DATA_SIZE_MAX
// This struct is used to send data related to the slider and billboard LEDs
struct _chuni_led_data_buf_t {
uint8_t framing; // Sync byte
uint8_t board; // LED output the data is for (0-1: billboard, 2: slider)
uint8_t data[LED_OUTPUT_DATA_SIZE_MAX]; // Buffer for LEDs
uint8_t data_len; // How many bytes to output from the buffer
};
static uint8_t chuni_led_board_data_lens[LED_BOARDS_TOTAL] = {53*3, 63*3, 31*3};

137
chuniio/ledoutput.c Normal file
View File

@ -0,0 +1,137 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/config.h"
#include "chuniio/leddata.h"
#include "chuniio/ledoutput.h"
#include "chuniio/pipeimpl.h"
#include "chuniio/serialimpl.h"
static struct _chuni_led_data_buf_t led_unescaped_buf[LED_BOARDS_TOTAL];
static struct _chuni_led_data_buf_t led_escaped_buf[LED_BOARDS_TOTAL];
static bool led_output_is_init = false;
static struct chuni_io_config* config;
static bool any_outputs_enabled;
HANDLE led_init_mutex;
HRESULT led_output_init(struct chuni_io_config* const cfg)
{
DWORD dwWaitResult = WaitForSingleObject(led_init_mutex, INFINITE);
if (dwWaitResult == WAIT_FAILED)
{
return HRESULT_FROM_WIN32(GetLastError());
// return 1;
}
else if (dwWaitResult != WAIT_OBJECT_0)
{
return E_FAIL;
// return 1;
}
if (!led_output_is_init)
{
config = cfg;
// Setup the framing bytes for the packets
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
led_unescaped_buf[i].framing = LED_PACKET_FRAMING;
led_unescaped_buf[i].board = i;
led_unescaped_buf[i].data_len = chuni_led_board_data_lens[i];
led_escaped_buf[i].framing = LED_PACKET_FRAMING;
led_escaped_buf[i].board = i;
led_escaped_buf[i].data_len = chuni_led_board_data_lens[i];
}
any_outputs_enabled = config->cab_led_output_pipe || config->controller_led_output_pipe
|| config->cab_led_output_serial || config->controller_led_output_serial;
if (config->cab_led_output_pipe || config->controller_led_output_pipe)
{
led_pipe_init(); // don't really care about errors here tbh
}
if (config->cab_led_output_serial || config->controller_led_output_serial)
{
led_serial_init(config->led_serial_port, config->led_serial_baud);
}
}
led_output_is_init = true;
ReleaseMutex(led_init_mutex);
return S_OK;
// return 0;
}
struct _chuni_led_data_buf_t* escape_led_data(struct _chuni_led_data_buf_t* unescaped)
{
struct _chuni_led_data_buf_t* out_struct = &led_escaped_buf[unescaped->board];
uint8_t* in_buf = unescaped->data;
uint8_t* out_buf = out_struct->data;
int i = 0;
int o = 0;
while (i < unescaped->data_len)
{
uint8_t b = in_buf[i++];
if (b == LED_PACKET_FRAMING || b == LED_PACKET_ESCAPE)
{
out_buf[o++] = LED_PACKET_ESCAPE;
b--;
}
out_buf[o++] = b;
}
out_struct->data_len = o;
return out_struct;
}
void led_output_update(uint8_t board, const uint8_t* rgb)
{
if (board < 0 || board > 2 || !any_outputs_enabled)
{
return;
}
memcpy(led_unescaped_buf[board].data, rgb, led_unescaped_buf[board].data_len);
struct _chuni_led_data_buf_t* escaped_data = escape_led_data(&led_unescaped_buf[board]);
if (board < 2)
{
// billboard (cab)
if (config->cab_led_output_pipe)
{
led_pipe_update(escaped_data);
}
if (config->cab_led_output_serial)
{
led_serial_update(escaped_data);
}
}
else
{
// slider
if (config->controller_led_output_pipe)
{
led_pipe_update(escaped_data);
}
if (config->controller_led_output_serial)
{
if (config->controller_led_output_openithm){
led_serial_update_openithm(rgb);
} else {
led_serial_update(escaped_data);
}
}
}
}

19
chuniio/ledoutput.h Normal file
View File

@ -0,0 +1,19 @@
/*
LED output functions
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/config.h"
extern HANDLE led_init_mutex;
HRESULT led_output_init(struct chuni_io_config* const cfg);
void led_output_update(uint8_t board, const uint8_t* rgb);

View File

@ -3,11 +3,20 @@ chuniio_lib = static_library(
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'chu2to3.c',
'chu2to3.h',
'chuniio.c',
'chuniio.h',
'config.c',
'config.h',
'leddata.h',
'ledoutput.c',
'ledoutput.h',
'pipeimpl.c',
'pipeimpl.h',
'serialimpl.c',
'serialimpl.h'
],
)

160
chuniio/pipeimpl.c Normal file
View File

@ -0,0 +1,160 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/leddata.h"
#include "chuniio/pipeimpl.h"
static bool pipe_update[LED_BOARDS_TOTAL];
// incoming data is copied into these to ensure it isn't written during output
static struct _chuni_led_data_buf_t pipe_write_buf[LED_BOARDS_TOTAL];
static HANDLE pipe_write_mutex;
static HRESULT pipe_create(LPHANDLE hPipe, LPCWSTR lpszPipename, DWORD dwBufSize)
{
*hPipe = INVALID_HANDLE_VALUE;
*hPipe = CreateNamedPipeW(
lpszPipename, // pipe name
PIPE_ACCESS_OUTBOUND, // read/write access
PIPE_TYPE_BYTE | // byte type pipe
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
dwBufSize, // output buffer size
0, // input buffer size
0, // client time-out
NULL); // default security attribute
if (*hPipe == INVALID_HANDLE_VALUE)
{
return E_FAIL;
}
return S_OK;
}
static HRESULT pipe_write(HANDLE hPipe, LPCVOID lpBuffer, DWORD dwSize)
{
DWORD cbWritten = 0;
bool fSuccess = WriteFile(
hPipe,
lpBuffer,
dwSize,
&cbWritten,
NULL);
if (!fSuccess || cbWritten != dwSize)
{
DWORD last_err = GetLastError();
return (last_err == ERROR_BROKEN_PIPE) ? E_ABORT : E_FAIL;
}
return S_OK;
}
static unsigned int __stdcall chuni_io_led_pipe_thread_proc(void *ctx)
{
HANDLE hPipe;
LPCWSTR lpszPipename = L"\\\\.\\pipe\\chuni_led";
while (true)
{
hPipe = INVALID_HANDLE_VALUE;
if (pipe_create(&hPipe, lpszPipename, LED_OUTPUT_TOTAL_SIZE_MAX) != S_OK)
{
continue;
}
// wait for a connection to the pipe
bool fConnected = ConnectNamedPipe(hPipe, NULL) ?
true : (GetLastError() == ERROR_PIPE_CONNECTED);
while (fConnected)
{
if (WaitForSingleObject(pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
{
continue;
}
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
if (pipe_update[i])
{
HRESULT result = pipe_write(
hPipe,
&pipe_write_buf[i],
LED_OUTPUT_HEADER_SIZE + pipe_write_buf[i].data_len);
if (result != S_OK)
{
//if (result == E_ABORT)
//{
fConnected = false;
//}
break;
}
pipe_update[i] = false;
}
}
ReleaseMutex(pipe_write_mutex);
}
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}
return 0;
}
HRESULT led_pipe_init()
{
pipe_write_mutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (pipe_write_mutex == NULL)
{
return E_FAIL;
}
// clear out update bools
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
pipe_update[i] = false;
}
_beginthreadex(
NULL,
0,
chuni_io_led_pipe_thread_proc,
0,
0,
NULL);
return S_OK;
}
void led_pipe_update(struct _chuni_led_data_buf_t* data)
{
if (data->board > 2)
{
return;
}
if (WaitForSingleObject(pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
{
return;
}
memcpy(&pipe_write_buf[data->board], data, sizeof(struct _chuni_led_data_buf_t));
pipe_update[data->board] = true;
ReleaseMutex(pipe_write_mutex);
}

15
chuniio/pipeimpl.h Normal file
View File

@ -0,0 +1,15 @@
/*
Pipe implementation for chuniio
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include "chuniio/leddata.h"
HRESULT led_pipe_init();
void led_pipe_update(struct _chuni_led_data_buf_t* data);

123
chuniio/serialimpl.c Normal file
View File

@ -0,0 +1,123 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/leddata.h"
#include "chuniio/serialimpl.h"
#include "util/dprintf.h"
static HANDLE serial_port;
static HANDLE serial_write_mutex;
HRESULT led_serial_init(wchar_t led_com[12], DWORD baud)
{
// Setup the serial communications
BOOL status;
serial_port = CreateFileW(led_com,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (serial_port == INVALID_HANDLE_VALUE)
dprintf("Chunithm Serial LEDs: Failed to open COM port (Attempted on %S)\n", led_com);
else
dprintf("Chunithm Serial LEDs: COM port success!\n");
DCB dcb_serial_params = { 0 };
dcb_serial_params.DCBlength = sizeof(dcb_serial_params);
status = GetCommState(serial_port, &dcb_serial_params);
dcb_serial_params.BaudRate = baud;
dcb_serial_params.ByteSize = 8;
dcb_serial_params.StopBits = ONESTOPBIT;
dcb_serial_params.Parity = NOPARITY;
SetCommState(serial_port, &dcb_serial_params);
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(serial_port, &timeouts);
if (!status)
{
return E_FAIL;
}
serial_write_mutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (serial_write_mutex == NULL)
{
return E_FAIL;
}
return S_OK;
}
void led_serial_update(struct _chuni_led_data_buf_t* data)
{
if (data->board > 2)
{
return;
}
if (WaitForSingleObject(serial_write_mutex, INFINITE) != WAIT_OBJECT_0)
{
return;
}
BOOL status = true;
DWORD bytes_written = 0;
if (serial_port != INVALID_HANDLE_VALUE) {
status = WriteFile(
serial_port,
data,
LED_OUTPUT_HEADER_SIZE + data->data_len,
&bytes_written,
NULL);
}
if (!status) {
DWORD last_err = GetLastError();
// dprintf("Chunithm Serial LEDs: Serial port write failed -- %d\n", last_err);
}
ReleaseMutex(serial_write_mutex);
}
void led_serial_update_openithm(const uint8_t* rgb)
{
if (serial_port != INVALID_HANDLE_VALUE)
{
char led_buffer[100];
DWORD bytes_to_write; // No of bytes to write into the port
DWORD bytes_written = 0; // No of bytes written to the port
bytes_to_write = sizeof(led_buffer);
BOOL status;
led_buffer[0] = 0xAA;
led_buffer[1] = 0xAA;
memcpy(led_buffer+2, rgb, sizeof(uint8_t) * 96);
led_buffer[98] = 0xDD;
led_buffer[99] = 0xDD;
status = WriteFile(serial_port, // Handle to the Serial port
led_buffer, // Data to be written to the port
bytes_to_write, // No of bytes to write
&bytes_written, // Bytes written
NULL);
}
}

17
chuniio/serialimpl.h Normal file
View File

@ -0,0 +1,17 @@
/*
Serial LED implementation for chuniio
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include <stdint.h>
#include "chuniio/leddata.h"
HRESULT led_serial_init(wchar_t led_com[12], DWORD baud);
void led_serial_update(struct _chuni_led_data_buf_t* data);
void led_serial_update_openithm(const uint8_t* rgb);

190
chusanhook/chuni-dll.c Normal file
View File

@ -0,0 +1,190 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <stdlib.h>
#include "chuniio/chu2to3.h"
#include "chusanhook/chuni-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym chuni_dll_syms[] = {
{
.sym = "chuni_io_jvs_init",
.off = offsetof(struct chuni_dll, jvs_init),
}, {
.sym = "chuni_io_jvs_poll",
.off = offsetof(struct chuni_dll, jvs_poll),
}, {
.sym = "chuni_io_jvs_read_coin_counter",
.off = offsetof(struct chuni_dll, jvs_read_coin_counter),
}, {
.sym = "chuni_io_slider_init",
.off = offsetof(struct chuni_dll, slider_init),
}, {
.sym = "chuni_io_slider_start",
.off = offsetof(struct chuni_dll, slider_start),
}, {
.sym = "chuni_io_slider_stop",
.off = offsetof(struct chuni_dll, slider_stop),
}, {
.sym = "chuni_io_slider_set_leds",
.off = offsetof(struct chuni_dll, slider_set_leds),
}, {
.sym = "chuni_io_led_init",
.off = offsetof(struct chuni_dll, led_init),
}, {
.sym = "chuni_io_led_set_colors",
.off = offsetof(struct chuni_dll, led_set_leds),
}
};
const struct dll_bind_sym chu2to3_dll_syms[] = {
{
.sym = "chu2to3_io_jvs_init",
.off = offsetof(struct chuni_dll, jvs_init),
}, {
.sym = "chu2to3_io_jvs_poll",
.off = offsetof(struct chuni_dll, jvs_poll),
}, {
.sym = "chu2to3_io_jvs_read_coin_counter",
.off = offsetof(struct chuni_dll, jvs_read_coin_counter),
}, {
.sym = "chu2to3_io_slider_init",
.off = offsetof(struct chuni_dll, slider_init),
}, {
.sym = "chu2to3_io_slider_start",
.off = offsetof(struct chuni_dll, slider_start),
}, {
.sym = "chu2to3_io_slider_stop",
.off = offsetof(struct chuni_dll, slider_stop),
}, {
.sym = "chu2to3_io_slider_set_leds",
.off = offsetof(struct chuni_dll, slider_set_leds),
}, {
.sym = "chu2to3_io_led_init",
.off = offsetof(struct chuni_dll, led_init),
}, {
.sym = "chu2to3_io_led_set_colors",
.off = offsetof(struct chuni_dll, led_set_leds),
}
};
/* Helper function to determine upon dll_bind failure whether the required functions were found
NOTE: relies on symbols order declared above */
static HRESULT has_enough_symbols(uint16_t version, uint8_t count)
{
if ( version <= 0x0101 && count == 7 )
return S_OK;
if ( version >= 0x0102 && count == 9 )
return S_OK;
return E_FAIL;
}
struct chuni_dll chuni_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
// Not much of this lends itself to being easily factored out. Also there
// will be a lot of API-specific branching code here eventually as new API
// versions get defined, so even though these functions all look the same
// now this won't remain the case forever.
HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
owned = NULL;
src = self;
if (cfg->chu2to3) {
dprintf("Chunithm IO: using chu2to3 engine for IO DLL: %S\n", cfg->path);
} else if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Chunithm IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Chunithm IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
}
if (cfg->chu2to3) {
if (chu2to3_load_dll(cfg->path) != 0)
dprintf("Could not init chu2to3 engine\n");
get_api_version = (void *) GetProcAddress(src, "chu2to3_io_get_api_version");
}
else
{
get_api_version = (void *) GetProcAddress(src, "chuni_io_get_api_version");
}
if (get_api_version != NULL) {
chuni_dll.api_version = get_api_version();
} else {
chuni_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose chuni_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (chuni_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Chunithm IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
chuni_dll.api_version);
goto end;
}
sym = cfg->chu2to3 ? chu2to3_dll_syms : chuni_dll_syms;
const struct dll_bind_sym *init_sym = &sym[0];
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
if (FAILED(hr)) {
if (src != self) {
// Might still be ok depending on external dll API version
int bind_count = sym - init_sym;
if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
{
hr = S_OK;
} else {
dprintf("Chunithm IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
dprintf("imported %d symbols\n", bind_count);
goto end;
}
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

27
chusanhook/chuni-dll.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <windows.h>
#include "chuniio/chuniio.h"
struct chuni_dll {
uint16_t api_version;
HRESULT (*jvs_init)(void);
void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams);
void (*jvs_read_coin_counter)(uint16_t *total);
HRESULT (*slider_init)(void);
void (*slider_start)(chuni_io_slider_callback_t callback);
void (*slider_stop)(void);
void (*slider_set_leds)(const uint8_t *rgb);
HRESULT (*led_init)(void);
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
};
struct chuni_dll_config {
wchar_t path[MAX_PATH];
uint8_t chu2to3;
};
extern struct chuni_dll chuni_dll;
HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self);

34
chusanhook/chusanhook.def Normal file
View File

@ -0,0 +1,34 @@
LIBRARY chusanhook
EXPORTS
Direct3DCreate9
aime_io_get_api_version
aime_io_init
aime_io_led_set_color
aime_io_nfc_get_aime_id
aime_io_nfc_get_felica_id
aime_io_nfc_poll
amDllVideoClose @2
amDllVideoGetVBiosVersion @4
amDllVideoOpen @1
amDllVideoSetResolution @3
chuni_io_get_api_version
chuni_io_jvs_init
chuni_io_jvs_poll
chuni_io_jvs_read_coin_counter
chuni_io_slider_init
chuni_io_slider_set_leds
chuni_io_slider_start
chuni_io_slider_stop
chuni_io_led_init
chuni_io_led_set_colors
chu2to3_io_get_api_version
chu2to3_io_jvs_init
chu2to3_io_jvs_poll
chu2to3_io_jvs_read_coin_counter
chu2to3_io_slider_init
chu2to3_io_slider_set_leds
chu2to3_io_slider_start
chu2to3_io_slider_stop
chu2to3_io_led_init
chu2to3_io_led_set_colors

169
chusanhook/config.c Normal file
View File

@ -0,0 +1,169 @@
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include "board/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "gfxhook/config.h"
#include "platform/config.h"
#include "chusanhook/config.h"
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
void chuni_dll_config_load(
struct chuni_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
// Workaround for x64/x86 external IO dlls
// path32 for 32bit, path64 for 64bit
// path for 32bit only dlls (internal chu2to3 engine)
GetPrivateProfileStringW(
L"chuniio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
if (cfg->path[0] != L'\0') {
cfg->chu2to3 = 1;
} else {
cfg->chu2to3 = 0;
#if defined(ENV32BIT)
GetPrivateProfileStringW(
L"chuniio",
L"path32",
L"",
cfg->path,
_countof(cfg->path),
filename);
#elif defined(ENV64BIT)
GetPrivateProfileStringW(
L"chuniio",
L"path64",
L"",
cfg->path,
_countof(cfg->path),
filename);
#else
#error "Unknown environment"
#endif
}
}
void slider_config_load(struct slider_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename);
}
void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t tmpstr[16];
bool cvt_port;
memset(cfg->board_number, ' ', sizeof(cfg->board_number));
memset(cfg->chip_number, ' ', sizeof(cfg->chip_number));
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
cfg->port_no[0] = GetPrivateProfileIntW(L"led15093", L"portNo1", 0, filename);
cfg->port_no[1] = GetPrivateProfileIntW(L"led15093", L"portNo2", 0, filename);
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xadf7, filename);
GetPrivateProfileStringW(
L"led15093",
L"boardNumber",
L"15093-06",
tmpstr,
_countof(tmpstr),
filename);
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
for (int i = n; i < sizeof(cfg->board_number); i++)
{
cfg->board_number[i] = ' ';
}
GetPrivateProfileStringW(
L"led15093",
L"chipNumber",
L"6710 ",
tmpstr,
_countof(tmpstr),
filename);
n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number));
for (int i = n; i < sizeof(cfg->chip_number); i++)
{
cfg->chip_number[i] = ' ';
}
GetPrivateProfileStringW(
L"led15093",
L"bootChipNumber",
L"6709 ",
tmpstr,
_countof(tmpstr),
filename);
n = wcstombs(cfg->boot_chip_number, tmpstr, sizeof(cfg->boot_chip_number));
for (int i = n; i < sizeof(cfg->boot_chip_number); i++)
{
cfg->boot_chip_number[i] = ' ';
}
}
void chusan_hook_config_load(
struct chusan_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
memset(cfg, 0, sizeof(*cfg));
// Force load the 64bit Aime DLL instead of the 32bit one
cfg->aime.dll.path64 = true;
platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
gfx_config_load(&cfg->gfx, filename);
vfd_config_load(&cfg->vfd, filename);
chuni_dll_config_load(&cfg->dll, filename);
slider_config_load(&cfg->slider, filename);
led15093_config_load(&cfg->led15093, filename);
}

35
chusanhook/config.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "board/led15093.h"
#include "hooklib/dvd.h"
#include "gfxhook/config.h"
#include "platform/config.h"
#include "chusanhook/chuni-dll.h"
#include "chusanhook/slider.h"
struct chusan_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct vfd_config vfd;
struct chuni_dll_config dll;
struct slider_config slider;
struct led15093_config led15093;
};
void chuni_dll_config_load(
struct chuni_dll_config *cfg,
const wchar_t *filename);
void slider_config_load(struct slider_config *cfg, const wchar_t *filename);
void chusan_hook_config_load(
struct chusan_hook_config *cfg,
const wchar_t *filename);

202
chusanhook/dllmain.c Normal file
View File

@ -0,0 +1,202 @@
/*
"CHUNITHM NEW" (chusan) hook
Devices
USB: 837-15257-02 "Type 4" I/O Board
COM1: 837-15330 Ground Slider
[CVT mode (DIPSW2 ON)]
COM2: 837-15093-06 LED Controller Board
COM3: 837-15093-06 LED Controller Board
COM4: 837-15286 "Gen 2" Aime Reader
[SP mode (DIPSW2 OFF)]
USB: 837-15067-02 USB Serial I/F Board
connected to
837-15093-06 LED Controller Board (COM20)
837-15093-06 LED Controller Board (COM21)
COM2: 200-6275 VFD GP1232A02A FUTABA Board
COM4: 837-15396 "Gen 3" Aime Reader
*/
#include <stddef.h>
#include <stdlib.h>
#include <windows.h>
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "chuniio/chuniio.h"
#include "chusanhook/config.h"
#include "chusanhook/io4.h"
#include "chusanhook/slider.h"
#include "gfxhook/d3d9.h"
#include "gfxhook/gfx.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "platform/platform.h"
#include "util/dprintf.h"
#include "util/env.h"
static HMODULE chusan_hook_mod;
static process_entry_t chusan_startup;
static struct chusan_hook_config chusan_hook_cfg;
static DWORD CALLBACK chusan_pre_startup(void) {
HMODULE d3dc;
HMODULE dbghelp;
HRESULT hr;
dprintf("--- Begin chusan_pre_startup ---\n");
/* Pin the D3D shader compiler. This makes startup much faster. */
d3dc = LoadLibraryW(L"D3DCompiler_43.dll");
if (d3dc != NULL) {
dprintf("Pinned shader compiler, hMod=%p\n", d3dc);
} else {
dprintf("Failed to load shader compiler!\n");
}
/* Pin dbghelp so the path hooks apply to it. */
dbghelp = LoadLibraryW(L"dbghelp.dll");
if (dbghelp != NULL) {
dprintf("Pinned debug helper library, hMod=%p\n", dbghelp);
} else {
dprintf("Failed to load debug helper library!\n");
}
/* Config load */
chusan_hook_config_load(&chusan_hook_cfg, get_config_path());
/* Hook Win32 APIs */
dvd_hook_init(&chusan_hook_cfg.dvd, chusan_hook_mod);
gfx_hook_init(&chusan_hook_cfg.gfx);
gfx_d3d9_hook_init(&chusan_hook_cfg.gfx, chusan_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
struct dipsw_config new_dipsw_config[8] = {
{L"Delivery Server", L"Server", L"Client"},
{L"Monitor Type", L"60FPS", L"120FPS"},
{L"Cabinet Type", L"CVT", L"SP"},
};
// Set the system dip switch configuration
memcpy(chusan_hook_cfg.platform.system.dipsw_config, new_dipsw_config,
sizeof(new_dipsw_config));
hr = platform_hook_init(
&chusan_hook_cfg.platform,
"SDHD",
"ACA2",
chusan_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = chuni_dll_init(&chusan_hook_cfg.dll, chusan_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = chusan_io4_hook_init(&chusan_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
hr = slider_hook_init(&chusan_hook_cfg.slider);
if (FAILED(hr)) {
goto fail;
}
bool is_cvt = chusan_hook_cfg.platform.system.dipsw[2];
if (!is_cvt) {
hr = vfd_hook_init(&chusan_hook_cfg.vfd, 2);
if (FAILED(hr)) {
goto fail;
}
}
unsigned int led_port_no[2];
if (is_cvt) {
led_port_no[0] = 2;
led_port_no[1] = 3;
} else {
led_port_no[0] = 20;
led_port_no[1] = 21;
}
if (chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL) {
dprintf(
"IO DLL doesn't support led_init/led_set_leds, cannot start "
"LED15093 hook\n");
} else {
hr = led15093_hook_init(&chusan_hook_cfg.led15093, chuni_dll.led_init,
chuni_dll.led_set_leds, led_port_no);
if (FAILED(hr)) {
goto fail;
}
}
hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_cvt ? 2 : 3,
chusan_hook_mod);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(get_config_path());
dprintf("--- End chusan_pre_startup ---\n");
/* Jump to EXE start address */
return chusan_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) {
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
chusan_hook_mod = mod;
hr = process_hijack_startup(chusan_pre_startup, &chusan_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int)hr);
}
return SUCCEEDED(hr);
}

106
chusanhook/io4.c Normal file
View File

@ -0,0 +1,106 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "chusanhook/chuni-dll.h"
#include "util/dprintf.h"
struct chunithm_jvs_ir_mask {
uint16_t p1;
uint16_t p2;
};
// Incorrect IR beam mappings retained for backward compatibility
static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks_v1[] = {
{ 0, 1 << 13 },
{ 1 << 13, 0 },
{ 0, 1 << 12 },
{ 1 << 12, 0 },
{ 0, 1 << 11 },
{ 1 << 11, 0 },
};
static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = {
{ 1 << 13, 0 },
{ 0, 1 << 13 },
{ 1 << 12, 0 },
{ 0, 1 << 12 },
{ 1 << 11, 0 },
{ 0, 1 << 11 },
};
static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state);
static uint16_t coins;
static const struct io4_ops chusan_io4_ops = {
.poll = chusan_io4_poll,
};
HRESULT chusan_io4_hook_init(const struct io4_config* cfg)
{
HRESULT hr;
assert(chuni_dll.jvs_init != NULL);
dprintf("USB I/O: Starting IO backend\n");
hr = chuni_dll.jvs_init();
if (FAILED(hr)) {
dprintf("USB I/O: Backend error, I/O disconnected: %x\n", (int)hr);
return hr;
}
io4_hook_init(cfg, &chusan_io4_ops, NULL);
return S_OK;
}
static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state)
{
const struct chunithm_jvs_ir_mask *masks;
uint8_t opbtn;
uint8_t beams;
size_t i;
memset(state, 0, sizeof(*state));
opbtn = 0;
beams = 0;
chuni_dll.jvs_poll(&opbtn, &beams);
chuni_dll.jvs_read_coin_counter(&coins);
if (chuni_dll.api_version >= 0x0101) {
// Use correct mapping
masks = chunithm_jvs_ir_masks;
} else {
// Use backwards-compatible incorrect mapping
masks = chunithm_jvs_ir_masks_v1;
}
if (opbtn & CHUNI_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & CHUNI_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
// Update the coin counter with the value from jvs_read_coin_counter
state->chutes[0] = coins << 8;
for (i = 0; i < 6; i++) {
/* Beam "press" is active-low hence the ~ */
if (~beams & (1 << i)) {
state->buttons[0] |= masks[i].p1;
state->buttons[1] |= masks[i].p2;
}
}
return S_OK;
}

5
chusanhook/io4.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <windows.h>
HRESULT chusan_io4_hook_init(const struct io4_config *cfg);

31
chusanhook/meson.build Normal file
View File

@ -0,0 +1,31 @@
shared_library(
'chusanhook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'chusanhook.def',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
],
link_with : [
aimeio_lib,
board_lib,
chuniio_lib,
gfxhook_lib,
hooklib_lib,
platform_lib,
util_lib,
],
sources : [
'chuni-dll.c',
'chuni-dll.h',
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'slider.c',
'slider.h',
],
)

249
chusanhook/slider.c Normal file
View File

@ -0,0 +1,249 @@
#include <windows.h>
#include <assert.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "board/slider-cmd.h"
#include "board/slider-frame.h"
#include "chusanhook/chuni-dll.h"
#include "chusanhook/slider.h"
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT slider_handle_irp(struct irp *irp);
static HRESULT slider_handle_irp_locked(struct irp *irp);
static HRESULT slider_req_dispatch(const union slider_req_any *req);
static HRESULT slider_req_reset(void);
static HRESULT slider_req_get_board_info(void);
static HRESULT slider_req_auto_scan_start(void);
static HRESULT slider_req_auto_scan_stop(void);
static HRESULT slider_req_set_led(const struct slider_req_set_led *req);
static void slider_res_auto_scan(const uint8_t *state);
static CRITICAL_SECTION slider_lock;
static struct uart slider_uart;
static uint8_t slider_written_bytes[520];
static uint8_t slider_readable_bytes[520];
HRESULT slider_hook_init(const struct slider_config *cfg)
{
assert(cfg != NULL);
assert(chuni_dll.slider_init != NULL);
if (!cfg->enable) {
return S_FALSE;
}
InitializeCriticalSection(&slider_lock);
uart_init(&slider_uart, 1);
slider_uart.written.bytes = slider_written_bytes;
slider_uart.written.nbytes = sizeof(slider_written_bytes);
slider_uart.readable.bytes = slider_readable_bytes;
slider_uart.readable.nbytes = sizeof(slider_readable_bytes);
return iohook_push_handler(slider_handle_irp);
}
static HRESULT slider_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&slider_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&slider_lock);
hr = slider_handle_irp_locked(irp);
LeaveCriticalSection(&slider_lock);
return hr;
}
static HRESULT slider_handle_irp_locked(struct irp *irp)
{
union slider_req_any req;
struct iobuf req_iobuf;
HRESULT hr;
if (irp->op == IRP_OP_OPEN) {
dprintf("Chunithm slider: Starting backend\n");
hr = chuni_dll.slider_init();
if (FAILED(hr)) {
dprintf("Chunithm slider: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&slider_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if defined(LOG_CHUSAN_SLIDER)
dprintf("TX Buffer:\n");
dump_iobuf(&slider_uart.written);
#endif
req_iobuf.bytes = req.bytes;
req_iobuf.nbytes = sizeof(req.bytes);
req_iobuf.pos = 0;
hr = slider_frame_decode(&req_iobuf, &slider_uart.written);
if (hr != S_OK) {
if (FAILED(hr)) {
dprintf("Chunithm slider: Deframe error: %x\n", (int) hr);
}
return hr;
}
#if defined(LOG_CHUSAN_SLIDER)
dprintf("Deframe Buffer:\n");
dump_iobuf(&req_iobuf);
#endif
hr = slider_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Chunithm slider: Processing error: %x\n", (int) hr);
}
}
}
static HRESULT slider_req_dispatch(const union slider_req_any *req)
{
switch (req->hdr.cmd) {
case SLIDER_CMD_RESET:
return slider_req_reset();
case SLIDER_CMD_GET_BOARD_INFO:
return slider_req_get_board_info();
case SLIDER_CMD_SET_LED:
return slider_req_set_led(&req->set_led);
case SLIDER_CMD_AUTO_SCAN_START:
return slider_req_auto_scan_start();
case SLIDER_CMD_AUTO_SCAN_STOP:
return slider_req_auto_scan_stop();
default:
dprintf("Unhandled command %02x\n", req->hdr.cmd);
return S_OK;
}
}
static HRESULT slider_req_reset(void)
{
struct slider_hdr resp;
dprintf("Chunithm slider: Reset\n");
resp.sync = 0xFF;
resp.cmd = SLIDER_CMD_RESET;
resp.nbytes = 0;
return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
}
static HRESULT slider_req_get_board_info(void)
{
struct slider_resp_get_board_info resp;
dprintf("Chunithm slider: Get firmware version\n");
memset(&resp, 0, sizeof(resp));
resp.hdr.sync = SLIDER_FRAME_SYNC;
resp.hdr.cmd = SLIDER_CMD_GET_BOARD_INFO;
resp.hdr.nbytes = sizeof(resp.version);
strcpy_s(
resp.version,
sizeof(resp.version),
"15330 \xA0" "06712\xFF" "\x90");
return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
}
static HRESULT slider_req_auto_scan_start(void)
{
assert(chuni_dll.slider_start != NULL);
dprintf("Chunithm slider: Start slider notifications\n");
chuni_dll.slider_start(slider_res_auto_scan);
/* This message is not acknowledged */
return S_OK;
}
static HRESULT slider_req_auto_scan_stop(void)
{
struct slider_hdr resp;
assert(chuni_dll.slider_stop != NULL);
dprintf("Chunithm slider: Stop slider notifications\n");
/* IO DLL worker thread might attempt to invoke the callback (which needs
to take slider_lock, which we are currently holding) before noticing that
it needs to shut down. Unlock here so that we don't deadlock in that
situation. */
LeaveCriticalSection(&slider_lock);
chuni_dll.slider_stop();
EnterCriticalSection(&slider_lock);
resp.sync = SLIDER_FRAME_SYNC;
resp.cmd = SLIDER_CMD_AUTO_SCAN_STOP;
resp.nbytes = 0;
return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
}
static HRESULT slider_req_set_led(const struct slider_req_set_led *req)
{
assert(chuni_dll.slider_set_leds != NULL);
chuni_dll.slider_set_leds(req->payload.rgb);
/* This message is not acknowledged */
return S_OK;
}
static void slider_res_auto_scan(const uint8_t *state)
{
struct slider_resp_auto_scan resp;
resp.hdr.sync = SLIDER_FRAME_SYNC;
resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN;
resp.hdr.nbytes = sizeof(resp.pressure);
memcpy(resp.pressure, state, sizeof(resp.pressure));
EnterCriticalSection(&slider_lock);
slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
LeaveCriticalSection(&slider_lock);
}

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