mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
Compare commits
502 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f94ef4478 | |||
| c4193eddc5 | |||
| c8962a0e77 | |||
| b6a749e5db | |||
| 7dbbe9809e | |||
| 79a3e32d5d | |||
| c5a4e954f9 | |||
| 7b862768b0 | |||
| af8d8dae9f | |||
| d9d5823fdb | |||
| 82dda33800 | |||
| 3876e38636 | |||
| 6bc1357d6a | |||
| 37218e7fe0 | |||
| 678e11eade | |||
| f72313fe45 | |||
| 06374ef78a | |||
| bdde1ec6b8 | |||
| d2176fe4f0 | |||
| fb9eef904a | |||
| 48903998a5 | |||
| f2dca0265f | |||
| 1147ed9858 | |||
| 19d93f4ffd | |||
| 15bfffa1d3 | |||
| 18a9a47c33 | |||
| 6838e69ebe | |||
| 26b3b64794 | |||
| aadd2bdd59 | |||
| d0e24e31ca | |||
| 4fb7f20c7b | |||
| 6ddae70c5a | |||
| b38160d6c2 | |||
| 231c998cc0 | |||
| 127d31a85c | |||
| a8419bbaa0 | |||
| 0fc430ceb3 | |||
| e59a50b64c | |||
| 2042b629e9 | |||
| 440b9ed749 | |||
| 9936088286 | |||
| 4d8a6c1952 | |||
| af69cf9251 | |||
| 1d5550b1e8 | |||
| ec3f6639b7 | |||
| 727b99effd | |||
| 8afff3aaca | |||
| b281517429 | |||
| 54ec9f272e | |||
| a159691097 | |||
| ca70f7b19c | |||
| 7b4227d2a9 | |||
| f89efa2558 | |||
| 6f4824b648 | |||
| 45debeb5c9 | |||
| a01f452eba | |||
| e452734bc1 | |||
| d1779b93fa | |||
| fe9aac7e29 | |||
| f3d887ebcf | |||
| 336500c8ae | |||
| ee3fa58bfa | |||
| a4c3eddc2f | |||
| e0f6cb2cbd | |||
| 1ad6a9412e | |||
| 5af3e0d494 | |||
| b1ee828457 | |||
| 0d60649715 | |||
| 220b27159b | |||
| b06a0a7d47 | |||
| d66af8e16b | |||
| ad366ecdd0 | |||
| 22cb4d689c | |||
| b38ea463aa | |||
| 056d1b2d90 | |||
| 37693916d9 | |||
| 889e8b43eb | |||
| 9db2db2340 | |||
| 1bb612f3c9 | |||
| e886ab77af | |||
| fbb3c1d4c3 | |||
| 73a69b2e8f | |||
| 63a0acac24 | |||
| 75ba49ff4a | |||
| b31ddb1ffc | |||
| 458a1495c8 | |||
| eb029faf2e | |||
| d65eb6ed4f | |||
| 97ec2e5d37 | |||
| 97d65b137a | |||
| a9f0e86aa3 | |||
| 88679b7cb2 | |||
| 9ec62a61ac | |||
| 44befb7e9a | |||
| be388f7b49 | |||
| 34ef034345 | |||
| ba4623b009 | |||
| a0a7b86508 | |||
| 6d7927927c | |||
| 837c8a46d6 | |||
| 22aeb64ff9 | |||
| e4b08064b7 | |||
| 1e6b4c16cc | |||
| d6eb8f7125 | |||
| dc6850e479 | |||
| 2d623e179b | |||
| 884d665c1c | |||
| 2842fe7ec6 | |||
| 954e6022d9 | |||
| 955c50a9f3 | |||
| dc82c980b1 | |||
| 408ed17521 | |||
| f4050e9adb | |||
| badae01357 | |||
| 38d9939eb7 | |||
| f5a3caf536 | |||
| 027445afb1 | |||
| 63a3900b79 | |||
| f542a78347 | |||
| 4d32dde83e | |||
| 927170ce73 | |||
| 875a0f0765 | |||
| 3ad88ef15c | |||
| 7a6cadb176 | |||
| 4feca23240 | |||
| 2e52bebd3d | |||
| aa218d5be7 | |||
| 94f009991a | |||
| c3b9ce3fdc | |||
| 1517cf9a7c | |||
| f0f46296f2 | |||
| 1d1981fcde | |||
| 2f538fe2e4 | |||
| 8e01b2e5c8 | |||
| cd14ca800e | |||
| cb59fe14e9 | |||
| 9fd9290cd3 | |||
| 1dd99aac74 | |||
| 9e376f373e | |||
| d4e51b77d1 | |||
| 61181836b0 | |||
| 4e26c8afa5 | |||
| 7684028f99 | |||
| 1d6be8fe4b | |||
| c70cf40af3 | |||
| 5715d4e605 | |||
| 821a705a7d | |||
| 86f2a1fd39 | |||
| 86f218329a | |||
| 66094de38a | |||
| c00cff2afd | |||
| b2cc8d404c | |||
| a0a04ab62f | |||
| a3a59e689e | |||
| a6f29d6a5a | |||
| 9be618d791 | |||
| 6bf7ae9074 | |||
| e3d63c65c1 | |||
| 5d2a9fa1cf | |||
| 0078b72b03 | |||
| b1ee37a066 | |||
| ebf6382e7f | |||
| 8e23ef224a | |||
| 6706ffc321 | |||
| e755b06e35 | |||
| 9ef22712c0 | |||
| 7b903d35e2 | |||
| 7f281a50c5 | |||
| 1354d63b85 | |||
| e745cdcac8 | |||
| bf7666939a | |||
| 7e89037db3 | |||
| 6ffaa77f58 | |||
| 7a1f46d1be | |||
| cf59ae5f89 | |||
| 9c06dd36a0 | |||
| d5d082ec37 | |||
| 17723d290a | |||
| 8de7e5a9e4 | |||
| b8e99e75e5 | |||
| 46dc22dd63 | |||
| 69d78e0a87 | |||
| ab3d41432a | |||
| 73e10cac0c | |||
| c6242233a7 | |||
| 6b8335780f | |||
| 21f469b260 | |||
| 07a0df5fdc | |||
| d4f1768bbd | |||
| 96e7e4949f | |||
| 24d3062c66 | |||
| 6cd212aac2 | |||
| 064031bc12 | |||
| 4c8d4ebab6 | |||
| 462a07680b | |||
| 081def30db | |||
| 6776b08bce | |||
| 6cb507f4fa | |||
| de05af052f | |||
| c767918729 | |||
| fbc8bc2502 | |||
| c38f42cb19 | |||
| 42d8bbc92f | |||
| a09905792d | |||
| f113eb4f52 | |||
| f726748ebe | |||
| 371c957025 | |||
| 83aa907b13 | |||
| 71b144918c | |||
| e618a05b08 | |||
| d0f3ffd12f | |||
| 6d18d6a496 | |||
| b0810558a7 | |||
| 6ae2e4003c | |||
| 8545f1cbdc | |||
| e0530fedad | |||
| b220cbf336 | |||
| f68a3439d3 | |||
| 6f58d9ab68 | |||
| c3428236f8 | |||
| 6856a0950c | |||
| dc7d8515d3 | |||
| e100093ead | |||
| e5808257ca | |||
| 283ec3960a | |||
| 0ec37ac6ea | |||
| 0ef0b84308 | |||
| 62c30d8895 | |||
| 4e86cb16a2 | |||
| 76f401de95 | |||
| 5947779502 | |||
| 61c97ccaab | |||
| 12d6a88c60 | |||
| 984b41a1ae | |||
| d06b8bd731 | |||
| 0dad96b876 | |||
| def296095b | |||
| 750b1fea80 | |||
| 4c87078807 | |||
| 052698afa3 | |||
| 9e2b04d5db | |||
| 5641e06a19 | |||
| 88ec6dbcee | |||
| 1b614c94a7 | |||
| 37c5913f69 | |||
| 2eca668593 | |||
| aec1da9d16 | |||
| 548b441f1f | |||
| 98bc285d98 | |||
| 553c180bee | |||
| 537252440e | |||
| 13a0877d38 | |||
| 50691b79f1 | |||
| ad229dabdb | |||
| bdff33a420 | |||
| 4b21821aa4 | |||
| 6b47b5db50 | |||
| a10ca5236b | |||
| be2dab9a1a | |||
| 02711cdee1 | |||
| c6b10402f4 | |||
| 58ea6ce17e | |||
| 8cc00c371a | |||
| 8e2985d8d0 | |||
| 8d3b15641f | |||
| 033e9f926a | |||
| da58503b47 | |||
| cffee2b7f8 | |||
| 32801319fa | |||
| afbe013811 | |||
| 862e4717f6 | |||
| d3fa27ae4a | |||
| b487cf2bcf | |||
| 0fc3aa5818 | |||
| 276874f882 | |||
| 1fdd270480 | |||
| 07cb6048a0 | |||
| 61d79f76b2 | |||
| 7335d07e30 | |||
| 9f99b71112 | |||
| 5c3921a99c | |||
| 9fc397b8bc | |||
| ddc8061268 | |||
| c0698a3872 | |||
| 1ed3521091 | |||
| 0dda920448 | |||
| 5f7580b69e | |||
| 3ef1b3ecaa | |||
| 931811da14 | |||
| 7624ded512 | |||
| a5252939dd | |||
| 808ac0c557 | |||
| 4e138b244e | |||
| 3fcd1bdbc9 | |||
| f857e63d5d | |||
| b462c838f6 | |||
| 4d658bf273 | |||
| 67721c416b | |||
| 0b5f7787be | |||
| a554c8f923 | |||
| 6592191624 | |||
| 164592b16d | |||
| 81f879e7ff | |||
| 1af8b3de61 | |||
| c33fef606f | |||
| a9fc6ef1b2 | |||
| d530bfcd65 | |||
| c96f4330d2 | |||
| 26a2a0f481 | |||
| 11b9790e6c | |||
| 1dfcf8c087 | |||
| 5c7ad0d51e | |||
| 587ae86341 | |||
| 926f2ef2a0 | |||
| d262e02f8c | |||
| f6b63473a0 | |||
| 1ad45edd6e | |||
| e79de3b117 | |||
| f783a5a1cd | |||
| 7848d5c237 | |||
| cec735a81b | |||
| 753702e32e | |||
| 7bd7503951 | |||
| d0c38956bf | |||
| 77bca2baee | |||
| 46e8c8a5a5 | |||
| 4d58e5d080 | |||
| 168084d672 | |||
| 740ed90d95 | |||
| 61c17f15b4 | |||
| 99035f312f | |||
| aafc916bb6 | |||
| c5eb1e999b | |||
| 7c15452c1e | |||
| 4bc849ccdb | |||
| 58481a035b | |||
| 66b3de8e5c | |||
| 257afe0cd3 | |||
| aafea3b1c1 | |||
| 346a159157 | |||
| 54028d8cbb | |||
| 621b702ed4 | |||
| 26ea8a1b92 | |||
| 6e7bf99af8 | |||
| df9d9d8673 | |||
| b3cee1dfcc | |||
| f40c382903 | |||
| e4cfb808bf | |||
| 61d14a16e8 | |||
| 521e87ea41 | |||
| 2b735ffd55 | |||
| 4f72b0a98b | |||
| 474f5f5203 | |||
| 205cf29aff | |||
| bf9074a76e | |||
| 44e1edb50c | |||
| 41d0dce6e9 | |||
| 598422b701 | |||
| b58a1ccfcc | |||
| 60efd643d1 | |||
| 0596eeb6fe | |||
| 3d5c64feb7 | |||
| 610984f40e | |||
| 1e271b7352 | |||
| 07c78caf01 | |||
| d153209d8a | |||
| 48186245fe | |||
| f7c274591a | |||
| d1a20e58b6 | |||
| 1e02c31524 | |||
| 66fab48a5b | |||
| ae06652148 | |||
| bd6c8d3f3c | |||
| e88abec2a6 | |||
| 6de8b529d2 | |||
| 7301cdaa6c | |||
| 6969dfcdd1 | |||
| edaf5a386f | |||
| 302f08dd57 | |||
| 68f5a614ef | |||
| 5db43031f2 | |||
| 873ade757a | |||
| 3c8dc9de32 | |||
| 4694b59cec | |||
| 4f1ada7a2f | |||
| 104a9cbffd | |||
| 45a52cff90 | |||
| bf79b5d2aa | |||
| e74c60f61d | |||
| 89921e5ec2 | |||
| 1b7ebd6fc2 | |||
| 165cc6028f | |||
| 8dc835bf52 | |||
| 8241641502 | |||
| 6de122c9f8 | |||
| b267ad09ac | |||
| e0dd371d61 | |||
| f8f45a0cbd | |||
| bf8ba050ce | |||
| 5fe7d1d993 | |||
| cdcd8849f1 | |||
| 4c215aa5bb | |||
| ab5c343c7d | |||
| 96cbf11270 | |||
| c734e410f5 | |||
| 1eed9ea2db | |||
| 7d436e0051 | |||
| ed8c72852f | |||
| 94af8d32c9 | |||
| 5fe96a4091 | |||
| d1e86aefb9 | |||
| ecd53b4222 | |||
| 9f02febf43 | |||
| a2ff2811e3 | |||
| ccf46a6c77 | |||
| 6ca215a9fd | |||
| 520411782e | |||
| c56e94316d | |||
| 461563c6e6 | |||
| 164615a9e3 | |||
| 1040b32f3e | |||
| d0719260e7 | |||
| 33d98d8217 | |||
| e1f077a11b | |||
| bd93d9cd11 | |||
| 004d1bbc7a | |||
| 99eb254e10 | |||
| 72c45f4ce1 | |||
| 3d3f2f5dd5 | |||
| be37865b74 | |||
| 5bce47e487 | |||
| ccf2009726 | |||
| 0253cb84a7 | |||
| 4d7017bd07 | |||
| cb4bd6e80d | |||
| 7ed33e261e | |||
| a7e7eb36be | |||
| 962db5d4d4 | |||
| b59db8dd14 | |||
| 680e7a7bda | |||
| 5e67ce5067 | |||
| 4051c728fc | |||
| ae4dbece0a | |||
| 6551687708 | |||
| bd690e183d | |||
| 550dd08f19 | |||
| eafe7a834a | |||
| 6e8c87921b | |||
| ccf4ac2080 | |||
| 98188524d7 | |||
| 7477a9996d | |||
| ab2625ec73 | |||
| df5da69be6 | |||
| 5618ae9694 | |||
| 8a4236fba5 | |||
| d5104ac117 | |||
| 8c8e2de4f6 | |||
| cb6252b15f | |||
| 864eec2391 | |||
| 56ad570231 | |||
| 33e0611ccf | |||
| c12679e815 | |||
| f3415b6691 | |||
| c6f9ce9639 | |||
| 11cd407f33 | |||
| 4055bf94ff | |||
| 3274360961 | |||
| 2bf69f8f1f | |||
| e531993e00 | |||
| f370c4683c | |||
| a905ebba69 | |||
| 2b0df61b0a | |||
| ee182aa083 | |||
| 6fc7990d6b | |||
| ab47675a8a | |||
| 4d703fd433 | |||
| 9e591c6f0d | |||
| 7ecb92ae94 | |||
| 2429011d65 | |||
| f48c30df8b | |||
| 5465f1b364 | |||
| e5875c6d73 | |||
| 5dc53e24e7 | |||
| d3e9575b75 | |||
| a22eec22dc | |||
| dad518a6bf | |||
| db5f8abac1 | |||
| a933ea551c | |||
| 6ceabdb746 | |||
| 834def4b70 | |||
| 06b9fcc0a4 | |||
| 3ecbdb5ae8 | |||
| 36d35fc196 | |||
| 78473d9a1b | |||
| 5847e8b63a | |||
| c18091798a | |||
| 9e84266b60 | |||
| 720478670e | |||
| 0b1335645a | |||
| 706a54df6e | |||
| 83a76757d6 | |||
| d8cdf58222 |
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Game and version
|
||||
*name of game, version of game*
|
||||
|
||||
## Version of spice2x
|
||||
*version of spice, this can be seen in the About tab, and in log.txt*
|
||||
|
||||
*please remember to test the LATEST beta version before posting a bug about it**
|
||||
|
||||
## Describe the issue
|
||||
*what's the issue?*
|
||||
|
||||
## Attached log.txt file, if available
|
||||
> [!WARNING]
|
||||
> Please make sure you remove any personally identifiable information from the log file.
|
||||
> Please set `-loglevel` to `all` before launching the game for more verbose logs.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Documentation update
|
||||
about: For suggesting edits to the wiki page.
|
||||
title: ''
|
||||
labels: documentation
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
on: [push, pull_request]
|
||||
name: Continuous Integration
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
fw-ci:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./src/spice2x
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set ccache environment variables
|
||||
run: |
|
||||
echo "CCACHE_DIR=${{ github.workspace }}/src/spice2x/.ccache" >> $GITHUB_ENV
|
||||
- name: Install ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
- name: Calculate commit SHA
|
||||
id: vars
|
||||
run: |
|
||||
calculatedSha=$(git rev-parse --short ${{ github.sha }})
|
||||
echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV
|
||||
- name: Compile
|
||||
run: ./build_docker.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }}
|
||||
path: src/spice2x/bin
|
||||
if-no-files-found: error
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
.vscode/*
|
||||
|
||||
# JetBrains IDEs
|
||||
.idea
|
||||
.idea_modules
|
||||
*.iws
|
||||
|
||||
# CMake
|
||||
cmake-build*
|
||||
|
||||
# user config
|
||||
build_all.local.sh
|
||||
build_docker.local.sh
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
## Contributing
|
||||
Baseline rules for patch submissions are as follows. Any patches violating the rules below will not be accepted.
|
||||
|
||||
To contribute, fork the repo (just the main branch), make changes in your fork, and contribute to upstream by opening a pull request to the main repo.
|
||||
|
||||
### Adding support for games
|
||||
|
||||
* No BomberGirl, as long as the Free To Play service is still alive.
|
||||
* No support for that one touch screen game that dispenses plushies of mouse character (`O26`).
|
||||
* No support for eaCloud ("Konasute") games.
|
||||
* For a new game that was never supported (not a new version of a game, but rather a new series): please wait 1 year after official AC release in Japan.
|
||||
* For new version of an already supported game: proceed with caution and use generally-accepted community guidelines.
|
||||
|
||||
### Forbidden "features" that will never be accepted
|
||||
|
||||
* Performing decryption of encrypted files
|
||||
* Including (bundling), distributing, or linking to game data, or making it easier to find/download game data
|
||||
* Any features that [phone home](https://en.wikipedia.org/wiki/Phoning_home)
|
||||
* This includes automatic updaters.
|
||||
* Exception: we have allowed the "patch import from URL" feature, but this has only been done under very careful considerations and plenty of warnings to the user.
|
||||
|
||||
### Avoiding regressions
|
||||
|
||||
The biggest risk in making code changes to spice is the risk of regressions. There is a **lot** of shared code used by many different games, reaching back game versions that are more than a decade old. It is practically impossible to test all supported games and versions.
|
||||
|
||||
Additionally, there's a lot of code that get exercised on specific hardware - including hardware that you probably don't have access to (e.g., ICCA reader, real cabinet I/O, specific model of a IIDX controller, particular brand of touch screens...)
|
||||
|
||||
Therefore, when making code changes, please be extremely careful about containing / scoping your changes. Make targeted bug fixes scoped to handful of game versions and hardware configuration. When adding new features, make it off by default, unless there is a really good reason to make it the default. If you make a new default, add an option that disables it so that users can opt out as needed.
|
||||
|
||||
#### Config file compatibility
|
||||
|
||||
Do not change the names of buttons binds, analogs, strings for command line parameters, etc - since they are used as unique identifiers in config files. If they are changed, you will introduce an incompatibility with previous versions of config file.
|
||||
|
||||
### Code quality requirements
|
||||
|
||||
* Test for regressions, at least in and around the component you are modifying:
|
||||
* All currently supported games / versions must continue to work.
|
||||
* Backwards compatibility must be preserved, unless there is a really good reason to break it. This includes (but not limited to): global/local config files, command line parameters, game patches, device interop (e.g., card readers), and SpiceAPI / Companion interop.
|
||||
* Reasonable level of compatibility with the last release of original spicetools is expected. The stated goal of spice2x is to be a drop-in replacement for spicetools.
|
||||
* Simply put, if someone has an existing install of spicetools/spice2x, copying over new version of spice2x should not result in vastly different behavior or major loss of functionality.
|
||||
* Make sure you compile with the included Docker script and ensure you do not introduce **any** new compiler warnings or build breaks. The Docker script is the standard build environment, your custom Linux build environment or MSVC can be used during development, but you must validate the final build using Docker.
|
||||
* Do not make code changes in unrelated areas; i.e., do not run code linters and auto-formatters for parts of the code that you didn't modify.
|
||||
* Try to submit smaller chunks of code, instead one gigantic patch. For example, don't submit a patch for "Improve feature XYZ"; instead, submit "Change how A works to prepare for feature XYZ" "Refactor B for feature XYZ" "Add feature B to enable feature XYZ".
|
||||
* Write to the log for anything useful - it helps immensely with troubleshooting and debugging. At the same time though, avoid spamming the log for something trivial.
|
||||
|
||||
### UI text
|
||||
spice2x has a global audience; majority of the user base do not speak English as their first language.
|
||||
|
||||
Use simple English, avoid colloquialism, and use concise language, even if it's slightly technical.
|
||||
|
||||
### Using OS APIs
|
||||
|
||||
Avoid making permanent changes to user's OS configuration. For example, spice must not make a call to set power profile to Maximum Performance, or switch default audio device. Making the reverse call to restore settings on game shutdown is **not** good enough; there is no guarantee that spice will gracefully shutdown, since games (or spice) can crash. This is to avoid inadvertently putting user's PC into a bad state, which can be seen as malware-like behavior.
|
||||
|
||||
Watch out for legacy OS compatibility. Currently, the minimum support floor is Windows 7. If you use any Windows API, make sure it's supported in Windows 7. If you need to use API that is not present in Windows 7, you must not directly link against it, otherwise spice will fail to launch on older OSes. Take a look at Windows 8 touch code (win8.cpp) for examples on how to discover OS APIs via pointers.
|
||||
|
||||
### Code style requirements
|
||||
* Indents are four spaces.
|
||||
* Try to keep under 100 characters per line. This includes comments and string literals.
|
||||
* Avoid making unnecessary formatting or whitespace changes in a PR; e.g., watch out for text editor automatically stripping trailing whitespace. It pollutes the diff and slows down code review.
|
||||
* For signed/unsigned integer types, prefer `cstdint` types; e.g., use `int32_t`, `uint64_t` and not `int` and `unsigned long long`.
|
||||
* Always use \{ curly braces \} when appropriate; do not omit them even when it's optional; such as `for` `if` `else`, etc.
|
||||
|
||||
OK:
|
||||
```c
|
||||
if (conditional) {
|
||||
DoSomething();
|
||||
}
|
||||
```
|
||||
|
||||
Not OK:
|
||||
```c
|
||||
if (conditional)
|
||||
DoSomething();
|
||||
```
|
||||
|
||||
Not OK:
|
||||
```c
|
||||
if (conditional) DoSomething();
|
||||
```
|
||||
|
||||
* Opening curly braces should appear at the end, not in a line on its own.
|
||||
|
||||
OK:
|
||||
```c
|
||||
if (conditional) {
|
||||
DoSomething();
|
||||
}
|
||||
```
|
||||
|
||||
Not OK:
|
||||
```c
|
||||
if (conditional)
|
||||
{
|
||||
DoSomething();
|
||||
}
|
||||
```
|
||||
|
||||
* Please give [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) a read. A few things to point out in spice codebase:
|
||||
* We don't use GSL.
|
||||
* Don't throw exceptions.
|
||||
* Stick to smart pointers. For interfacing with C Win32 API and raw buffers, use `unique_plain_ptr`.
|
||||
* Writing in C is also completely acceptable.
|
||||
* Other than that, there are no strict rules for code formatting, but please attempt to emulate the style around the code you are modifying.
|
||||
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -1 +1,61 @@
|
||||
# spice2x.github.io
|
||||
<br/><br/><br/><br/><br/><br/><br/><br/>
|
||||
|
||||
# This is the GitHub page for code development. If you just want to download the latest spice2x EXE, visit the homepage instead:
|
||||
|
||||
# 🌶️🌶️ [https://spice2x.github.io/](https://spice2x.github.io/) 🌶️🌶️
|
||||
|
||||
# If you have problems, read [Known Issues (FAQ)](https://github.com/spice2x/spice2x.github.io/wiki/Known-issues) wiki page first, most of your questions are already answered.
|
||||
|
||||
<br/><br/><br/><br/><br/><br/><br/><br/>
|
||||
|
||||
# spice2x
|
||||
|
||||
## Overview
|
||||
|
||||
spice2x is a continuation of SpiceTools project. Original development of [SpiceTools](https://github.com/spicetools/spicetools) has stalled in 2022; spice2x is led by a new group of developers.
|
||||
|
||||
spice2x team does not provide any tools to circumvent software copy protection, nor distribute any copyright-protected game data.
|
||||
|
||||
[Features](https://github.com/spice2x/spice2x.github.io/wiki/spice2x-features)
|
||||
|
||||
[List of supported games](https://github.com/spice2x/spice2x.github.io/wiki/List-of-supported-games)
|
||||
|
||||
## How do I set up game data & start playing?
|
||||
|
||||
By policy,
|
||||
|
||||
* we do **NOT** provide links on where to acquire game data
|
||||
* we do **NOT** provide tools or instructions on decrypting data
|
||||
* we do **NOT** provide guides on setting up & basic troubleshooting
|
||||
|
||||
please do not ask for these, as it will never happen here.
|
||||
|
||||
## Submitting to the Issue Tracker
|
||||
|
||||
**Please file bugs if you find them!** If you just complain about something in Discord servers, we can't see them, so they'll never be fixed.
|
||||
|
||||
Rules for filing a new issue or adding comments to existing issues in the tracker:
|
||||
|
||||
* Low effort submissions will be simply deleted, and repeated attempts will get you banned.
|
||||
* Check the [known issues](https://github.com/spice2x/spice2x.github.io/wiki/Known-issues) page first before reporting a new issue. If you have some new information or workarounds for a known issue, you can file a documentation bug as well.
|
||||
* Use the search function and see if there is an existing issue.
|
||||
* This is not the place to obtain a guide or receive basic troubleshooting.
|
||||
* Don't file a bug demanding game XYZ to be supported.
|
||||
* **Do not upload game data - any part of it, XML files included!**
|
||||
* **Do not mention or link to external websites that distribute game data!**
|
||||
* **Do not link to external websites that provide guides on how to run games!**
|
||||
|
||||
New GitHub accounts are prevented from creating new issues to prevent spam. Maintainers of this project reserve the right to close or delete any issues that violate the rules above, or any low effort issues. If you ask for very basic troubleshooting help with setting up a game, we'll just delete them without warning.
|
||||
|
||||
## Contributing
|
||||
**We encourage the community to submit bugs and suggestions via the issue tracker, and to contribute code changes by opening pull requests**
|
||||
|
||||
If you want to resolve any reported (or not reported) bugs, implement features, add support for new games, or fix a [known issue](https://github.com/spice2x/spice2x.github.io/wiki/Known-issues) - feel free to reach out via the Issue tracker or by opening a Pull Request. All submitted code is assumed to be GPLv3 compliant.
|
||||
|
||||
We explicitly do **NOT** have a Discord server for dicussions - we try to do everything out in the open on this GitHub repo. That being said we haven't opened a Discussions tab here because we know it's going to be immediately filled with support requests.
|
||||
|
||||
Please see [CONTRIBUTING page](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md) for a full list of guidelines when submitting code.
|
||||
|
||||
## Additional information
|
||||
|
||||
Please read [README.md](https://github.com/spice2x/spice2x.github.io/blob/main/src/spice2x/README.md) inside `src/spice2x`.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
> [!NOTE]
|
||||
> Before submitting code changes...
|
||||
> * Please do note that this is a GPL v3.0 open source project.
|
||||
> * Please read the [CONTRIBUTING](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md) guide.
|
||||
> * Maintainers reserve the right to reject or modify your submission without reason.
|
||||
> * No new compiler warnings must be introduced. Check the CI build results.
|
||||
>
|
||||
> Feel free to remove this section after you have read it.
|
||||
|
||||
## Link to GitHub Issue or related Pull Request, if one exists
|
||||
#0
|
||||
|
||||
## Description of change
|
||||
*what does this code do?*
|
||||
|
||||
## Testing
|
||||
*how was the code tested?*
|
||||
@@ -0,0 +1,5 @@
|
||||
bin/**
|
||||
dist/**
|
||||
docker/**
|
||||
cmake-build*
|
||||
.ccache/**
|
||||
@@ -0,0 +1,26 @@
|
||||
.vscode/*
|
||||
|
||||
# JetBrains IDEs
|
||||
.idea
|
||||
.idea_modules
|
||||
*.iws
|
||||
|
||||
# CMake
|
||||
cmake-build*
|
||||
|
||||
# user config
|
||||
build_all.local.sh
|
||||
build_docker.local.sh
|
||||
|
||||
# output from build script
|
||||
bin/*
|
||||
dist/*
|
||||
|
||||
external/cv2pdb/*
|
||||
|
||||
.ccache/*
|
||||
|
||||
# Visual Studio
|
||||
.vs
|
||||
out
|
||||
CMakeSettings.json
|
||||
@@ -0,0 +1,883 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
project(spicetools)
|
||||
include(CheckIPOSupported)
|
||||
|
||||
# set language level
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
|
||||
# niceities for vscode
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
# for RapidJSON
|
||||
add_compile_definitions(RAPIDJSON_HAS_STDSTRING)
|
||||
|
||||
if(MSVC)
|
||||
# disable intermediate manifest
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no")
|
||||
|
||||
# disable warnings about using non _s variants like strncpy
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
# disable warnings about using deprecated winsock2 functions
|
||||
add_compile_definitions(_WINSOCK_DEPRECATED_NO_WARNINGS)
|
||||
|
||||
# RapidJSON does this
|
||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||
|
||||
# define M_PI
|
||||
add_compile_definitions(_USE_MATH_DEFINES)
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DYNAMICBASE:NO")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DYNAMICBASE:NO")
|
||||
|
||||
set(USE_STATIC_MSVCRT ON CACHE BOOL "If enabled, will force the use of static crt instead of dynamic")
|
||||
|
||||
if(USE_STATIC_MSVCRT)
|
||||
# use statically linked runtime
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
set(CompilerFlags
|
||||
CMAKE_CXX_FLAGS
|
||||
CMAKE_CXX_FLAGS_DEBUG
|
||||
CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS
|
||||
CMAKE_C_FLAGS_DEBUG
|
||||
CMAKE_C_FLAGS_RELEASE
|
||||
)
|
||||
|
||||
foreach(CompilerFlag ${CompilerFlags})
|
||||
string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# disable C4996 "The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name" warning
|
||||
add_compile_options("/wd4996")
|
||||
|
||||
# cleanup windows.h includes
|
||||
add_compile_options("/DNOMINMAX")
|
||||
|
||||
# cleanup weird types
|
||||
add_compile_options("/DWINBOOL=BOOL")
|
||||
|
||||
# add support for the deprecated std::result_of in c++20
|
||||
add_compile_options("/D_HAS_DEPRECATED_RESULT_OF")
|
||||
|
||||
# enable build paralellization
|
||||
add_compile_options("/MP")
|
||||
|
||||
# enable edit and continue in Debug
|
||||
add_compile_options("$<$<CONFIG:DEBUG>:/ZI>")
|
||||
add_link_options("$<$<CONFIG:DEBUG>:/SAFESEH:NO>")
|
||||
|
||||
# enable fast pdb generation in debug
|
||||
add_link_options("$<$<CONFIG:DEBUG>:/DEBUG:FASTLINK>")
|
||||
|
||||
# enable pdb generation for release builds
|
||||
add_compile_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/Zi>")
|
||||
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/DEBUG:FULL>")
|
||||
|
||||
# enable COMDAT folding for even smaller release builds
|
||||
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/OPT:ICF>")
|
||||
|
||||
# always use UTF-8 (fix 4819)
|
||||
add_compile_options("/utf-8")
|
||||
|
||||
# spectre mitigation warning
|
||||
add_compile_options("/wd5045")
|
||||
|
||||
# implicit type convert warnings
|
||||
add_compile_options("/wd4244")
|
||||
add_compile_options("/wd4267")
|
||||
add_compile_options("/wd4305")
|
||||
|
||||
# unreferenced local variable
|
||||
add_compile_options("/wd4101")
|
||||
|
||||
# warning in winbase.h??
|
||||
add_compile_options("/wd5039")
|
||||
|
||||
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# disable warnings about using non _s variants like strncpy
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
# disable warnings about using deprecated winsock2 functions
|
||||
add_compile_definitions(_WINSOCK_DEPRECATED_NO_WARNINGS)
|
||||
|
||||
# RapidJSON does this
|
||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||
|
||||
add_compile_options("-Wno-nontrivial-memcall") # imgui, rapidjson
|
||||
add_compile_options("-Wno-deprecated-builtins") # robin_hood
|
||||
add_compile_options("-Wno-deprecated-declarations") # rapidjson
|
||||
add_compile_options("-Wno-inconsistent-missing-override") # headsocket
|
||||
add_compile_options("-Wno-microsoft-exception-spec") # gcc/clang have different noexcept specifiers on d3d9 COM interfaces
|
||||
|
||||
# warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
||||
|
||||
# static linking
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static")
|
||||
else()
|
||||
|
||||
# warnings as errors
|
||||
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
|
||||
|
||||
# warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # enable stuff
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pointer-arith") # but we love pointer arithmetic :)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # since CLion does clang pragmas
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-address") # to allow checking function pointers for null
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-cast-function-type") # we actually do this a lot
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-class-memaccess") # RapidJSON does this
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") # RapidJSON issue
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") # for all those stubs
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter") # for all those stubs
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-stringop-truncation") # since we do that from time to time
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs") # for our logging system
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes") # fmtlib workaround
|
||||
|
||||
# release flags
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Release")
|
||||
|
||||
# hide ident strings
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-ident -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-ident -ffunction-sections -fdata-sections")
|
||||
|
||||
# a change in the linker caused the executable to be loaded above 4GB base virtual address
|
||||
# https://github.com/msys2/MINGW-packages/pull/6880
|
||||
# some games crash if some DLLS load above 4GB VA, so manually set base address to standard 32-bit VA,
|
||||
# and might as well double make sure ASLR is disabled here
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
|
||||
|
||||
# set visibility to hidden
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
|
||||
# remove symbol table and relocation information
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
|
||||
|
||||
# performance
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -pipe")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -pipe")
|
||||
|
||||
# ensure frame pointers are enabled
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-omit-frame-pointer")
|
||||
|
||||
# no debug
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
|
||||
# file prefix map for relative working directory
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||
endif()
|
||||
|
||||
# release with debug info flags
|
||||
if(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
|
||||
|
||||
# hide ident strings
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections")
|
||||
|
||||
# linker fix to load below 4GB
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
|
||||
|
||||
# set visibility to hidden
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
|
||||
# generate dwarf
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -gdwarf")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -gdwarf")
|
||||
|
||||
# ensure frame pointers are enabled
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
|
||||
# debug flags
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
|
||||
# generate dwarf
|
||||
set(CMAKE_C_FLAGS_DEBUG "-gdwarf")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf")
|
||||
|
||||
# linker fix to load below 4GB
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
|
||||
|
||||
# enable debug symbols on level 3 and keep frame pointers
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer")
|
||||
|
||||
# optimize for debugging
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og -pipe")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -pipe")
|
||||
endif()
|
||||
|
||||
# static linking
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static -static-libgcc")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -static-libgcc -static-libstdc++")
|
||||
endif()
|
||||
|
||||
# default defines
|
||||
add_compile_definitions(
|
||||
WIN32_LEAN_AND_MEAN
|
||||
_WIN32_IE=0x0400
|
||||
)
|
||||
|
||||
# acioemu log
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACIOEMU_LOG")
|
||||
|
||||
# add project directory to include path so we can comfortably import
|
||||
include_directories(${spicetools_SOURCE_DIR} ${spicetools_SOURCE_DIR}/external/imgui)
|
||||
|
||||
# add external libraries
|
||||
add_subdirectory(external/fmt EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/discord-rpc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/hash-library EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/imgui EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/minhook EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/cpu_features EXCLUDE_FROM_ALL)
|
||||
|
||||
# set link time optimizations (disabled for Debug builds for speed, disabled
|
||||
# for RelWithDebInfo builds due to "lto1: error: two or more sections for"
|
||||
# errors)
|
||||
check_ipo_supported()
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO OFF)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
||||
|
||||
# resources
|
||||
###########
|
||||
set_source_files_properties(build/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(build/icon.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/icon.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/Win32D.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(build/manifest64.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(stubs/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
|
||||
# sources
|
||||
#########
|
||||
|
||||
set(SOURCE_FILES ${SOURCE_FILES}
|
||||
|
||||
# acio
|
||||
acio/acio.cpp
|
||||
acio/module.cpp
|
||||
acio/pix/pix.cpp
|
||||
acio/core/core.cpp
|
||||
acio/hgth/hgth.cpp
|
||||
acio/bmpu/bmpu.cpp
|
||||
acio/hbhi/hbhi.cpp
|
||||
acio/hdxs/hdxs.cpp
|
||||
acio/kfca/kfca.cpp
|
||||
acio/i36g/i36g.cpp
|
||||
acio/panb/panb.cpp
|
||||
acio/icca/icca.cpp
|
||||
acio/j32d/j32d.cpp
|
||||
acio/bi2a/bi2a.cpp
|
||||
acio/klpa/klpa.cpp
|
||||
acio/mdxf/mdxf.cpp
|
||||
acio/pjei/pjei.cpp
|
||||
acio/pjec/pjec.cpp
|
||||
acio/i36i/i36i.cpp
|
||||
acio/nddb/nddb.cpp
|
||||
acio/la9a/la9a.cpp
|
||||
|
||||
# acioemu
|
||||
acioemu/acioemu.cpp
|
||||
acioemu/device.cpp
|
||||
acioemu/handle.cpp
|
||||
acioemu/icca.cpp
|
||||
|
||||
# acio2emu
|
||||
acio2emu/handle.cpp
|
||||
acio2emu/packet.cpp
|
||||
acio2emu/firmware/bi2x.cpp
|
||||
|
||||
# api
|
||||
api/controller.cpp
|
||||
api/websocket.cpp
|
||||
api/request.cpp
|
||||
api/response.cpp
|
||||
api/module.cpp
|
||||
api/modules/card.cpp
|
||||
api/modules/buttons.cpp
|
||||
api/modules/capture.cpp
|
||||
api/modules/analogs.cpp
|
||||
api/modules/lights.cpp
|
||||
api/modules/memory.cpp
|
||||
api/modules/coin.cpp
|
||||
api/modules/info.cpp
|
||||
api/modules/keypads.cpp
|
||||
api/modules/control.cpp
|
||||
api/modules/touch.cpp
|
||||
api/modules/iidx.cpp
|
||||
api/serial.cpp
|
||||
api/modules/drs.cpp
|
||||
api/modules/lcd.cpp
|
||||
api/modules/ddr.cpp
|
||||
api/modules/resize.cpp
|
||||
|
||||
# avs
|
||||
avs/core.cpp
|
||||
avs/ea3.cpp
|
||||
avs/game.cpp
|
||||
avs/automap.cpp
|
||||
avs/ssl.cpp
|
||||
|
||||
# build
|
||||
build/defs.cpp
|
||||
|
||||
# cfg
|
||||
cfg/spicecfg.cpp
|
||||
cfg/analog.cpp
|
||||
cfg/game.cpp
|
||||
cfg/button.cpp
|
||||
cfg/config.cpp
|
||||
cfg/api.cpp
|
||||
cfg/option.cpp
|
||||
cfg/light.cpp
|
||||
cfg/configurator.cpp
|
||||
cfg/configurator_wnd.cpp
|
||||
cfg/screen_resize.cpp
|
||||
cfg/controller_presets.cpp
|
||||
|
||||
# easrv
|
||||
easrv/easrv.cpp
|
||||
easrv/smartea.cpp
|
||||
|
||||
# external asio
|
||||
external/asio/asiolist.cpp
|
||||
|
||||
# external cardio
|
||||
external/cardio/cardio_hid.cpp
|
||||
external/cardio/cardio_window.cpp
|
||||
external/cardio/cardio_runner.cpp
|
||||
|
||||
# external misc
|
||||
external/stackwalker/stackwalker.cpp
|
||||
external/tinyxml2/tinyxml2.cpp
|
||||
external/http-parser/http_parser.c
|
||||
external/usbhidusage/usb-hid-usage.c
|
||||
external/toojpeg/toojpeg.cpp
|
||||
external/scard/scard.cpp
|
||||
|
||||
# games
|
||||
games/game.cpp
|
||||
games/io.cpp
|
||||
games/shared/lcdhandle.cpp
|
||||
games/shared/printer.cpp
|
||||
games/shared/twtouch.cpp
|
||||
games/popn/popn.cpp
|
||||
games/popn/io.cpp
|
||||
games/popn/bi3a_hook.cpp
|
||||
games/bbc/bbc.cpp
|
||||
games/bbc/io.cpp
|
||||
games/hpm/hpm.cpp
|
||||
games/hpm/io.cpp
|
||||
games/iidx/iidx.cpp
|
||||
games/iidx/io.cpp
|
||||
games/iidx/poke.cpp
|
||||
games/iidx/bi2a.cpp
|
||||
games/iidx/bi2x.cpp
|
||||
games/iidx/bi2x_hook.cpp
|
||||
games/iidx/ezusb.cpp
|
||||
games/iidx/legacy_camera.cpp
|
||||
games/iidx/local_camera.cpp
|
||||
games/iidx/camera.cpp
|
||||
games/iidx/mf_wrappers.cpp
|
||||
games/sdvx/bi2x_hook.cpp
|
||||
games/sdvx/sdvx.cpp
|
||||
games/sdvx/io.cpp
|
||||
games/sdvx/camera.cpp
|
||||
games/jb/jb.cpp
|
||||
games/jb/io.cpp
|
||||
games/nost/nost.cpp
|
||||
games/nost/io.cpp
|
||||
games/nost/poke.cpp
|
||||
games/gitadora/gitadora.cpp
|
||||
games/gitadora/io.cpp
|
||||
games/gitadora/handle.cpp
|
||||
games/gitadora/j32d.cpp
|
||||
games/gitadora/j33i.cpp
|
||||
games/gitadora/bi2x_hook.cpp
|
||||
games/mga/mga.cpp
|
||||
games/mga/io.cpp
|
||||
games/mga/gunio.cpp
|
||||
games/sc/sc.cpp
|
||||
games/sc/io.cpp
|
||||
games/rb/rb.cpp
|
||||
games/rb/io.cpp
|
||||
games/rb/touch.cpp
|
||||
games/bs/bs.cpp
|
||||
games/bs/io.cpp
|
||||
games/rf3d/rf3d.cpp
|
||||
games/rf3d/io.cpp
|
||||
games/museca/io.cpp
|
||||
games/museca/museca.cpp
|
||||
games/dea/dea.cpp
|
||||
games/dea/io.cpp
|
||||
games/qma/qma.cpp
|
||||
games/qma/io.cpp games/qma/ezusb.cpp
|
||||
games/ddr/ddr.cpp
|
||||
games/ddr/io.cpp
|
||||
games/ddr/p3io/foot.cpp
|
||||
games/ddr/p3io/p3io.cpp
|
||||
games/ddr/p3io/sate.cpp
|
||||
games/ddr/p3io/usbmem.cpp
|
||||
games/ddr/p4io/p4io.cpp
|
||||
games/ddr/p4io/p4io.h
|
||||
games/mfc/mfc.cpp
|
||||
games/mfc/io.cpp
|
||||
games/ftt/ftt.cpp
|
||||
games/ftt/io.cpp
|
||||
games/loveplus/loveplus.cpp
|
||||
games/loveplus/io.cpp
|
||||
games/scotto/scotto.cpp
|
||||
games/scotto/io.cpp
|
||||
games/drs/drs.cpp
|
||||
games/drs/io.cpp
|
||||
games/drs/rgb_cam.cpp
|
||||
games/we/we.cpp
|
||||
games/we/io.cpp
|
||||
games/we/touchpanel.cpp
|
||||
games/shogikai/shogikai.cpp
|
||||
games/shogikai/io.cpp
|
||||
games/otoca/otoca.cpp
|
||||
games/otoca/io.cpp
|
||||
games/otoca/p4io.cpp
|
||||
games/silentscope/silentscope.cpp
|
||||
games/silentscope/io.cpp
|
||||
games/pcm/pcm.cpp
|
||||
games/pcm/io.cpp
|
||||
games/onpara/onpara.cpp
|
||||
games/onpara/io.cpp
|
||||
games/onpara/westboard.cpp
|
||||
games/onpara/touchpanel.cpp
|
||||
games/bc/bc.cpp
|
||||
games/bc/io.cpp
|
||||
games/ccj/ccj.cpp
|
||||
games/ccj/io.cpp
|
||||
games/ccj/bi2x_hook.cpp
|
||||
games/ccj/trackball.cpp
|
||||
games/qks/qks.cpp
|
||||
games/qks/io.cpp
|
||||
games/qks/bi2x_hook.cpp
|
||||
games/mfg/mfg.cpp
|
||||
games/mfg/io.cpp
|
||||
games/mfg/bi2a_hook.cpp
|
||||
games/pc/pc.cpp
|
||||
games/pc/io.cpp
|
||||
games/pc/bi2x_hook.cpp
|
||||
|
||||
# hooks
|
||||
hooks/audio/acm.cpp
|
||||
hooks/audio/audio.cpp
|
||||
hooks/audio/buffer.cpp
|
||||
hooks/audio/mme.cpp
|
||||
hooks/audio/util.cpp
|
||||
hooks/audio/backends/dsound/dsound_backend.cpp
|
||||
hooks/audio/backends/mmdevice/audio_endpoint_volume.cpp
|
||||
hooks/audio/backends/mmdevice/device.cpp
|
||||
hooks/audio/backends/mmdevice/device_collection.cpp
|
||||
hooks/audio/backends/mmdevice/device_enumerator.cpp
|
||||
hooks/audio/backends/wasapi/audio_client.cpp
|
||||
hooks/audio/backends/wasapi/audio_render_client.cpp
|
||||
hooks/audio/backends/wasapi/dummy_audio_client.cpp
|
||||
hooks/audio/backends/wasapi/dummy_audio_clock.cpp
|
||||
hooks/audio/backends/wasapi/dummy_audio_render_client.cpp
|
||||
hooks/audio/backends/wasapi/dummy_audio_session_control.cpp
|
||||
hooks/audio/backends/wasapi/low_latency_client.cpp
|
||||
hooks/audio/backends/wasapi/util.cpp
|
||||
hooks/audio/implementations/asio.cpp
|
||||
hooks/audio/implementations/wave_out.cpp
|
||||
hooks/avshook.cpp
|
||||
hooks/cfgmgr32hook.cpp
|
||||
hooks/debughook.cpp
|
||||
hooks/devicehook.cpp
|
||||
hooks/graphics/graphics.cpp
|
||||
hooks/graphics/graphics_windowed.cpp
|
||||
hooks/graphics/nvapi_hook.cpp
|
||||
hooks/graphics/nvenc_hook.cpp
|
||||
hooks/graphics/backends/d3d9/d3d9_backend.cpp
|
||||
hooks/graphics/backends/d3d9/d3d9_device.cpp
|
||||
hooks/graphics/backends/d3d9/d3d9_fake_swapchain.cpp
|
||||
hooks/graphics/backends/d3d9/d3d9_swapchain.cpp
|
||||
hooks/graphics/backends/d3d9/d3d9_texture.cpp
|
||||
hooks/input/dinput8/fake_backend.cpp
|
||||
hooks/input/dinput8/fake_device.cpp
|
||||
hooks/input/dinput8/hook.cpp
|
||||
hooks/lang.cpp
|
||||
hooks/libraryhook.cpp
|
||||
hooks/networkhook.cpp
|
||||
hooks/powrprof.cpp
|
||||
#hooks/rom.cpp
|
||||
hooks/setupapihook.cpp
|
||||
hooks/sleephook.cpp
|
||||
hooks/unisintrhook.cpp
|
||||
hooks/winuser.cpp
|
||||
|
||||
# launcher
|
||||
launcher/launcher.cpp
|
||||
launcher/signal.cpp
|
||||
launcher/superexit.cpp
|
||||
launcher/logger.cpp
|
||||
launcher/richpresence.cpp
|
||||
launcher/shutdown.cpp
|
||||
launcher/options.cpp
|
||||
|
||||
# misc
|
||||
misc/bt5api.cpp
|
||||
misc/clipboard.cpp
|
||||
misc/device.cpp
|
||||
misc/eamuse.cpp
|
||||
misc/extdev.cpp
|
||||
misc/nativetouchhook.cpp
|
||||
misc/sciunit.cpp
|
||||
misc/sde.cpp
|
||||
misc/wintouchemu.cpp
|
||||
misc/ami2000.cpp
|
||||
|
||||
# nvapi
|
||||
nvapi/nvapi.cpp
|
||||
|
||||
# overlay
|
||||
overlay/overlay.cpp
|
||||
overlay/window.cpp
|
||||
overlay/imgui/extensions.cpp
|
||||
overlay/imgui/impl_spice.cpp
|
||||
overlay/imgui/impl_sw.cpp
|
||||
overlay/windows/acio_status_buffers.cpp
|
||||
overlay/windows/camera_control.cpp
|
||||
overlay/windows/card_manager.cpp
|
||||
overlay/windows/drs_dancefloor.cpp
|
||||
overlay/windows/gfdm_sub.cpp
|
||||
overlay/windows/screen_resize.cpp
|
||||
overlay/windows/sdvx_sub.cpp
|
||||
overlay/windows/config.cpp
|
||||
overlay/windows/controller_presets.cpp
|
||||
overlay/windows/control.cpp
|
||||
overlay/windows/eadev.cpp
|
||||
overlay/windows/fps.cpp
|
||||
overlay/windows/generic_sub.cpp
|
||||
overlay/windows/iidx_seg.cpp
|
||||
overlay/windows/iidx_sub.cpp
|
||||
overlay/windows/iopanel.cpp
|
||||
overlay/windows/iopanel_ddr.cpp
|
||||
overlay/windows/iopanel_gfdm.cpp
|
||||
overlay/windows/iopanel_iidx.cpp
|
||||
overlay/windows/keypad.cpp
|
||||
overlay/windows/log.cpp
|
||||
overlay/windows/midi.cpp
|
||||
overlay/windows/patch_manager.cpp
|
||||
overlay/windows/popn_sub.cpp
|
||||
overlay/windows/wnd_manager.cpp
|
||||
|
||||
# rawinput
|
||||
rawinput/rawinput.cpp
|
||||
rawinput/sextet.cpp
|
||||
rawinput/piuio.cpp
|
||||
rawinput/touch.cpp
|
||||
rawinput/hotplug.cpp
|
||||
rawinput/smx.cpp
|
||||
rawinput/smx.h
|
||||
rawinput/smxstage.cpp
|
||||
rawinput/smxstage.h
|
||||
rawinput/smxdedicab.cpp
|
||||
rawinput/smxdedicab.h
|
||||
rawinput/xinput.cpp
|
||||
rawinput/xinput.h
|
||||
|
||||
# reader
|
||||
reader/reader.cpp
|
||||
reader/message.cpp
|
||||
reader/structuredmessage.cpp
|
||||
reader/crypt.cpp
|
||||
|
||||
# stubs
|
||||
stubs/stubs.cpp
|
||||
|
||||
# touch
|
||||
touch/touch.cpp
|
||||
touch/touch_indicators.cpp
|
||||
touch/win7.cpp
|
||||
touch/win8.cpp
|
||||
|
||||
# util
|
||||
util/sigscan.cpp
|
||||
util/detour.cpp
|
||||
util/logging.cpp
|
||||
util/detour.cpp
|
||||
util/peb.cpp
|
||||
util/libutils.cpp
|
||||
util/fileutils.cpp
|
||||
util/resutils.cpp
|
||||
util/unity_player.cpp
|
||||
util/utils.cpp
|
||||
util/memutils.cpp
|
||||
util/rc4.cpp
|
||||
util/crypt.cpp
|
||||
util/time.cpp
|
||||
util/cpuutils.cpp
|
||||
util/netutils.cpp
|
||||
util/sysutils.cpp
|
||||
util/lz77.cpp
|
||||
util/tapeled.cpp
|
||||
util/execexe.cpp
|
||||
util/dependencies.cpp
|
||||
util/deferlog.cpp
|
||||
util/socd_cleaner.cpp
|
||||
)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
# spice.exe
|
||||
###########
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_spice
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
|
||||
PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
target_link_libraries(spicetools_spice PUBLIC winscard)
|
||||
set_target_properties(spicetools_spice PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice")
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# spice_laa.exe
|
||||
###########
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_spice_laa
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
|
||||
PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
target_link_libraries(spicetools_spice_laa PUBLIC winscard)
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa")
|
||||
target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware")
|
||||
endif()
|
||||
|
||||
# spice_linux.exe
|
||||
###########
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice_linux ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_spice_linux
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
|
||||
PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
set_target_properties(spicetools_spice_linux PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice_linux PROPERTIES OUTPUT_NAME "spice_linux")
|
||||
target_compile_definitions(spicetools_spice_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice_linux PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# spice64.exe
|
||||
#############
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest64.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice64 ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
|
||||
# do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break
|
||||
target_link_libraries(spicetools_spice64
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp mfuuid strmiids dxva2
|
||||
PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
target_link_libraries(spicetools_spice64 PUBLIC winscard)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64")
|
||||
target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# spice64_linux.exe
|
||||
#############
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest64.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice64_linux ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
|
||||
# do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break
|
||||
target_link_libraries(spicetools_spice64_linux
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp mfuuid strmiids dxva2
|
||||
PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
set_target_properties(spicetools_spice64_linux PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice64_linux PROPERTIES OUTPUT_NAME "spice64_linux")
|
||||
target_compile_definitions(spicetools_spice64_linux PRIVATE SPICE64=1)
|
||||
target_compile_definitions(spicetools_spice64_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice64_linux PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# spicecfg.exe
|
||||
##############
|
||||
|
||||
set(SOURCE_FILES ${SOURCE_FILES} launcher/options.h launcher/options.cpp)
|
||||
set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_cfg WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_cfg
|
||||
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp strmiids
|
||||
PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
target_link_libraries(spicetools_cfg PUBLIC winscard)
|
||||
set_target_properties(spicetools_cfg PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_cfg PROPERTIES OUTPUT_NAME "spicecfg")
|
||||
target_compile_definitions(spicetools_cfg PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_cfg PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# spicecfg_linux.exe
|
||||
##############
|
||||
|
||||
set(SOURCE_FILES ${SOURCE_FILES} launcher/options.h launcher/options.cpp)
|
||||
set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_cfg_linux WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_cfg_linux
|
||||
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp strmiids
|
||||
PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
set_target_properties(spicetools_cfg_linux PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_cfg_linux PROPERTIES OUTPUT_NAME "spicecfg_linux")
|
||||
target_compile_definitions(spicetools_cfg_linux PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
|
||||
target_compile_definitions(spicetools_cfg_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_cfg_linux PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# stubs
|
||||
#######
|
||||
|
||||
# kbt.dll
|
||||
set(SOURCE_FILES stubs/stubs.cpp)
|
||||
add_library(spicetools_stubs_kbt SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kbt PRIVATE fmt::fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kbt PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kbt PROPERTIES OUTPUT_NAME "kbt")
|
||||
target_compile_definitions(spicetools_stubs_kbt PRIVATE STUB=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_kbt PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# kbt.dll 64bit
|
||||
add_library(spicetools_stubs_kbt64 SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kbt64 PRIVATE fmt::fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kbt64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kbt64 PROPERTIES OUTPUT_NAME "kbt")
|
||||
target_compile_definitions(spicetools_stubs_kbt64 PRIVATE STUB=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_kbt64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# kld.dll
|
||||
set(SOURCE_FILES stubs/stubs.cpp)
|
||||
add_library(spicetools_stubs_kld SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kld PRIVATE fmt::fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kld PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kld PROPERTIES OUTPUT_NAME "kld")
|
||||
target_compile_definitions(spicetools_stubs_kld PRIVATE STUB=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_kld PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# kld.dll 64bit
|
||||
add_library(spicetools_stubs_kld64 SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kld64 PRIVATE fmt::fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kld64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kld64 PROPERTIES OUTPUT_NAME "kld")
|
||||
target_compile_definitions(spicetools_stubs_kld64 PRIVATE STUB=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_kld64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# nvcuda.dll
|
||||
set(SOURCE_FILES stubs/nvcuda.cpp)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuda.def)
|
||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_nvcuda PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# nvcuvid.dll
|
||||
set(SOURCE_FILES stubs/nvcuvid.cpp)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvcuvid.def)
|
||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# nvEncodeAPI64.dll
|
||||
set(SOURCE_FILES stubs/nvEncodeAPI64.cpp)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/nvEncodeAPI64.def)
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# cpusbxpkm.dll (32 bit)
|
||||
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
|
||||
set(RESOURCE_FILES stubs/manifest.rc)
|
||||
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} ${RESOURCE_FILES} stubs/cpusbxpkm.def)
|
||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# output directories
|
||||
####################
|
||||
|
||||
# output config
|
||||
set_target_properties(spicetools_cfg spicetools_cfg_linux
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
||||
|
||||
# output 32bit
|
||||
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
|
||||
|
||||
# output 64bit
|
||||
set_target_properties(spicetools_spice64 spicetools_spice64_linux spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvcuda spicetools_stubs_nvcuvid spicetools_stubs_nvEncodeAPI64
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64")
|
||||
@@ -0,0 +1,7 @@
|
||||
FROM spicetools/deps
|
||||
WORKDIR /src
|
||||
|
||||
COPY --from=gitroot . /src/.git
|
||||
COPY . /src/src/spice2x
|
||||
WORKDIR /src/src/spice2x
|
||||
ENTRYPOINT ["./build_all.sh"]
|
||||
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -0,0 +1,298 @@
|
||||
SpiceTools
|
||||
==========
|
||||
This is a loader for various arcade games developed by 573.
|
||||
The project is using CMake as it's build system, with a custom build
|
||||
script for making packages ready for distribution and to keep it easy
|
||||
for people not knowing how to use CMake.
|
||||
|
||||
## Building/Distribution
|
||||
|
||||
We're currently using Arch Linux for building the binaries.
|
||||
You'll need:
|
||||
- MinGW-64 packages (can be found in the AUR)
|
||||
- bash
|
||||
- git
|
||||
- zip
|
||||
- upx (optional)
|
||||
|
||||
For any other GNU/Linux distributions (or Windows lol), you're on your
|
||||
own.
|
||||
|
||||
To build the project, run:
|
||||
|
||||
$ ./build_all.sh
|
||||
|
||||
## Build Configuration
|
||||
|
||||
You can tweak some settings at the beginning of the build script. You
|
||||
might want to modify the paths of the toolchains if yours differ. It's
|
||||
also possible to disable UPX compression and source distribution for
|
||||
example.
|
||||
|
||||
If you're not using an IDE and want to run the build script from command
|
||||
line manually each time you make a change, you can set CLEAN_BUILD to 0
|
||||
so it will only compile what's needed. When modifying the resources you
|
||||
should build from scratch, since CMake isn't able to track changes of
|
||||
those files (e.g. changelog.txt, licenses.txt).
|
||||
|
||||
You can put your custom build script under "build_all.local.sh" if you
|
||||
don't want git to track the changes you made to the build settings.
|
||||
|
||||
## Debug Build
|
||||
|
||||
To get proper stackstraces, you need to build with the DEBUG setting
|
||||
enabled. This will set the CMake build type to debug and the script
|
||||
will try to make use of cv2pdb.
|
||||
Check external/cv2pdb/README.md for details on how to set it up.
|
||||
It is required to use cv2pdb to generate PDB output, because at time of
|
||||
writing, MinGW still didn't support Microsoft's proprietary file format.
|
||||
|
||||
## API
|
||||
|
||||
SpiceTools is providing a TCP/JSON based API. You can enable it with the
|
||||
`-api [PORT]` option. It's recommended to also set a password with
|
||||
`-apipass [PASS]`.
|
||||
|
||||
The protocol is meant to be simple, easy to use and with few overhead.
|
||||
To connect to the API, just open a TCP connection to the host computer
|
||||
at the specified port.
|
||||
To make a request you write the UTF-8 encoded JSON contents to the
|
||||
socket. To mark the end of the request, you need to terminate the JSON
|
||||
string with `0x00`. The server returns its answer on the same way,
|
||||
UTF-8 encoded JSON terminated with NULL.
|
||||
|
||||
To save space, by default the JSONs are without whitespace. To enable
|
||||
pretty printing, use `-apipretty`.
|
||||
|
||||
If you want to test the API server without running a game, you can
|
||||
run it headless via `-apidebug`.
|
||||
|
||||
If a password is specified, both request and response are encrypted
|
||||
using RC4 with the key being the password encoded in UTF-8.
|
||||
While only providing weak security when the password is static,
|
||||
it's simple to implement and requires no authentication protocol.
|
||||
The password is also able to change dynamically, see
|
||||
`control.session_refresh()` for details.
|
||||
|
||||
If you don't want to worry about all the details, you can just use one
|
||||
of the supplied libraries (check /api/resources). There's also a little
|
||||
remote control GUI available for python.
|
||||
|
||||
### WebSocket
|
||||
SpiceTools opens a WebSocket server on the specified port plus one.
|
||||
That means if the API is on 1337, the WebSocket server will be on 1338.
|
||||
The protocol is the very same as for the normal API, but instead of
|
||||
directly sending the data over TCP you need to send binary datapackets.
|
||||
The included dart spiceapi library also has a WebSocket implementation.
|
||||
|
||||
### Example Call
|
||||
This example inserts the a card into P1's reader slot.
|
||||
If you need more examples, you can check the example code.
|
||||
|
||||
#### Request
|
||||
```JSON
|
||||
{
|
||||
"id": 1,
|
||||
"module": "card",
|
||||
"function": "insert",
|
||||
"params": [0, "E004010000000000"]
|
||||
}
|
||||
```
|
||||
#### Response
|
||||
```JSON
|
||||
{
|
||||
"id": 1,
|
||||
"errors": [],
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
### Modules
|
||||
|
||||
For the sake of simplifying this documentation, I will describe functions
|
||||
as if they were called via a normal programming language. That means,
|
||||
besides `module`/`function` needing to be set accordingly, the `params` field
|
||||
must contain all the parameters described below. Some of them are optional.
|
||||
The returning data will always be in the `data` field of the response.
|
||||
|
||||
Errors will be reported as strings in the `errors` field. Sometimes, you can
|
||||
receive more than one error. The error messages may be changed/improved in the
|
||||
future, so you shouldn't rely on them. If the error list is empty, the function
|
||||
executed successfully.
|
||||
|
||||
The `id` of the response will always match the `id` of the request. The API
|
||||
server itself doesn't expect any logic of your ID choices, however you can use
|
||||
them to make sure your answers aren't arriving out of order. Currently it
|
||||
doesn't matter since the TCP protocol doesn't allow for out of order data,
|
||||
however this may change when/if support for UDP is being introduced. The only
|
||||
restriction is that the ID has to be a valid 64-bit unsigned integer.
|
||||
|
||||
#### Card
|
||||
- insert(index: uint, card_id: hex)
|
||||
- inserts a card which gets read by the emulated card readers for the game
|
||||
- index has to be either 0 (for P1) or 1 (for P2)
|
||||
- card_id has to be a valid 16-character hex string
|
||||
|
||||
#### Coin
|
||||
- get()
|
||||
- returns the amount of unprocessed coins in queue
|
||||
- not equal to the amount of coins shown ingame since the game may drain it
|
||||
- set(amount: int)
|
||||
- sets the amount of coins in queue for the game to process
|
||||
- insert(amount: int)
|
||||
- adds the amount to the coins in queue
|
||||
- amount is optional and defaults to 1
|
||||
- blocker_get()
|
||||
- returns the current coin blocker state (false: open, true: closed)
|
||||
|
||||
#### Info
|
||||
- avs()
|
||||
- returns a dict including the AVS model, dest, spec, rev and ext
|
||||
- launcher()
|
||||
- returns a dict including the version, compile date/time, system time and
|
||||
the arguments used to start the process
|
||||
- memory()
|
||||
- returns a dict including total, total used and bytes used by the process
|
||||
for both physical and virtual RAM
|
||||
|
||||
#### Keypads
|
||||
For all functions in this module, the keypad parameter must be
|
||||
either 0 (for P1) or 1 (for P2). Accepted keypad characters are "0" to "9" for
|
||||
the single digit numbers, "A" for the double zero and "D" for the decimal key.
|
||||
- write(keypad: uint, input: str)
|
||||
- writes all characters in input sequentially to the keypad
|
||||
- this should be used for things like PIN input
|
||||
- set(keypad: uint, key: char, ...)
|
||||
- clears all overrides, then applies each given key as override
|
||||
- if a key is overridden, it shows up as pressed in-game
|
||||
- get(keypad: uint)
|
||||
- returns all pressed keys of the specified keypad
|
||||
|
||||
#### Analogs/Buttons/Lights
|
||||
All of those three modules have equally named methods for you to call.
|
||||
- read()
|
||||
- returns an array of state objects containing name, state and a bool
|
||||
- the bool indicates if the object is active (e.g. the button was overridden)
|
||||
- write([name: str, state: float], ...)
|
||||
- applies the states as an override value
|
||||
- the device binding via the config will be ignored while an override is set
|
||||
- if an override value is set, the object will be marked as active
|
||||
- write_reset([name: str], ...)
|
||||
- removes the override value from the objects specified by name
|
||||
- if no names were passed, all overrides will be removed
|
||||
|
||||
##### Additional API for lights
|
||||
- read(name: string, ...)
|
||||
- same as read(), but you can specify light names
|
||||
- write_reset(name: str, ...)
|
||||
- same as write_reset(), but it accepts a flat list of strings
|
||||
|
||||
#### Touch
|
||||
- read()
|
||||
- returns an array of state objects containing id, x and y
|
||||
- write([id: uint, x: int, y: int], ...)
|
||||
- adds the given touch points having an unknown ID
|
||||
- overrides old touch points when the ID matches
|
||||
- write_reset(id: uint, ...)
|
||||
- removes the touch points matching one of the given IDs
|
||||
|
||||
#### Control
|
||||
This module only functions if a password is being used to communicate,
|
||||
with the single exception of session_refresh().
|
||||
- raise(signal: str)
|
||||
- raises a signal to the current process
|
||||
- signal is a string and must be one of the following values:
|
||||
"SIGABRT", "SIGFPE", "SIGILL", "SIGINT", "SIGSEGV", "SIGTERM"
|
||||
- the signal will be raised while the message is being processed
|
||||
- exit(code: int)
|
||||
- code is optional and defaults to 0
|
||||
- the process will end while the message is being processed
|
||||
- restart()
|
||||
- spawns a new instance with the same arguments as passed to the main executable
|
||||
- session_refresh()
|
||||
- generates a new secure password and applies it after the response
|
||||
- can be used to increase the security by using the password as some kind of
|
||||
session token
|
||||
- recommended to call directly after connecting and once in a while for
|
||||
persistent connections
|
||||
- shutdown()
|
||||
- tries to force shutdown the computer
|
||||
- reboot()
|
||||
- tries to force reboot the computer
|
||||
|
||||
#### Memory
|
||||
This module only functions if a password is being used to communicate.
|
||||
All offsets are specified in file offsets of the corresponding `dll_name`,
|
||||
which also means that your hex edits are applicable directly.
|
||||
- write(dll_name: str, data: hex, offset: uint)
|
||||
- writes the bytes in data to the specified offset
|
||||
- read(dll_name: str, offset: uint, size: uint)
|
||||
- reads bytes starting from offset and returns it
|
||||
- signature(dll_name: str, signature: hex, replacement: hex, offset: uint,
|
||||
usage: uint)
|
||||
- tries to find the signature in the module's memory
|
||||
- unknown bytes are masked out via "??" (e.g. "75??90????74")
|
||||
- masking out bytes works for both the signature and the replacement
|
||||
- offset is the byte difference between the offset of the found signature and
|
||||
the offset where the replacement data gets written to
|
||||
- usage is the number of the result being used for replacement, starting at 0
|
||||
- returns the offset where the replacement data gets written to
|
||||
- the replacement string can be empty, so you can only get the address of the
|
||||
signature while not actually replacing anything
|
||||
|
||||
#### IIDX
|
||||
- ticker_get()
|
||||
- returns a string of the characters displayed on the 16 segment display
|
||||
- ticker_set(text: str)
|
||||
- sets the contents of the 16 segment display and disables writes from game
|
||||
- ticker_reset()
|
||||
- re-enables writes from game
|
||||
- tapeled_get(name: str, ...)
|
||||
- returns a list containing a dict of the current tape LED states. The dict keys are:
|
||||
- `Stage Left`
|
||||
- `Stage Right`
|
||||
- `Cabinet Left`
|
||||
- `Cabinet Right`
|
||||
- `Control Panel Under`
|
||||
- `Ceiling Left`
|
||||
- `Title Left`
|
||||
- `Title Right`
|
||||
- `Ceiling Right`
|
||||
- `Touch Panel Left`
|
||||
- `Touch Panel Right`
|
||||
- `Side Panel Left Inner`
|
||||
- `Side Panel Left Outer`
|
||||
- `Side Panel Left`
|
||||
- `Side Panel Right Outer`
|
||||
- `Side Panel Right Inner`
|
||||
- `Side Panel Right`
|
||||
|
||||
#### LCD
|
||||
- info()
|
||||
- returns information about the serial LCD controller some games use
|
||||
|
||||
#### Resize
|
||||
- image_resize_enable(enable: bool)
|
||||
- enables or disables image resize state
|
||||
- image_resize_set_scene(scene: int)
|
||||
- sets the active scene for image resize state; set to 0 to disable resize
|
||||
|
||||
## Native wrapper libraries
|
||||
Spicetools provides wrapper libraries in: Arduino, C++, Dart, and Python.
|
||||
Python is the only one that is fully spec compliant.
|
||||
Other libraries may be missing features and contain bugs; please feel free to
|
||||
contribute code to fill the gaps if you work on a project using these libraries.
|
||||
|
||||
## License
|
||||
Unless otherwise noted, all files are licensed under the GPLv3.
|
||||
See the LICENSE file for the full license text.
|
||||
Files not originating from this project should be placed in "external",
|
||||
they each have their own license.
|
||||
|
||||
###### What that means for you
|
||||
- If you want to distribute the binaries, you'll have to include the
|
||||
sources, otherwise you're violating the GPL.
|
||||
- If you want to merge some of this project's code into similar
|
||||
software, that software has to be put under the GPL as well.
|
||||
You'll probably have to modify a lot anyways, so you can just use the
|
||||
source as some kind of documentation.
|
||||
@@ -0,0 +1,144 @@
|
||||
#include "acio.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "cfg/config.h"
|
||||
#include "cfg/api.h"
|
||||
#include "hooks/libraryhook.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "bi2a/bi2a.h"
|
||||
#include "bmpu/bmpu.h"
|
||||
#include "core/core.h"
|
||||
#include "hbhi/hbhi.h"
|
||||
#include "hdxs/hdxs.h"
|
||||
#include "hgth/hgth.h"
|
||||
#include "i36g/i36g.h"
|
||||
#include "i36i/i36i.h"
|
||||
#include "icca/icca.h"
|
||||
#include "j32d/j32d.h"
|
||||
#include "kfca/kfca.h"
|
||||
#include "klpa/klpa.h"
|
||||
#include "mdxf/mdxf.h"
|
||||
#include "nddb/nddb.h"
|
||||
#include "panb/panb.h"
|
||||
#include "pix/pix.h"
|
||||
#include "pjec/pjec.h"
|
||||
#include "pjei/pjei.h"
|
||||
#include "la9a/la9a.h"
|
||||
|
||||
#include "module.h"
|
||||
|
||||
// globals
|
||||
namespace acio {
|
||||
HINSTANCE DLL_INSTANCE = nullptr;
|
||||
std::vector<acio::ACIOModule *> MODULES;
|
||||
std::atomic<bool> IO_INIT_IN_PROGRESS = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* decide on hook mode used
|
||||
* libacio compiled using ICC64 sometimes doesn't leave enough space to insert the inline hooks
|
||||
* in this case, we want to use IAT instead
|
||||
*/
|
||||
static inline acio::HookMode get_hookmode() {
|
||||
#ifdef SPICE64
|
||||
return acio::HookMode::IAT;
|
||||
#else
|
||||
return acio::HookMode::INLINE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void acio::attach() {
|
||||
log_info("acio", "SpiceTools ACIO");
|
||||
IO_INIT_IN_PROGRESS = true;
|
||||
|
||||
// load settings and instance
|
||||
acio::DLL_INSTANCE = LoadLibraryA("libacio.dll");
|
||||
|
||||
/*
|
||||
* library hook
|
||||
* some games have a second DLL laying around which gets loaded dynamically
|
||||
* we just give it the same instance as the normal one so the hooks still work
|
||||
*/
|
||||
libraryhook_hook_library("libacioex.dll", acio::DLL_INSTANCE);
|
||||
libraryhook_hook_library("libacio_ex.dll", acio::DLL_INSTANCE);
|
||||
libraryhook_hook_library("libacio_old.dll", acio::DLL_INSTANCE);
|
||||
// libacioEx.dll for Road Fighters 3D
|
||||
// needed as comparisons in LoadLibrary hooks are case-sensitive
|
||||
libraryhook_hook_library("libacioEx.dll", acio::DLL_INSTANCE);
|
||||
libraryhook_enable(avs::game::DLL_INSTANCE);
|
||||
|
||||
// get hook mode
|
||||
acio::HookMode hook_mode = get_hookmode();
|
||||
|
||||
// load modules
|
||||
MODULES.push_back(new acio::BI2AModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::BMPUModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::CoreModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::HBHIModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::HDXSModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::HGTHModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::I36GModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::I36IModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::ICCAModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::J32DModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::KFCAModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::KLPAModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::MDXFModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::NDDBModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::PANBModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::PJECModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::PJEIModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::LA9AModule(acio::DLL_INSTANCE, hook_mode));
|
||||
|
||||
/*
|
||||
* PIX is special and needs another DLL.
|
||||
* we load that module only if the file exists.
|
||||
*/
|
||||
if (fileutils::file_exists(MODULE_PATH / "libacio_pix.dll")) {
|
||||
HINSTANCE pix_instance = libutils::load_library(MODULE_PATH / "libacio_pix.dll");
|
||||
MODULES.push_back(new acio::PIXModule(pix_instance, hook_mode));
|
||||
}
|
||||
|
||||
// apply modules
|
||||
for (auto &module : MODULES) {
|
||||
module->attach();
|
||||
}
|
||||
|
||||
IO_INIT_IN_PROGRESS = false;
|
||||
}
|
||||
|
||||
void acio::attach_icca() {
|
||||
log_info("acio", "SpiceTools ACIO ICCA");
|
||||
|
||||
// load instance if needed
|
||||
if (!acio::DLL_INSTANCE) {
|
||||
acio::DLL_INSTANCE = LoadLibraryA("libacio.dll");
|
||||
}
|
||||
|
||||
// get hook mode
|
||||
acio::HookMode hook_mode = get_hookmode();
|
||||
|
||||
// load single module
|
||||
auto icca_module = new acio::ICCAModule(acio::DLL_INSTANCE, hook_mode);
|
||||
icca_module->attach();
|
||||
MODULES.push_back(icca_module);
|
||||
}
|
||||
|
||||
void acio::detach() {
|
||||
|
||||
// clear modules
|
||||
while (!MODULES.empty()) {
|
||||
delete MODULES.back();
|
||||
MODULES.pop_back();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
// globals
|
||||
extern HINSTANCE DLL_INSTANCE;
|
||||
extern std::vector<acio::ACIOModule *> MODULES;
|
||||
extern std::atomic<bool> IO_INIT_IN_PROGRESS;
|
||||
|
||||
void attach();
|
||||
void attach_icca();
|
||||
void detach();
|
||||
}
|
||||
@@ -0,0 +1,821 @@
|
||||
#include "bi2a.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/ddr/io.h"
|
||||
#include "games/ddr/ddr.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "games/sdvx/io.h"
|
||||
#include "games/drs/io.h"
|
||||
#include "games/drs/drs.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/tapeled.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[272] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static unsigned int BI2A_VOLL = 0;
|
||||
static unsigned int BI2A_VOLR = 0;
|
||||
|
||||
|
||||
static bool __cdecl ac_io_bi2a_init_is_finished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_get_control_status_buffer(void *buffer) {
|
||||
|
||||
// copy buffer
|
||||
memcpy(buffer, STATUS_BUFFER, std::size(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, std::size(STATUS_BUFFER));
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
/*
|
||||
* Unmapped Buttons
|
||||
*
|
||||
* Control Bit
|
||||
* EX BUTTON 1 93
|
||||
* EX BUTTON 2 92
|
||||
* EX ANALOG 1 170-183
|
||||
* EX ANALOG 2 186-199
|
||||
*/
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::sdvx::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Test))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 19);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Service))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 18);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::CoinMech))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 17);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Start))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 85);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_A))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 84);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_B))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 83);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_C))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 82);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_D))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 81);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_L))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 80);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_R))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 95);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Headphone))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 87);
|
||||
}
|
||||
|
||||
// volume left
|
||||
const auto now = get_performance_milliseconds();
|
||||
const auto vol_l_state = socd::socd_clean(0,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||
now);
|
||||
if (vol_l_state == socd::SocdCCW) {
|
||||
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
} else if (vol_l_state == socd::SocdCW) {
|
||||
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
const auto vol_r_state = socd::socd_clean(1,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||
now);
|
||||
if (vol_r_state == socd::SocdCCW) {
|
||||
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
} else if (vol_r_state == socd::SocdCW) {
|
||||
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// update volumes
|
||||
auto &analogs = games::sdvx::get_analogs();
|
||||
auto vol_left = BI2A_VOLL;
|
||||
auto vol_right = BI2A_VOLR;
|
||||
if (analogs.at(0).isSet() || analogs.at(1).isSet()) {
|
||||
vol_left += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_L)) * 1023.99f);
|
||||
vol_right += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_R)) * 1023.99f);
|
||||
}
|
||||
|
||||
// proper loops
|
||||
vol_left %= 1024;
|
||||
vol_right %= 1024;
|
||||
|
||||
// save volumes in buffer
|
||||
*((uint16_t*) &STATUS_BUFFER[17]) = (uint16_t) ((vol_left) << 2);
|
||||
*((uint16_t*) &STATUS_BUFFER[19]) = (uint16_t) ((vol_right) << 2);
|
||||
|
||||
log_debug(
|
||||
"bi2a",
|
||||
"knobs = {} {}",
|
||||
*((uint16_t*) &STATUS_BUFFER[17]),
|
||||
*((uint16_t*) &STATUS_BUFFER[19]));
|
||||
}
|
||||
|
||||
// DanceDanceRevolution
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, std::size(STATUS_BUFFER));
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::ddr::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::COIN_MECH)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[2] |= 1 << 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::SERVICE)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[2] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::TEST)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[2] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_START)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 7;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_UP)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_DOWN)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_LEFT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_RIGHT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_START)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_UP)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_DOWN)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_LEFT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_RIGHT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
// DANCERUSH
|
||||
if (avs::game::is_model("REC")) {
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, std::size(STATUS_BUFFER));
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::drs::get_buttons();
|
||||
|
||||
// test
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::Test)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 19);
|
||||
}
|
||||
|
||||
// service
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::Service)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 18);
|
||||
}
|
||||
|
||||
// coin
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::CoinMech)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 17);
|
||||
}
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Start)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 87);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Up)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 86);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Down)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 85);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Left)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 84);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Right)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 83);
|
||||
}
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Start)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 93);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Up)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 92);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Down)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 91);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Left)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 90);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Right)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 89);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_current_coinstock(size_t index, DWORD *coins) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// get coins and return success
|
||||
*coins = (DWORD) eamuse_coin_get_stock();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_consume_coinstock(size_t index, int amount) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// calculate new stock
|
||||
auto stock = eamuse_coin_get_stock();
|
||||
auto stock_new = stock - amount;
|
||||
|
||||
// check new stock
|
||||
if (stock_new < 0)
|
||||
return false;
|
||||
|
||||
// apply new stock
|
||||
eamuse_coin_set_stock(stock_new);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_lock_coincounter(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// enable coin blocker
|
||||
eamuse_coin_set_block(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_unlock_coincounter(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// disable coin blocker
|
||||
eamuse_coin_set_block(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bi2a_control_coin_blocker_close(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return;
|
||||
|
||||
// enable coin blocker
|
||||
eamuse_coin_set_block(true);
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bi2a_control_coin_blocker_open(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return;
|
||||
|
||||
// disable coin blocker
|
||||
eamuse_coin_set_block(false);
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_bi2a_control_led_bright(size_t index, uint8_t brightness) {
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
/*
|
||||
* Control R G B
|
||||
* =======================
|
||||
* WING UP 28 29 30
|
||||
* WING LOW 31 32 33
|
||||
* WOOFER 0 1 3
|
||||
* CONTROLLER 4 5 6
|
||||
*
|
||||
* Values go up to 255.
|
||||
*
|
||||
*
|
||||
* Control Index
|
||||
* ==================
|
||||
* START BUTTON 8
|
||||
* A BUTTON 9
|
||||
* B BUTTON 10
|
||||
* C BUTTON 11
|
||||
* D BUTTON 12
|
||||
* FX L BUTTON 13
|
||||
* FX R BUTTON 14
|
||||
* POP 24
|
||||
* TITLE LEFT 25
|
||||
* TITLE RIGHT 26
|
||||
*
|
||||
* Values go up to 127.
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
int light1, light2;
|
||||
float max;
|
||||
} mapping[] = {
|
||||
{ games::sdvx::Lights::WOOFER_R, -1, 255 },
|
||||
{ games::sdvx::Lights::WOOFER_G, -1, 255 },
|
||||
{ -1, -1, 0 },
|
||||
{ games::sdvx::Lights::WOOFER_B, -1, 255 },
|
||||
{ games::sdvx::Lights::CONTROLLER_R, -1, 255 },
|
||||
{ games::sdvx::Lights::CONTROLLER_G, -1, 255 },
|
||||
{ games::sdvx::Lights::CONTROLLER_B, -1, 255 },
|
||||
{ -1, -1, 0 },
|
||||
{ games::sdvx::Lights::START, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_A, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_B, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_C, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_D, -1, 127 },
|
||||
{ games::sdvx::Lights::FX_L, -1, 127 },
|
||||
{ games::sdvx::Lights::FX_R, -1, 127 },
|
||||
{ -1, -1, 0 }, { -1, -1, 0 }, { -1, -1, 0 },
|
||||
{ games::sdvx::Lights::GENERATOR_R, -1, 255 },
|
||||
{ games::sdvx::Lights::GENERATOR_G, -1, 255 },
|
||||
{ games::sdvx::Lights::GENERATOR_B, -1, 255 },
|
||||
{ -1, -1, 0 }, { -1, -1, 0 }, { -1, -1, 0 },
|
||||
{ games::sdvx::Lights::POP, -1, 127 },
|
||||
{ games::sdvx::Lights::TITLE_LEFT, -1, 127 },
|
||||
{ games::sdvx::Lights::TITLE_RIGHT, -1, 127 },
|
||||
{ -1, -1, 0 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_UP_R, games::sdvx::Lights::WING_LEFT_UP_R, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_UP_G, games::sdvx::Lights::WING_LEFT_UP_G, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_UP_B, games::sdvx::Lights::WING_LEFT_UP_B, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_LOW_R, games::sdvx::Lights::WING_LEFT_LOW_R, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_LOW_G, games::sdvx::Lights::WING_LEFT_LOW_G, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_LOW_B, games::sdvx::Lights::WING_LEFT_LOW_B, 255 },
|
||||
};
|
||||
|
||||
// ignore index out of range
|
||||
if (index > std::size(mapping)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
|
||||
// get light from mapping
|
||||
auto light = mapping[index];
|
||||
|
||||
// write lights
|
||||
if (light.light1 >= 0) {
|
||||
Lights::writeLight(RI_MGR, lights[light.light1], brightness / light.max);
|
||||
} else {
|
||||
log_warning("sdvx", "light unset {} {}", index, (int) brightness);
|
||||
}
|
||||
if (light.light2 >= 0) {
|
||||
Lights::writeLight(RI_MGR, lights[light.light2], brightness / light.max);
|
||||
}
|
||||
|
||||
// DANCERUSH
|
||||
} else if (avs::game::is_model("REC")) {
|
||||
|
||||
/*
|
||||
* Control R G B
|
||||
* ==============================
|
||||
* CARD UNIT 13 14 15
|
||||
* TITLE PANEL 28 29 30
|
||||
* MONITOR SIDE LEFT (tape LED - see ac_io_bi2a_control_tapeled_bright)
|
||||
* MONITOR SIDE RIGHT (tape LED - see ac_io_bi2a_control_tapeled_bright)
|
||||
*
|
||||
* Values go up to 127.
|
||||
*
|
||||
* Control Index
|
||||
* ==================
|
||||
* 1P LEFT 11
|
||||
* 1P RIGHT 12
|
||||
* 1P UP 9
|
||||
* 1P DOWN 10
|
||||
* 1P START 8
|
||||
* 2P LEFT 19
|
||||
* 2P RIGHT 20
|
||||
* 2P UP 17
|
||||
* 2P DOWN 18
|
||||
* 2P START 16
|
||||
*
|
||||
* Values go up to 127.
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
int light;
|
||||
float max;
|
||||
} mapping[] = {
|
||||
{ -1, 0 }, // 0
|
||||
{ -1, 0 }, // 1
|
||||
{ -1, 0 }, // 2
|
||||
{ -1, 0 }, // 3
|
||||
{ -1, 0 }, // 4
|
||||
{ -1, 0 }, // 5
|
||||
{ -1, 0 }, // 6
|
||||
{ -1, 0 }, // 7
|
||||
{ games::drs::Lights::P1_START, 127 }, // 8
|
||||
{ games::drs::Lights::P1_MENU_UP, 127 }, // 9
|
||||
{ games::drs::Lights::P1_MENU_DOWN, 127 }, // 10
|
||||
{ games::drs::Lights::P1_MENU_LEFT, 127 }, // 11
|
||||
{ games::drs::Lights::P1_MENU_RIGHT, 127 }, // 12
|
||||
{ games::drs::Lights::CARD_READER_R, 127 }, // 13
|
||||
{ games::drs::Lights::CARD_READER_G, 127 }, // 14
|
||||
{ games::drs::Lights::CARD_READER_B, 127 }, // 15
|
||||
{ games::drs::Lights::P2_START, 127 }, // 16
|
||||
{ games::drs::Lights::P2_MENU_UP, 127 }, // 17
|
||||
{ games::drs::Lights::P2_MENU_DOWN, 127 }, // 18
|
||||
{ games::drs::Lights::P2_MENU_LEFT, 127 }, // 19
|
||||
{ games::drs::Lights::P2_MENU_RIGHT, 127 }, // 20
|
||||
{ -1, 0 }, // 21
|
||||
{ -1, 0 }, // 22
|
||||
{ -1, 0 }, // 23
|
||||
{ -1, 0 }, // 24
|
||||
{ -1, 0 }, // 25
|
||||
{ -1, 0 }, // 26
|
||||
{ -1, 0 }, // 27
|
||||
{ games::drs::Lights::TITLE_PANEL_R, 127 }, // 28
|
||||
{ games::drs::Lights::TITLE_PANEL_G, 127 }, // 29
|
||||
{ games::drs::Lights::TITLE_PANEL_B, 127 }, // 30
|
||||
};
|
||||
|
||||
// ignore index out of range
|
||||
if (index > std::size(mapping)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get lights
|
||||
auto &lights = games::drs::get_lights();
|
||||
|
||||
// get light from mapping
|
||||
auto light = mapping[index];
|
||||
|
||||
// write lights
|
||||
if (light.light >= 0) {
|
||||
Lights::writeLight(RI_MGR, lights[light.light], brightness / light.max);
|
||||
} else {
|
||||
log_warning("drs", "light unset {} {}", index, (int) brightness);
|
||||
}
|
||||
|
||||
// DanceDanceRevolution
|
||||
} else if (avs::game::is_model("MDX")) {
|
||||
|
||||
static const struct {
|
||||
int light;
|
||||
float max;
|
||||
} mapping[] = {
|
||||
{ -1, 0 }, // 0
|
||||
{ -1, 0 }, // 1
|
||||
{ -1, 0 }, // 2
|
||||
{ -1, 0 }, // 3
|
||||
{ -1, 0 }, // 4
|
||||
{ -1, 0 }, // 5
|
||||
{ -1, 0 }, // 6
|
||||
{ -1, 0 }, // 7
|
||||
{ games::ddr::Lights::GOLD_P1_MENU_START, 127 }, // 8
|
||||
{ games::ddr::Lights::GOLD_P1_MENU_UP, 127 }, // 9
|
||||
{ games::ddr::Lights::GOLD_P1_MENU_DOWN, 127 }, // 10
|
||||
{ games::ddr::Lights::GOLD_P1_MENU_LEFT, 127 }, // 11
|
||||
{ games::ddr::Lights::GOLD_P1_MENU_RIGHT, 127 }, // 12
|
||||
{ games::ddr::Lights::GOLD_P1_CARD_UNIT_R, 127 }, // 13
|
||||
{ games::ddr::Lights::GOLD_P1_CARD_UNIT_G, 127 }, // 14
|
||||
{ games::ddr::Lights::GOLD_P1_CARD_UNIT_B, 127 }, // 15
|
||||
{ games::ddr::Lights::GOLD_P2_MENU_START, 127 }, // 16
|
||||
{ games::ddr::Lights::GOLD_P2_MENU_UP, 127 }, // 17
|
||||
{ games::ddr::Lights::GOLD_P2_MENU_DOWN, 127 }, // 18
|
||||
{ games::ddr::Lights::GOLD_P2_MENU_LEFT, 127 }, // 19
|
||||
{ games::ddr::Lights::GOLD_P2_MENU_RIGHT, 127 }, // 20
|
||||
{ games::ddr::Lights::GOLD_P2_CARD_UNIT_R, 0 }, // 21
|
||||
{ games::ddr::Lights::GOLD_P2_CARD_UNIT_G, 0 }, // 22
|
||||
{ games::ddr::Lights::GOLD_P2_CARD_UNIT_B, 0 }, // 23
|
||||
{ -1, 0 }, // 24
|
||||
{ -1, 0 }, // 25
|
||||
{ -1, 0 }, // 26
|
||||
{ -1, 0 }, // 27
|
||||
{ games::ddr::Lights::GOLD_TITLE_PANEL_LEFT, 0 }, // 28
|
||||
{ games::ddr::Lights::GOLD_TITLE_PANEL_CENTER, 0 }, // 29
|
||||
{ games::ddr::Lights::GOLD_TITLE_PANEL_RIGHT, 0 }, // 30
|
||||
{ games::ddr::Lights::GOLD_P1_WOOFER_CORNER, 0 }, // 31
|
||||
{ games::ddr::Lights::GOLD_P2_WOOFER_CORNER, 0 } // 32
|
||||
};
|
||||
|
||||
// ignore index out of range
|
||||
if (index > std::size(mapping)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get lights
|
||||
auto &lights = games::ddr::get_lights();
|
||||
|
||||
// get light from mapping
|
||||
auto light = mapping[index];
|
||||
|
||||
// write lights
|
||||
if (light.light >= 0) {
|
||||
Lights::writeLight(RI_MGR, lights[light.light], brightness / light.max);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_bi2a_get_watchdog_time_min() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_bi2a_get_watchdog_time_now() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bi2a_watchdog_off() {
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_init(uint8_t param) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_set_watchdog_time(uint16_t time) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_get_watchdog_status() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_set_amp_volume(uint8_t a1, uint8_t a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_tapeled_init(uint8_t a1, uint8_t a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_tapeled_init_is_finished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_control_tapeled_rec_set(uint8_t* data, size_t x_sz, size_t y_sz) {
|
||||
|
||||
// check dimensions
|
||||
if (x_sz != DRS_TAPELED_COLS || y_sz != DRS_TAPELED_ROWS) {
|
||||
log_fatal("drs", "DRS tapeled wrong dimensions");
|
||||
}
|
||||
|
||||
// copy data into our buffer - 4 bytes per pixel BGR
|
||||
for (size_t i = 0; i < x_sz * y_sz; i++) {
|
||||
games::drs::DRS_TAPELED[i][0] = data[i*4+2];
|
||||
games::drs::DRS_TAPELED[i][1] = data[i*4+1];
|
||||
games::drs::DRS_TAPELED[i][2] = data[i*4];
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: DRS tape lights
|
||||
static bool __cdecl ac_io_bi2a_control_tapeled_bright(size_t off1, size_t off2,
|
||||
uint8_t r, uint8_t g, uint8_t b, uint8_t bank) {
|
||||
|
||||
if (!tapeledutils::is_enabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
/*
|
||||
* r, g, b values range from [0-255]
|
||||
* bank always seems to be [0]
|
||||
*
|
||||
* [off1.off2] [LEDs] [tape name]
|
||||
* 0.0 25 P1 Foot Up (0.0 to 0.24, inclusive)
|
||||
* 0.25 25 P1 Foot Right
|
||||
* 1.0 25 P1 Foot Left
|
||||
* 1.25 25 P1 Foot Down
|
||||
*
|
||||
* 2.0 25 P2 Foot Up
|
||||
* 2.25 25 P2 Foot Right
|
||||
* 3.0 25 P2 Foot Left
|
||||
* 3.25 25 P2 Foot Down
|
||||
*
|
||||
* 5.0 50 Top Panel
|
||||
* 6.0 50 Monitor side left
|
||||
* 7.0 50 Monitor side right
|
||||
*/
|
||||
|
||||
// In order to set the data that can be output via Spice API, we first
|
||||
// need to figure out which device in our buffers this data belongs to
|
||||
int device = -1;
|
||||
if (off1 == 0) {
|
||||
if (off2 < 25) device = 0; // P1 Foot Up
|
||||
else device = 1; // P1 Foot Right
|
||||
} else if (off1 == 1) {
|
||||
if (off2 < 25) device = 2; // P1 Foot Left
|
||||
else device = 3; // P1 Foot Down
|
||||
} else if (off1 == 2) {
|
||||
if (off2 < 25) device = 4; // P2 Foot Up
|
||||
else device = 5; // P2 Foot Right
|
||||
} else if (off1 == 3) {
|
||||
if (off2 < 25) device = 6; // P2 Foot Left
|
||||
else device = 7; // P2 Foot Down
|
||||
} else if (off1 >= 5 && off1 <= 7) {
|
||||
device = off1 + 3; // Top Panel / Monitor Side Left / Monitor Side Right
|
||||
}
|
||||
|
||||
if (device != -1) {
|
||||
// We subtract 25 from off2 to get the device's LED index, if it's for one of the
|
||||
// arrow panels that is on the latter half of the logical strip
|
||||
size_t subtractor = 0;
|
||||
if (off1 <= 3 && off2 >= 25) {
|
||||
subtractor = 25;
|
||||
}
|
||||
size_t led_index = off2 - subtractor;
|
||||
|
||||
games::ddr::DDR_TAPELEDS[device][led_index][0] = r;
|
||||
games::ddr::DDR_TAPELEDS[device][led_index][1] = g;
|
||||
games::ddr::DDR_TAPELEDS[device][led_index][2] = b;
|
||||
}
|
||||
|
||||
static struct TapeLedMapping {
|
||||
bool split; // true == 50 LEDs for one light, false == 25 for two lights
|
||||
uint8_t index_r0, index_g0, index_b0;
|
||||
uint8_t index_r1, index_g1, index_b1;
|
||||
size_t index_for_avg0 = UINT8_MAX;
|
||||
size_t index_for_avg1 = UINT8_MAX;
|
||||
|
||||
TapeLedMapping(
|
||||
uint8_t index_r0, uint8_t index_g0, uint8_t index_b0,
|
||||
uint8_t index_r1, uint8_t index_g1, uint8_t index_b1)
|
||||
: index_r0(index_r0), index_g0(index_g0), index_b0(index_b0),
|
||||
index_r1(index_r1), index_g1(index_g1), index_b1(index_b1) {
|
||||
|
||||
split = (index_r1 != UINT8_MAX);
|
||||
if (split) {
|
||||
index_for_avg0 = tapeledutils::get_led_index_using_avg_algo(25);
|
||||
index_for_avg1 = index_for_avg0 + 25;
|
||||
} else {
|
||||
index_for_avg0 = tapeledutils::get_led_index_using_avg_algo(50);
|
||||
index_for_avg1 = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} mapping[] = {
|
||||
{
|
||||
games::ddr::Lights::GOLD_P1_FOOT_UP_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_UP_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_UP_AVG_B,
|
||||
games::ddr::Lights::GOLD_P1_FOOT_RIGHT_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_RIGHT_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_RIGHT_AVG_B
|
||||
},
|
||||
{
|
||||
games::ddr::Lights::GOLD_P1_FOOT_LEFT_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_LEFT_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_LEFT_AVG_B,
|
||||
games::ddr::Lights::GOLD_P1_FOOT_DOWN_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_DOWN_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_DOWN_AVG_B
|
||||
},
|
||||
{
|
||||
games::ddr::Lights::GOLD_P2_FOOT_UP_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_UP_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_UP_AVG_B,
|
||||
games::ddr::Lights::GOLD_P2_FOOT_RIGHT_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_RIGHT_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_RIGHT_AVG_B
|
||||
},
|
||||
{
|
||||
games::ddr::Lights::GOLD_P2_FOOT_LEFT_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_LEFT_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_LEFT_AVG_B,
|
||||
games::ddr::Lights::GOLD_P2_FOOT_DOWN_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_DOWN_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_DOWN_AVG_B
|
||||
},
|
||||
{
|
||||
games::ddr::Lights::GOLD_TOP_PANEL_AVG_R, games::ddr::Lights::GOLD_TOP_PANEL_AVG_G, games::ddr::Lights::GOLD_TOP_PANEL_AVG_B,
|
||||
UINT8_MAX, UINT8_MAX, UINT8_MAX
|
||||
},
|
||||
{
|
||||
UINT8_MAX, UINT8_MAX, UINT8_MAX,
|
||||
UINT8_MAX, UINT8_MAX, UINT8_MAX
|
||||
},
|
||||
{
|
||||
games::ddr::Lights::GOLD_MONITOR_SIDE_LEFT_AVG_R, games::ddr::Lights::GOLD_MONITOR_SIDE_LEFT_AVG_G, games::ddr::Lights::GOLD_MONITOR_SIDE_LEFT_AVG_B,
|
||||
UINT8_MAX, UINT8_MAX, UINT8_MAX
|
||||
},
|
||||
{
|
||||
games::ddr::Lights::GOLD_MONITOR_SIDE_RIGHT_AVG_R, games::ddr::Lights::GOLD_MONITOR_SIDE_RIGHT_AVG_G, games::ddr::Lights::GOLD_MONITOR_SIDE_RIGHT_AVG_B,
|
||||
UINT8_MAX, UINT8_MAX, UINT8_MAX
|
||||
},
|
||||
};
|
||||
|
||||
if (off1 < std::size(mapping)) {
|
||||
auto &map = mapping[off1];
|
||||
|
||||
size_t off2_match = -1;
|
||||
if (!map.split || off2 < 25) {
|
||||
off2_match = map.index_for_avg0;
|
||||
} else {
|
||||
off2_match = map.index_for_avg1;
|
||||
}
|
||||
if (off2_match == off2 && map.index_r0 != UINT8_MAX) {
|
||||
auto &lights = games::ddr::get_lights();
|
||||
if (!map.split || off2 < 25) {
|
||||
Lights::writeLight(RI_MGR, lights[map.index_r0], r / 255.f);
|
||||
Lights::writeLight(RI_MGR, lights[map.index_g0], g / 255.f);
|
||||
Lights::writeLight(RI_MGR, lights[map.index_b0], b / 255.f);
|
||||
} else {
|
||||
Lights::writeLight(RI_MGR, lights[map.index_r1], r / 255.f);
|
||||
Lights::writeLight(RI_MGR, lights[map.index_g1], g / 255.f);
|
||||
Lights::writeLight(RI_MGR, lights[map.index_b1], b / 255.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_tapeled_send() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bi2a_get_exbio2_status(uint8_t *info) {
|
||||
// surely this meme never gets old
|
||||
info[5] = 5;
|
||||
info[6] = 7;
|
||||
info[7] = 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
acio::BI2AModule::BI2AModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("BI2A", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::BI2AModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_init_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_watchdog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_init);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_set_amp_volume);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_tapeled_init);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_tapeled_init_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_exbio2_status);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_tapeled_rec_set);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_tapeled_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_tapeled_send);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BI2AModule : public ACIOModule {
|
||||
public:
|
||||
BI2AModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
#include "bmpu.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "cfg/api.h"
|
||||
#include "cfg/light.h"
|
||||
#include "games/bbc/io.h"
|
||||
#include "games/dea/io.h"
|
||||
#include "games/ftt/io.h"
|
||||
#include "games/museca/io.h"
|
||||
#include "games/silentscope/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[64] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static char __cdecl ac_io_bmpu_consume_coinstock(int a1, int a2) {
|
||||
eamuse_coin_consume_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_1p_start_led_off() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 0.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 0.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_1p_start_led_on() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 1.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 1.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_2p_start_led_off() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 0.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 0.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_2p_start_led_on() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 1.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 1.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_coin_blocker_close() {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_coin_blocker_open() {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_control_led_bright(uint32_t led_field, uint8_t brightness) {
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::museca::get_lights();
|
||||
|
||||
// control mapping
|
||||
static const int mapping[] = {
|
||||
games::museca::Lights::UnderLED3G,
|
||||
games::museca::Lights::UnderLED3R,
|
||||
games::museca::Lights::UnderLED2B,
|
||||
games::museca::Lights::UnderLED2G,
|
||||
games::museca::Lights::UnderLED2R,
|
||||
games::museca::Lights::UnderLED1B,
|
||||
games::museca::Lights::UnderLED1G,
|
||||
games::museca::Lights::UnderLED1R,
|
||||
-1, -1, -1, -1,
|
||||
games::museca::Lights::SideB,
|
||||
games::museca::Lights::SideG,
|
||||
games::museca::Lights::SideR,
|
||||
games::museca::Lights::UnderLED3B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::bbc::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::bbc::Lights::UNDER_LED3_G,
|
||||
games::bbc::Lights::UNDER_LED3_R,
|
||||
games::bbc::Lights::UNDER_LED2_B,
|
||||
games::bbc::Lights::UNDER_LED2_G,
|
||||
games::bbc::Lights::UNDER_LED2_R,
|
||||
games::bbc::Lights::UNDER_LED1_B,
|
||||
games::bbc::Lights::UNDER_LED1_G,
|
||||
games::bbc::Lights::UNDER_LED1_R,
|
||||
-1, -1, -1, -1,
|
||||
games::bbc::Lights::IC_CARD_B,
|
||||
games::bbc::Lights::IC_CARD_G,
|
||||
games::bbc::Lights::IC_CARD_R,
|
||||
games::bbc::Lights::UNDER_LED3_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FutureTomTom
|
||||
if (avs::game::is_model("MMD")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::ftt::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::ftt::Lights::Pad3_G,
|
||||
games::ftt::Lights::Pad3_R,
|
||||
games::ftt::Lights::Pad2_B,
|
||||
games::ftt::Lights::Pad2_G,
|
||||
games::ftt::Lights::Pad2_R,
|
||||
games::ftt::Lights::Pad1_B,
|
||||
games::ftt::Lights::Pad1_G,
|
||||
games::ftt::Lights::Pad1_R,
|
||||
-1, -1, -1, -1,
|
||||
games::ftt::Lights::Pad4_B,
|
||||
games::ftt::Lights::Pad4_G,
|
||||
games::ftt::Lights::Pad4_R,
|
||||
games::ftt::Lights::Pad3_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dance Evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::dea::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
games::dea::Lights::P2LRButton,
|
||||
games::dea::Lights::P1LRButton,
|
||||
-1,
|
||||
games::dea::Lights::TitleB,
|
||||
games::dea::Lights::TitleR,
|
||||
games::dea::Lights::TitleG,
|
||||
-1,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 128.f ? 1.f : brightness / 128.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++)
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_control_led_bright_pack(int a1, int a2, int a3) {
|
||||
// TODO(felix): NDD lights
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_current_coinstock(int a1, int *a2) {
|
||||
*a2 = eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_destroy_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_get_control_status_buffer(void *buffer) {
|
||||
size_t buffer_len = 0;
|
||||
|
||||
if (avs::game::is_model({ "KDM", "MMD" })) {
|
||||
buffer_len = sizeof(STATUS_BUFFER);
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
buffer_len = 16;
|
||||
} else if (avs::game::is_model("R66")) {
|
||||
buffer_len = 56;
|
||||
} else if (avs::game::is_model("NDD")) {
|
||||
buffer_len = 56;
|
||||
}
|
||||
|
||||
if (buffer_len > 0) {
|
||||
memcpy(buffer, &STATUS_BUFFER, buffer_len);
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *__cdecl ac_io_bmpu_get_softwareid(char *a1) {
|
||||
*a1 = 0;
|
||||
return a1;
|
||||
}
|
||||
|
||||
static char *__cdecl ac_io_bmpu_get_systemid(char *a1) {
|
||||
*a1 = 0;
|
||||
return a1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_init_outport() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_lock_coincounter(signed int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_check_isfinished(DWORD *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_check_softwareplug(char *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_check_systemplug() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_missing_check() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_req_secplug_missing_check_isfinished(DWORD *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_set_outport_led(uint8_t *data1, uint8_t *data2) {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::dea::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
games::dea::Lights::SideUpperLeftR,
|
||||
games::dea::Lights::SideUpperLeftG,
|
||||
games::dea::Lights::SideUpperLeftB,
|
||||
games::dea::Lights::SideLowerLeft1R,
|
||||
games::dea::Lights::SideLowerLeft1G,
|
||||
games::dea::Lights::SideLowerLeft1B,
|
||||
games::dea::Lights::SideLowerLeft2R,
|
||||
games::dea::Lights::SideLowerLeft2G,
|
||||
games::dea::Lights::SideLowerLeft2B,
|
||||
games::dea::Lights::SideLowerLeft3R,
|
||||
games::dea::Lights::SideLowerLeft3G,
|
||||
games::dea::Lights::SideLowerLeft3B,
|
||||
games::dea::Lights::SideUpperRightR,
|
||||
games::dea::Lights::SideUpperRightG,
|
||||
games::dea::Lights::SideUpperRightB,
|
||||
games::dea::Lights::SideLowerRight1R,
|
||||
games::dea::Lights::SideLowerRight1G,
|
||||
games::dea::Lights::SideLowerRight1B,
|
||||
games::dea::Lights::SideLowerRight2R,
|
||||
games::dea::Lights::SideLowerRight2G,
|
||||
games::dea::Lights::SideLowerRight2B,
|
||||
games::dea::Lights::SideLowerRight3R,
|
||||
games::dea::Lights::SideLowerRight3G,
|
||||
games::dea::Lights::SideLowerRight3B,
|
||||
};
|
||||
|
||||
// write lights
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
float brightness = data1[i * 2] / 255.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), brightness);
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_set_output_mode(__int16 a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_unlock_coincounter(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_update_control_status_buffer() {
|
||||
unsigned int control_data = 0;
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DEA
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// keypad mirror fix
|
||||
acio::ICCA_FLIP_ROWS = true;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::dea::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P1Start))) {
|
||||
control_data |= 0x00000001;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P1Left))) {
|
||||
control_data |= 0x00000008;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P1Right))) {
|
||||
control_data |= 0x00000010;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P2Start))) {
|
||||
control_data |= 0x00000100;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P2Left))) {
|
||||
control_data |= 0x00000800;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P2Right))) {
|
||||
control_data |= 0x00001000;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// FutureTomTom
|
||||
if (avs::game::is_model("MMD")) {
|
||||
|
||||
// keypad mirror fix
|
||||
acio::ICCA_FLIP_ROWS = true;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::ftt::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::museca::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Start))) {
|
||||
control_data |= 0x00000001;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::bbc::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Silent Scope Bone Eater
|
||||
if (avs::game::is_model("NDD")) {
|
||||
|
||||
// clear state
|
||||
memset(STATUS_BUFFER, 0, 56);
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::silentscope::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::TEST))) {
|
||||
STATUS_BUFFER[7] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::SERVICE))) {
|
||||
STATUS_BUFFER[7] |= 0x2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::COIN_MECH))) {
|
||||
STATUS_BUFFER[7] |= 0x1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::START))) {
|
||||
STATUS_BUFFER[5] |= 0x1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::UP))) {
|
||||
STATUS_BUFFER[5] |= 0x2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::DOWN))) {
|
||||
STATUS_BUFFER[5] |= 0x4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::LEFT))) {
|
||||
STATUS_BUFFER[5] |= 0x8;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::RIGHT))) {
|
||||
STATUS_BUFFER[5] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::SCOPE_RIGHT))) {
|
||||
STATUS_BUFFER[4] |= 0x80;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::SCOPE_LEFT))) {
|
||||
STATUS_BUFFER[4] |= 0x40;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::GUN_PRESSED))) {
|
||||
STATUS_BUFFER[4] |= 0x20;
|
||||
}
|
||||
|
||||
// joy stick raw input
|
||||
auto &analogs = games::silentscope::get_analogs();
|
||||
unsigned short joy_x = 0x7FFF;
|
||||
unsigned short joy_y = 0x7FFF;
|
||||
if (analogs.at(games::silentscope::Analogs::GUN_X).isSet()) {
|
||||
joy_x = (unsigned short) (Analogs::getState(RI_MGR, analogs.at(games::silentscope::Analogs::GUN_X)) * USHRT_MAX);
|
||||
}
|
||||
if (analogs.at(games::silentscope::Analogs::GUN_Y).isSet()) {
|
||||
joy_y = (unsigned short) (Analogs::getState(RI_MGR, analogs.at(games::silentscope::Analogs::GUN_Y)) * USHRT_MAX);
|
||||
}
|
||||
|
||||
// invert X axis
|
||||
joy_x = USHRT_MAX - joy_x;
|
||||
|
||||
STATUS_BUFFER[8] = HIBYTE(joy_x);
|
||||
STATUS_BUFFER[9] = LOBYTE(joy_x);
|
||||
STATUS_BUFFER[10] = HIBYTE(joy_y);
|
||||
STATUS_BUFFER[11] = LOBYTE(joy_y);
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_set_watchdog_time(char a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_get_watchdog_time_min() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_get_watchdog_time_now() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bmpu_watchdog_off() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::BMPUModule::BMPUModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("BMPU", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::BMPUModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_1p_start_led_off);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_1p_start_led_on);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_2p_start_led_off);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_2p_start_led_on);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_led_bright_pack);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_init_outport);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_check_softwareplug);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_check_systemplug);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_set_outport_led);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_set_output_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_watchdog_off);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BMPUModule : public ACIOModule {
|
||||
public:
|
||||
BMPUModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#include "core.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
|
||||
// static stuff
|
||||
static int ACIO_WARMUP = 0;
|
||||
static HHOOK ACIO_KB_HOOK = nullptr;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
// needed for some games to make GetAsyncKeyState() working
|
||||
static LRESULT CALLBACK ac_io_kb_hook_callback(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
CallNextHookEx(ACIO_KB_HOOK, nCode, wParam, lParam);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_begin(
|
||||
size_t dev,
|
||||
const char *ver,
|
||||
unsigned int *val,
|
||||
size_t flags,
|
||||
void *ptr,
|
||||
size_t baud)
|
||||
{
|
||||
if (ACIO_KB_HOOK == nullptr) {
|
||||
ACIO_KB_HOOK = SetWindowsHookEx(WH_KEYBOARD_LL, ac_io_kb_hook_callback, GetModuleHandle(nullptr), 0);
|
||||
}
|
||||
|
||||
// always return success
|
||||
if (val && avs::game::is_model("KFC")) {
|
||||
*val = 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_begin_get_status() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_end(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_end_get_status(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_get_rs232c_status(char *a1, int a2) {
|
||||
return memset(a1, 0, 88);
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_get_version(uint8_t *a1, int a2) {
|
||||
|
||||
// some games have version checks
|
||||
// pop'n music only accepts versions bigger than 1.X.X (check yourself), anything starting with 2 works though
|
||||
memset(a1 + 5, 2, 1);
|
||||
memset(a1 + 6, 0, 1);
|
||||
memset(a1 + 7, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *__cdecl ac_io_get_version_string() {
|
||||
static const char *version = "1.25.0";
|
||||
return version;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_is_active(int a1, int a2) {
|
||||
if (a1 == 1 && avs::game::is_model("JMA")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (char) (++ACIO_WARMUP > 601 ? 1 : 0);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_is_active2(int a1, int *a2, int a3) {
|
||||
ACIO_WARMUP = 601;
|
||||
*a2 = 6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_is_active_device(int index, int a2) {
|
||||
|
||||
// for scotto
|
||||
static bool CHECKED_24 = false;
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// disable mysterious LED devices
|
||||
if (index >= 12 && index <= 15)
|
||||
return false;
|
||||
}
|
||||
|
||||
// scotto
|
||||
if (avs::game::is_model("NSC") && index == 24) {
|
||||
|
||||
// scotto expects device index 24 to come online after
|
||||
// it initializes device index 22
|
||||
if (!CHECKED_24) {
|
||||
CHECKED_24 = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// dunno for what game we did this again
|
||||
return (char) (index != 5);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_reset(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_secplug_set_encodedpasswd(void *a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_set_soft_watch_dog(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_soft_watch_dog_on(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_soft_watch_dog_off() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_update(int a1) {
|
||||
|
||||
// flush device output
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
// update wintouchemu
|
||||
wintouchemu::update();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_get_firmware_update_device_index() {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_go_firmware_update() {
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_set_get_status_device(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::CoreModule::CoreModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("Core", module, hookMode) {
|
||||
}
|
||||
|
||||
void acio::CoreModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_begin);
|
||||
ACIO_MODULE_HOOK(ac_io_begin_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_end);
|
||||
ACIO_MODULE_HOOK(ac_io_end_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_get_rs232c_status);
|
||||
ACIO_MODULE_HOOK(ac_io_get_version);
|
||||
ACIO_MODULE_HOOK(ac_io_get_version_string);
|
||||
ACIO_MODULE_HOOK(ac_io_is_active);
|
||||
ACIO_MODULE_HOOK(ac_io_is_active2);
|
||||
ACIO_MODULE_HOOK(ac_io_is_active_device);
|
||||
ACIO_MODULE_HOOK(ac_io_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_secplug_set_encodedpasswd);
|
||||
ACIO_MODULE_HOOK(ac_io_set_soft_watch_dog);
|
||||
ACIO_MODULE_HOOK(ac_io_soft_watch_dog_on);
|
||||
ACIO_MODULE_HOOK(ac_io_soft_watch_dog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_update);
|
||||
ACIO_MODULE_HOOK(ac_io_get_firmware_update_device_index);
|
||||
ACIO_MODULE_HOOK(ac_io_go_firmware_update);
|
||||
ACIO_MODULE_HOOK(ac_io_set_get_status_device);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class CoreModule : public ACIOModule {
|
||||
public:
|
||||
CoreModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
#include "hbhi.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/rf3d/io.h"
|
||||
#include "games/sc/io.h"
|
||||
#include "games/hpm/io.h"
|
||||
#include "avs/game.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[64] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_hbhi_add_coin(int a1, int a2) {
|
||||
eamuse_coin_add();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_consume_coinstock(int a1, int a2) {
|
||||
eamuse_coin_consume_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_coin_blocker_close(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_coin_blocker_open(int a1) {
|
||||
eamuse_coin_set_block(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method, not a real ACIO one
|
||||
*/
|
||||
static inline int __cdecl ac_io_hbhi_control_lamp_set(uint32_t lamp_bits, float value) {
|
||||
|
||||
// steel chronicle
|
||||
if (avs::game::is_model("KGG")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sc::get_lights();
|
||||
|
||||
// write lights
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::SideRed), value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::SideGreen), value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::SideBlue), value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::CenterRed), value);
|
||||
}
|
||||
if (lamp_bits & 0x10) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::CenterGreen), value);
|
||||
}
|
||||
if (lamp_bits & 0x20) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::CenterBlue), value);
|
||||
}
|
||||
if (lamp_bits & 0x40) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::ControllerRed), value);
|
||||
}
|
||||
if (lamp_bits & 0x80) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::ControllerBlue), value);
|
||||
}
|
||||
}
|
||||
|
||||
// hello popn music
|
||||
if (avs::game::is_model("JMP")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::hpm::get_lights();
|
||||
|
||||
// write lights
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_RED_P2_GREEN), value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_BLUE), value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_YELLOW), value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_GREEN), value);
|
||||
}
|
||||
if (lamp_bits & 0x10) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_RED), value);
|
||||
}
|
||||
if (lamp_bits & 0x20) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_BLUE), value);
|
||||
}
|
||||
if (lamp_bits & 0x40) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_YELLOW), value);
|
||||
}
|
||||
if (lamp_bits & 0x80) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_START), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_control_lamp_bright(uint32_t lamp_bits, uint8_t value) {
|
||||
ac_io_hbhi_control_lamp_set(lamp_bits, value / 31.f);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_lamp_mode(uint32_t mode) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_lamp_off(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_lamp_set(lamp_bits, 0.f);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_lamp_on(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_lamp_set(lamp_bits, 1.f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method, not a real ACIO one
|
||||
*/
|
||||
static inline int __cdecl ac_io_hbhi_control_parallel_set(uint8_t lamp_bits, float value) {
|
||||
|
||||
// hello popn music
|
||||
if (avs::game::is_model("JMP")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::hpm::get_lights();
|
||||
|
||||
// write lights
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_START), value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::SPEAKER_BLUE), value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::SPEAKER_ORANGE), value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::SPEAKER_RED), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_parallel_off(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_parallel_set(lamp_bits, 0.f);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_parallel_on(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_parallel_set(lamp_bits, 1.f);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_reset() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_create_get_status_thread(void *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_current_coinstock(int a1, int *coinstock) {
|
||||
*coinstock = eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_destroy_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_get_coin_input_wave_buffer(int *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_hbhi_get_control_status_buffer(uint8_t *buffer) {
|
||||
|
||||
// return buffer
|
||||
memcpy(buffer, STATUS_BUFFER, std::size(STATUS_BUFFER));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_get_softwareid(char *a1) {
|
||||
memset(a1, 'F', 16);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_get_systemid(char *a1) {
|
||||
memset(a1, 'F', 16);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_get_watchdog_status() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_hbhi_get_watchdog_time_min() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_hbhi_get_watchdog_time_now() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_lock_coincounter(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_carddispenser_disburse() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_carddispenser_disburse_isfinished(int *a1) {
|
||||
*a1 += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_carddispenser_get_status() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_req_carddispenser_get_status_isfinished(int *a1) {
|
||||
*a1 += 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_carddispenser_init() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_carddispenser_init_isfinished(int *a1) {
|
||||
*a1 += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_coin_input_wave() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_get_control_status(int *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_check(char *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_secplug_check_isfinished(int *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_check_softwareplug(char *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_check_systemplug() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_missing_check() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_secplug_missing_check_isfinished(int *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_volume_control(char a1, char a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_volume_control_isfinished(int *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_reset_coin_slot_noise_flag(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_set_framing_err_packet_send_interval(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_unlock_coincounter(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// steel chronicle
|
||||
if (avs::game::is_model("KGG")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::sc::get_buttons();
|
||||
|
||||
// reset
|
||||
memset(STATUS_BUFFER, 0, 64);
|
||||
|
||||
// check buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::Service))) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::Test))) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::LButton))) {
|
||||
STATUS_BUFFER[12] |= 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::L1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::L2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::JogLeft))) {
|
||||
STATUS_BUFFER[12] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::JogRight))) {
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::RButton))) {
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::R1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::R2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
}
|
||||
|
||||
// get analogs
|
||||
auto &analogs = games::sc::get_analogs();
|
||||
|
||||
auto joy_left_x = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::LEFT_X)) * USHRT_MAX;
|
||||
auto joy_left_y = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::LEFT_Y)) * USHRT_MAX;
|
||||
auto joy_right_x = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::RIGHT_X)) * USHRT_MAX;
|
||||
auto joy_right_y = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::RIGHT_Y)) * USHRT_MAX;
|
||||
|
||||
// because these are flight sticks, the X axis is inverted
|
||||
*((uint16_t *) &STATUS_BUFFER[20]) = USHRT_MAX - (uint16_t) joy_left_x;
|
||||
*((uint16_t *) &STATUS_BUFFER[22]) = (uint16_t) joy_left_y;
|
||||
*((uint16_t *) &STATUS_BUFFER[24]) = USHRT_MAX - (uint16_t) joy_right_x;
|
||||
*((uint16_t *) &STATUS_BUFFER[26]) = (uint16_t) joy_right_y;
|
||||
}
|
||||
|
||||
// hello popn music
|
||||
if (avs::game::is_model("JMP")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::hpm::get_buttons();
|
||||
|
||||
// reset
|
||||
memset(STATUS_BUFFER, 0x00, 64);
|
||||
|
||||
// check buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::Service))) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::Test))) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[5] |= 1 << 2;
|
||||
}
|
||||
if (!Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_Start))) {
|
||||
STATUS_BUFFER[4] |= 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_3))) {
|
||||
STATUS_BUFFER[12] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_4))) {
|
||||
STATUS_BUFFER[12] |= 1 << 3;
|
||||
}
|
||||
if (!Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_Start))) {
|
||||
STATUS_BUFFER[6] |= 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_3))) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_4))) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
// road fighters 3D
|
||||
if (avs::game::is_model("JGT")) {
|
||||
static int lever_state = 0;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::rf3d::get_buttons();
|
||||
|
||||
// reset
|
||||
memset(STATUS_BUFFER, 0x00, 64);
|
||||
|
||||
// check buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Service))) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Test))) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[5] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::View))) {
|
||||
STATUS_BUFFER[12] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Toggle2D3D))) {
|
||||
STATUS_BUFFER[12] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverUp))) {
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
lever_state = 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverDown))) {
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
lever_state = 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverLeft))) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
lever_state = 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverRight))) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
lever_state = 0;
|
||||
}
|
||||
|
||||
// auto lever buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::AutoLeverUp)) && lever_state < 6) {
|
||||
lever_state++;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::AutoLeverDown)) && lever_state > 0) {
|
||||
lever_state--;
|
||||
}
|
||||
|
||||
// auto lever logic
|
||||
switch (lever_state) {
|
||||
case 1:
|
||||
STATUS_BUFFER[12] |= 1 << 4 | 1 << 6;
|
||||
break;
|
||||
case 2:
|
||||
STATUS_BUFFER[12] |= 1 << 4 | 1 << 6;
|
||||
break;
|
||||
case 3:
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
break;
|
||||
case 4:
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
break;
|
||||
case 5:
|
||||
STATUS_BUFFER[12] |= 1 << 4 | 1 << 7;
|
||||
break;
|
||||
case 6:
|
||||
STATUS_BUFFER[12] |= 1 << 5 | 1 << 7;
|
||||
break;
|
||||
default:
|
||||
lever_state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_hbhi_watchdog_off() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::HBHIModule::HBHIModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("HBHI", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::HBHIModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_add_coin);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_off);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_on);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_parallel_off);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_parallel_on);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_coin_input_wave_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_disburse);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_disburse_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_get_status_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_init);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_init_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_coin_input_wave);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_get_control_status);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check_softwareplug);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check_systemplug);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_volume_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_reset_coin_slot_noise_flag);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_set_framing_err_packet_send_interval);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_watchdog_off);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HBHIModule : public ACIOModule {
|
||||
public:
|
||||
HBHIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
#include "hdxs.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/popn/io.h"
|
||||
#include "games/rb/io.h"
|
||||
#include "games/dea/io.h"
|
||||
#include "avs/game.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[32] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_hdxs_get_control_status_buffer(int a1, void *a2) {
|
||||
|
||||
// copy buffer
|
||||
memcpy(a2, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_led_scroll(int a1, char a2, char a3, char a4, char a5, char a6, char a7, char a8, char a9,
|
||||
char a10, char a11, char a12, char a13) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_led_set_pattern(int index, char r, char g, char b, uint64_t led_bits) {
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model({"KBR", "LBR", "MBR"})) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::rb::get_lights();
|
||||
|
||||
// set values
|
||||
Lights::writeLight(RI_MGR, lights.at(games::rb::Lights::PoleR), r / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::rb::Lights::PoleG), g / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::rb::Lights::PoleB), b / 127.f);
|
||||
}
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::dea::get_lights();
|
||||
|
||||
// decide on index
|
||||
switch (index) {
|
||||
case 12:
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperLeftR), r / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperLeftG), g / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperLeftB), b / 127.f);
|
||||
break;
|
||||
case 14:
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperRightR), r / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperRightG), g / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperRightB), b / 127.f);
|
||||
}
|
||||
}
|
||||
|
||||
// popn
|
||||
if (avs::game::is_model("M39")) {
|
||||
|
||||
// mappings
|
||||
static const uint64_t top_led_bits[] = {
|
||||
0x80000000000000,
|
||||
0x40000000000000,
|
||||
0x20000000000000,
|
||||
0x10000000000000,
|
||||
0x8000000000000,
|
||||
0x4000000000000,
|
||||
0x2000000000000,
|
||||
0x1000000000000,
|
||||
0x800000000000,
|
||||
0x400000000000,
|
||||
0x200000000000,
|
||||
0x100000000000,
|
||||
0x80000000000,
|
||||
0x40000000000,
|
||||
0x20000000000,
|
||||
0x10000000000,
|
||||
0x8000000000,
|
||||
0x4000000000,
|
||||
0x2000000000,
|
||||
0x1000000000,
|
||||
0x800000000,
|
||||
0x400000000,
|
||||
0x200000000,
|
||||
0x100000000,
|
||||
0x80000000,
|
||||
0x40000000,
|
||||
0x20000000,
|
||||
0x10000000,
|
||||
0x8000000,
|
||||
0x4000000,
|
||||
0x2000000,
|
||||
0x1000000,
|
||||
};
|
||||
static const size_t light_mapping[] {
|
||||
games::popn::Lights::TopLED1,
|
||||
games::popn::Lights::TopLED2,
|
||||
games::popn::Lights::TopLED3,
|
||||
games::popn::Lights::TopLED4,
|
||||
games::popn::Lights::TopLED5,
|
||||
games::popn::Lights::TopLED6,
|
||||
games::popn::Lights::TopLED7,
|
||||
games::popn::Lights::TopLED8,
|
||||
games::popn::Lights::TopLED9,
|
||||
games::popn::Lights::TopLED10,
|
||||
games::popn::Lights::TopLED11,
|
||||
games::popn::Lights::TopLED12,
|
||||
games::popn::Lights::TopLED13,
|
||||
games::popn::Lights::TopLED14,
|
||||
games::popn::Lights::TopLED15,
|
||||
games::popn::Lights::TopLED16,
|
||||
games::popn::Lights::TopLED17,
|
||||
games::popn::Lights::TopLED18,
|
||||
games::popn::Lights::TopLED19,
|
||||
games::popn::Lights::TopLED20,
|
||||
games::popn::Lights::TopLED21,
|
||||
games::popn::Lights::TopLED22,
|
||||
games::popn::Lights::TopLED23,
|
||||
games::popn::Lights::TopLED24,
|
||||
games::popn::Lights::TopLED25,
|
||||
games::popn::Lights::TopLED26,
|
||||
games::popn::Lights::TopLED27,
|
||||
games::popn::Lights::TopLED28,
|
||||
games::popn::Lights::TopLED29,
|
||||
games::popn::Lights::TopLED30,
|
||||
games::popn::Lights::TopLED31,
|
||||
games::popn::Lights::TopLED32,
|
||||
};
|
||||
|
||||
// get lights
|
||||
auto &lights = games::popn::get_lights();
|
||||
|
||||
// bit scan
|
||||
for (int i = 0; i < 32; i++) {
|
||||
bool value = (led_bits & top_led_bits[i]) > 0;
|
||||
Lights::writeLight(RI_MGR, lights.at(light_mapping[i]), value ? 1.f : 0.f);
|
||||
}
|
||||
|
||||
// write RGB
|
||||
auto value_r = r / 127.f;
|
||||
auto value_g = g / 127.f;
|
||||
auto value_b = b / 127.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(games::popn::Lights::TopLED_R), value_r);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::popn::Lights::TopLED_G), value_g);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::popn::Lights::TopLED_B), value_b);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_led_set_rgb_mask(int a1, char a2, char a3, long a4) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hdxs_update_control_status_buffer(int a1) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_set_framing_err_packet_send_interval(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::HDXSModule::HDXSModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("HDXS", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::HDXSModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_led_scroll);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_led_set_pattern);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_led_set_rgb_mask);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_set_framing_err_packet_send_interval);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HDXSModule : public ACIOModule {
|
||||
public:
|
||||
HDXSModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "hgth.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "cfg/api.h"
|
||||
#include "games/rf3d/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[32] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_hgth_set_senddata(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hgth_update_recvdata() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Road Fighters 3D
|
||||
if (avs::game::is_model("JGT")) {
|
||||
|
||||
// keypad mirror fix
|
||||
acio::ICCA_FLIP_ROWS = true;
|
||||
|
||||
// variables
|
||||
uint16_t wheel = 0x7FFF;
|
||||
uint16_t accelerator = 0x00;
|
||||
uint16_t brake = 0x00;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::rf3d::get_buttons();
|
||||
|
||||
// check buttons
|
||||
bool wheel_button = false;
|
||||
bool accelerate_button = false;
|
||||
bool brake_button = false;
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::WheelLeft))) {
|
||||
wheel -= 0x7FFF;
|
||||
wheel_button = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::WheelRight))) {
|
||||
wheel += 0x8000;
|
||||
wheel_button = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Accelerate))) {
|
||||
accelerator = 0xFFFF;
|
||||
accelerate_button = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Brake))) {
|
||||
brake = 0xFFFF;
|
||||
brake_button = true;
|
||||
}
|
||||
|
||||
// analogs
|
||||
auto &analogs = games::rf3d::get_analogs();
|
||||
if (!wheel_button && analogs.at(games::rf3d::Analogs::Wheel).isSet()) {
|
||||
wheel = (uint16_t) (Analogs::getState(RI_MGR, analogs.at(games::rf3d::Analogs::Wheel)) * 0xFFFF);
|
||||
}
|
||||
if (!accelerate_button && analogs.at(games::rf3d::Analogs::Accelerate).isSet()) {
|
||||
accelerator = (uint16_t) (Analogs::getState(RI_MGR, analogs.at(games::rf3d::Analogs::Accelerate)) * 0xFFFF);
|
||||
}
|
||||
if (!brake_button && analogs.at(games::rf3d::Analogs::Brake).isSet()) {
|
||||
brake = (uint16_t) (Analogs::getState(RI_MGR, analogs.at(games::rf3d::Analogs::Brake)) * 0xFFFF);
|
||||
}
|
||||
|
||||
// write values
|
||||
*((uint16_t *) STATUS_BUFFER + 1) = wheel;
|
||||
*((uint16_t *) STATUS_BUFFER + 2) = accelerator;
|
||||
*((uint16_t *) STATUS_BUFFER + 3) = brake;
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_hgth_get_recvdata(void *buffer) {
|
||||
|
||||
// copy buffer
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hgth_directreq_set_handle_limit(char a1, int *a2) {
|
||||
*a2 = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hgth_directreq_set_handle_limit_isfinished(int *a1) {
|
||||
*a1 = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::HGTHModule::HGTHModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("HGTH", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::HGTHModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_set_senddata);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_update_recvdata);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_get_recvdata);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_directreq_set_handle_limit);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_directreq_set_handle_limit_isfinished);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HGTHModule : public ACIOModule {
|
||||
public:
|
||||
HGTHModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
#include "i36g.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "avs/game.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/mga/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[88 * 2] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_i36g_add_coin(int a1, int a2, int a3) {
|
||||
|
||||
// not so sure we want to add coins
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_consume_coinstock(int a1, int a2, int a3) {
|
||||
eamuse_coin_consume_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_coin_blocker_close(int a1, int a2) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_coin_blocker_open(int a1, int a2) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_lamp_bright(uint32_t device, uint32_t lamp_bits, uint8_t brightness) {
|
||||
|
||||
// calculate value
|
||||
float value = (float) brightness / 255.f;
|
||||
|
||||
// get lights
|
||||
auto &lights = games::mga::get_lights();
|
||||
|
||||
// cabinet device
|
||||
if (device == 21) {
|
||||
if (lamp_bits & 1) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::LeftR], value);
|
||||
}
|
||||
if (lamp_bits & 2) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::LeftG], value);
|
||||
}
|
||||
if (lamp_bits & 4) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::LeftB], value);
|
||||
}
|
||||
if (lamp_bits & 8) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::RightR], value);
|
||||
}
|
||||
if (lamp_bits & 16) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::RightG], value);
|
||||
}
|
||||
if (lamp_bits & 32) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::RightB], value);
|
||||
}
|
||||
if (lamp_bits & 512) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::Start], value);
|
||||
}
|
||||
}
|
||||
|
||||
// gun device
|
||||
if (device == 22) {
|
||||
if (lamp_bits & 1) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunR], value);
|
||||
}
|
||||
if (lamp_bits & 2) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunG], value);
|
||||
}
|
||||
if (lamp_bits & 4) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunB], value);
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_motor_power(int device, uint8_t strength) {
|
||||
|
||||
// gun device
|
||||
if (device == 22) {
|
||||
float value = (float) strength / 255.f;
|
||||
auto &lights = games::mga::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunVibration], value);
|
||||
}
|
||||
|
||||
// success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_current_coinstock(int a1, int a2, int *a3) {
|
||||
|
||||
// get coinstock
|
||||
*a3 = eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_get_coin_input_wave_buffer(int a1, char *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_i36g_get_control_status_buffer(int device, void *buffer) {
|
||||
|
||||
// cabinet buffer
|
||||
if (device == 21) {
|
||||
memcpy(buffer, &STATUS_BUFFER[0], 88);
|
||||
}
|
||||
|
||||
// gun buffer
|
||||
if (device == 22) {
|
||||
memcpy(buffer, &STATUS_BUFFER[88], 88);
|
||||
}
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_get_softwareid(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_get_systemid(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_get_watchdog_status(int a1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_i36g_get_watchdog_time_min(int a1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_i36g_get_watchdog_time_now(int a1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_lock_coincounter(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_coin_input_wave(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_get_control_status(int a1, int *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_check(int a1, char *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_secplug_check_isfinished(int a1, int *a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_check_softwareplug(int a1, char *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_check_systemplug(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_missing_check(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_secplug_missing_check_isfinished(int a1, int *a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_volume_control(int a1, char a2, char a3, char a4, char a5) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_volume_control_isfinished(int a1, int *ret_state) {
|
||||
*ret_state = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_set_cmdmode(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_set_framing_err_packet_send_interval(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_set_watchdog_time(int a1, short a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_unlock_coincounter(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_update_control_status_buffer(int node) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Metal Gear Arcade
|
||||
if (avs::game::is_model("I36")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::mga::get_buttons();
|
||||
|
||||
// cabinet device
|
||||
if (node == 21) {
|
||||
|
||||
// clear status buffer
|
||||
memset(&STATUS_BUFFER[0], 0, 88);
|
||||
|
||||
// update buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Service))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 44);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Test))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 45);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::CoinMech))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 42);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Start))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 124);
|
||||
}
|
||||
}
|
||||
|
||||
// gun device
|
||||
if (node == 22) {
|
||||
|
||||
// clear status buffer
|
||||
memset(&STATUS_BUFFER[88], 0, 88);
|
||||
|
||||
// update buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::TriggerButton))
|
||||
|| (GetKeyState(VK_LBUTTON) & 0x100) != 0) { // mouse button
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 109);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::FrontTop))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 108);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::FrontBottom))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 106);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SideLeft))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 107);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SideRight))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 105);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SideLever))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 104);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SwitchButton))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 125);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Top))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 124);
|
||||
}
|
||||
|
||||
// joy stick
|
||||
unsigned short joy_x = 0x7FFF;
|
||||
unsigned short joy_y = 0x7FFF;
|
||||
bool joy_x_pressed = false;
|
||||
bool joy_y_pressed = false;
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyForwards])) {
|
||||
joy_y -= 0x7FFF;
|
||||
joy_y_pressed = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyBackwards])) {
|
||||
joy_y += 0x7FFF;
|
||||
joy_y_pressed = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyLeft])) {
|
||||
joy_x -= 0x7FFF;
|
||||
joy_x_pressed = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyRight])) {
|
||||
joy_x += 0x7FFF;
|
||||
joy_x_pressed = true;
|
||||
}
|
||||
|
||||
// joy stick raw input
|
||||
auto &analogs = games::mga::get_analogs();
|
||||
if (!joy_x_pressed && analogs[games::mga::Analogs::JoyX].isSet()) {
|
||||
joy_x = (unsigned short) (Analogs::getState(RI_MGR, analogs[games::mga::Analogs::JoyX]) * 0xFFFF);
|
||||
}
|
||||
if (!joy_y_pressed && analogs[games::mga::Analogs::JoyY].isSet()) {
|
||||
joy_y = (unsigned short) (Analogs::getState(RI_MGR, analogs[games::mga::Analogs::JoyY]) * 0xFFFF);
|
||||
}
|
||||
|
||||
// save joy stick
|
||||
STATUS_BUFFER[88 + 42] = LOBYTE(joy_y);
|
||||
STATUS_BUFFER[88 + 43] = HIBYTE(joy_y);
|
||||
STATUS_BUFFER[88 + 44] = LOBYTE(joy_x);
|
||||
STATUS_BUFFER[88 + 45] = HIBYTE(joy_x);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_watchdog_off(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::I36GModule::I36GModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("I36G", module, hookMode) {
|
||||
this->status_buffer = &STATUS_BUFFER[0];
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::I36GModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_add_coin);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_lamp_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_motor_power);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_coin_input_wave_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_coin_input_wave);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_get_control_status);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check_softwareplug);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check_systemplug);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_volume_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_set_cmdmode);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_set_framing_err_packet_send_interval);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_watchdog_off);
|
||||
|
||||
// I36S links
|
||||
this->hook((void *) ac_io_i36g_update_control_status_buffer,
|
||||
"ac_io_i36s_update_control_status_buffer");
|
||||
this->hook((void *) ac_io_i36g_get_control_status_buffer,
|
||||
"ac_io_i36s_get_control_status_buffer");
|
||||
this->hook((void *) ac_io_i36g_set_cmdmode,
|
||||
"ac_io_i36s_set_cmdmode");
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36GModule : public ACIOModule {
|
||||
public:
|
||||
I36GModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#include "i36i.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
//using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[48];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_i36i_ps3_controller_pwr_on() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_ps3_controller_pwr_off() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_destroy_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// Winning Eleven
|
||||
if (avs::game::is_model({ "KCK", "NCK" })) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_get_control_status_buffer(uint8_t *buffer) {
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_usb_controller_bus_IO() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_usb_controller_bus_PC() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_req_get_usb_desc(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_req_get_usb_desc_isfinished(
|
||||
int a1, uint32_t *a2, int a3, uint32_t *out_size, uint8_t *in_data, unsigned int in_size) {
|
||||
|
||||
// DualShock 3 device descriptor
|
||||
static uint8_t DS3_DESC[] {
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x4C, 0x05, // idVendor 0x054C
|
||||
0x68, 0x02, // idProduct 0x0268
|
||||
0x00, 0x01, // bcdDevice 1.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
// copy descriptor to buffer
|
||||
*out_size = MIN(sizeof(DS3_DESC), in_size);
|
||||
memcpy(in_data, DS3_DESC, *out_size);
|
||||
|
||||
// we apparently need this too
|
||||
*a2 = 3;
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
acio::I36IModule::I36IModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("I36I", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::I36IModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_ps3_controller_pwr_on);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_ps3_controller_pwr_off);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_usb_controller_bus_IO);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_usb_controller_bus_PC);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_req_get_usb_desc);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_req_get_usb_desc_isfinished);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36IModule : public ACIOModule {
|
||||
public:
|
||||
I36IModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
#include <cmath>
|
||||
#include "icca.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/time.h"
|
||||
|
||||
// settings
|
||||
namespace acio {
|
||||
bool ICCA_FLIP_ROWS = false;
|
||||
bool ICCA_COMPAT_ACTIVE = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
struct ICCA_STATUS {
|
||||
uint8_t status_code;
|
||||
uint8_t solenoid;
|
||||
uint8_t front_sensor;
|
||||
uint8_t rear_sensor;
|
||||
uint8_t uid[8];
|
||||
int32_t error;
|
||||
uint32_t key_edge;
|
||||
uint32_t key_level;
|
||||
};
|
||||
struct ICCA_STATUS_LA9 {
|
||||
uint8_t status_code;
|
||||
uint8_t card_in;
|
||||
uint8_t uid[8];
|
||||
uint8_t error;
|
||||
uint8_t uid2[8];
|
||||
};
|
||||
static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes");
|
||||
|
||||
enum ICCA_WORKFLOW {
|
||||
STEP = 0,
|
||||
SLEEP = 1,
|
||||
START = 2,
|
||||
INIT = 3,
|
||||
READY = 4,
|
||||
GET_USERID = 5,
|
||||
ACTIVE = 6,
|
||||
EJECT = 7,
|
||||
EJECT_CHECK = 8,
|
||||
END = 9,
|
||||
CLOSE_EJECT = 10,
|
||||
CLOSE_E_CHK = 11,
|
||||
CLOSE_END = 12,
|
||||
ERR_GETUID = -2,
|
||||
ERR_REMOVED = -11
|
||||
};
|
||||
|
||||
// used by RA as the status_code
|
||||
enum ICCA_STATUS_CODE {
|
||||
EMPTY = 1,
|
||||
ENGAGED = 2
|
||||
};
|
||||
|
||||
struct ICCA_UNIT {
|
||||
struct ICCA_STATUS status {};
|
||||
enum ICCA_WORKFLOW state = STEP;
|
||||
enum ICCA_WORKFLOW state_jdz = SLEEP;
|
||||
bool card_cmd_pressed = false;
|
||||
bool card_in = false;
|
||||
double card_in_time = 0.0;
|
||||
char key_serial = 0;
|
||||
bool uid_skip = false;
|
||||
bool initialized = false;
|
||||
int felica_retries = 0;
|
||||
};
|
||||
static ICCA_UNIT ICCA_UNITS[2] {};
|
||||
static bool IS_LAST_CARD_FELICA = false;
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static double CARD_TIMEOUT = 2.0;
|
||||
|
||||
static inline int icca_get_active_count() {
|
||||
int active_count = 0;
|
||||
for (auto unit : ICCA_UNITS) {
|
||||
active_count += unit.initialized;
|
||||
}
|
||||
return active_count;
|
||||
}
|
||||
|
||||
static inline int icca_get_unit_id(int unit_id) {
|
||||
// on RA the unit_id is zero-indexed
|
||||
if (avs::game::is_model("JDZ")) {
|
||||
return unit_id;
|
||||
}
|
||||
|
||||
if (icca_get_active_count() < 2)
|
||||
return 1;
|
||||
else {
|
||||
if (unit_id > 1) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_card(int unit_id) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eamio keypress
|
||||
int index = unit_id > 0 && icca_get_active_count() > 1 ? 1 : 0;
|
||||
bool kb_insert_press = (eamuse_get_keypad_state(index) & (1 << EAM_IO_INSERT)) > 0;
|
||||
static bool kb_insert_press_old[2] = {false, false};
|
||||
|
||||
// get unit
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
|
||||
const bool card_presented = eamuse_card_insert_consume(icca_get_active_count(), unit_id);
|
||||
const bool key_pressed = (kb_insert_press && !kb_insert_press_old[unit_id]);
|
||||
|
||||
// beatstream and nostalgia have logic that requires ac_io_icca_get_uid_felica to return the
|
||||
// exact same card number multiple times in a row in order for the card number to be read...
|
||||
// for whatever reason setting this to 3-4 doesn't work the very first time the game boots up
|
||||
// so we use 10 just to be safe
|
||||
const bool need_felica_retries = avs::game::is_model({"NBT", "PAN"});
|
||||
if (need_felica_retries && (card_presented || key_pressed)) {
|
||||
unit->felica_retries = 10;
|
||||
}
|
||||
|
||||
// check for card insert
|
||||
if (card_presented || key_pressed || (0 < unit->felica_retries)) {
|
||||
if (!unit->card_cmd_pressed) {
|
||||
unit->card_cmd_pressed = true;
|
||||
if (unit->state == GET_USERID || unit->state == CLOSE_EJECT || unit->state == STEP) {
|
||||
if (unit->uid_skip || eamuse_get_card(icca_get_active_count(), unit_id, unit->status.uid)) {
|
||||
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
||||
|
||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||
unit->state_jdz = ACTIVE;
|
||||
unit->status.error = 0;
|
||||
unit->status.front_sensor = 1;
|
||||
unit->status.rear_sensor = 1;
|
||||
} else {
|
||||
unit->state = ERR_GETUID;
|
||||
unit->state_jdz = ERR_GETUID;
|
||||
unit->status.front_sensor = 1;
|
||||
unit->status.rear_sensor = 1;
|
||||
memset(unit->status.uid, 0, 8);
|
||||
}
|
||||
unit->card_in = true;
|
||||
unit->card_in_time = get_performance_seconds();
|
||||
} else if (unit->state == EJECT_CHECK) {
|
||||
unit->state = SLEEP;
|
||||
unit->card_in = false;
|
||||
unit->status.front_sensor = 0;
|
||||
unit->status.rear_sensor = 0;
|
||||
} else if (unit->state_jdz == READY) {
|
||||
unit->state = GET_USERID;
|
||||
unit->state_jdz = GET_USERID;
|
||||
unit->status.error = 0;
|
||||
unit->status.front_sensor = 1;
|
||||
unit->status.rear_sensor = 1;
|
||||
unit->card_in = true;
|
||||
unit->card_in_time = get_performance_seconds();
|
||||
}
|
||||
} else {
|
||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||
if (unit->state_jdz == READY) {
|
||||
unit->state_jdz = GET_USERID;
|
||||
}
|
||||
unit->status.error = 0;
|
||||
unit->status.front_sensor = 1;
|
||||
unit->status.rear_sensor = 1;
|
||||
unit->card_in = true;
|
||||
unit->card_in_time = get_performance_seconds();
|
||||
}
|
||||
} else {
|
||||
unit->card_cmd_pressed = false;
|
||||
unit->state = CLOSE_EJECT;
|
||||
if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) {
|
||||
unit->card_in = false;
|
||||
memset(unit->status.uid, 0, 8);
|
||||
if (unit->state_jdz == ACTIVE || unit->state_jdz == GET_USERID) {
|
||||
unit->state_jdz = ERR_REMOVED;
|
||||
}
|
||||
unit->status.front_sensor = 0;
|
||||
unit->status.rear_sensor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// save state
|
||||
kb_insert_press_old[unit_id] = kb_insert_press;
|
||||
}
|
||||
|
||||
static bool KEYPAD_LAST[2][12];
|
||||
static uint32_t KEYPAD_EAMUSE_MAPPING[] = {
|
||||
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
|
||||
};
|
||||
static uint32_t KEYPAD_KEY_CODES[] = {
|
||||
0x100,
|
||||
0x200,
|
||||
0x2000,
|
||||
2,
|
||||
0x400,
|
||||
0x4000,
|
||||
4,
|
||||
0x800,
|
||||
0x8000,
|
||||
8,
|
||||
1,
|
||||
0x1000
|
||||
};
|
||||
static uint32_t KEYPAD_KEY_CODE_NUMS[] = {
|
||||
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
|
||||
};
|
||||
|
||||
static inline void keypad_update(int unit_id) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset unit
|
||||
struct ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
unit->status.key_level = 0;
|
||||
unit->status.error = 0;
|
||||
|
||||
// get eamu state
|
||||
int index = unit_id > 0 && icca_get_active_count() > 1 ? 1 : 0;
|
||||
uint16_t eamu_state = eamuse_get_keypad_state(index);
|
||||
|
||||
// iterate keypad
|
||||
for (int n = 0; n < 12; n++) {
|
||||
int i = n;
|
||||
|
||||
// flip 123 with 789
|
||||
if (acio::ICCA_FLIP_ROWS) {
|
||||
if (!n)
|
||||
i = 11;
|
||||
else if (n < 4)
|
||||
i = n + 6;
|
||||
else if (n > 6 && n < 10)
|
||||
i = n - 6;
|
||||
else if (n == 11)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
// check if pressed
|
||||
if ((eamu_state & (1 << KEYPAD_EAMUSE_MAPPING[i])))
|
||||
{
|
||||
unit->status.key_level |= KEYPAD_KEY_CODES[i];
|
||||
|
||||
if (!KEYPAD_LAST[unit_id][i]) {
|
||||
unit->status.key_edge = KEYPAD_KEY_CODES[n] << 16;
|
||||
unit->status.key_edge |= 0x80 | (unit->key_serial << 4) | KEYPAD_KEY_CODE_NUMS[n];
|
||||
|
||||
unit->key_serial += 1;
|
||||
unit->key_serial &= 0x07;
|
||||
}
|
||||
KEYPAD_LAST[unit_id][i] = true;
|
||||
} else {
|
||||
unit->status.key_edge &= ~(KEYPAD_KEY_CODES[n] << 16);
|
||||
|
||||
KEYPAD_LAST[unit_id][i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_icca_cardunit_init(int unit_id) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
|
||||
// dirty workaround code
|
||||
if (icca_get_active_count() < 1)
|
||||
ICCA_UNITS[unit_id].initialized = true;
|
||||
else {
|
||||
ICCA_UNITS[0].initialized = true;
|
||||
ICCA_UNITS[1].initialized = true;
|
||||
}
|
||||
|
||||
// initial poll
|
||||
eamuse_get_keypad_state(unit_id);
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_cardunit_init_isfinished(int unit_id, DWORD *status) {
|
||||
*status = READY;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_crypt_init(int unit_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_device_control_iccard_power_supply_off(int unit_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_device_control_iccard_power_supply_on(int unit_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_device_control_isfinished(int unit_id, DWORD *a2) {
|
||||
if (a2 && avs::game::is_model("KFC")) {
|
||||
*a2 = 6;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_get_keep_alive_error(int unit_id, DWORD *a2) {
|
||||
*a2 = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_get_status(void *a1, void *a2) {
|
||||
|
||||
// Metal Gear Arcade and Charge Machine had the args swapped so we need to check for valid pointers!
|
||||
if (reinterpret_cast<uintptr_t>(a2) > 2) {
|
||||
std::swap(a1, a2);
|
||||
|
||||
// honestly this could be used to detect if compat mode should be active
|
||||
// but we are too lazy to check if all games still work with this change
|
||||
//acio::ICCA_COMPAT_ACTIVE = true;
|
||||
}
|
||||
|
||||
// and best just leave this casting mess alone unless something is wrong with it.
|
||||
// long long is required because casting to int loses precision on 64-bit
|
||||
void *status = a1;
|
||||
int unit_id = static_cast<int>(reinterpret_cast<long long>(a2));
|
||||
|
||||
// update state
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
keypad_update(unit_id);
|
||||
update_card(unit_id);
|
||||
|
||||
// copy state to output buffer
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
if (avs::game::is_model("JDZ")) {
|
||||
unit->status.status_code = unit->card_in ? ENGAGED : EMPTY;
|
||||
} else {
|
||||
unit->status.status_code = unit->state;
|
||||
}
|
||||
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
|
||||
|
||||
// funny workaround
|
||||
if (acio::ICCA_COMPAT_ACTIVE) {
|
||||
if (avs::game::is_model("LA9")) {
|
||||
auto p = (ICCA_STATUS*) status;
|
||||
|
||||
ICCA_STATUS_LA9 p_la9;
|
||||
p_la9.status_code = unit->state;
|
||||
p_la9.card_in = unit->card_in;
|
||||
memcpy(p_la9.uid, p->uid, sizeof(p_la9.uid));
|
||||
p_la9.error = p->error;
|
||||
memcpy(p_la9.uid2, p->uid, sizeof(p_la9.uid));
|
||||
|
||||
memcpy(status, &p_la9, sizeof(ICCA_STATUS_LA9));
|
||||
} else {
|
||||
// the struct is different (28 bytes instead of 24) but nobody ain't got time for that
|
||||
auto p = (ICCA_STATUS*) status;
|
||||
p->error = p->key_level << 16;
|
||||
p->front_sensor = p->uid[0];
|
||||
p->rear_sensor = p->uid[1];
|
||||
for (size_t i = 2; i < sizeof(p->uid); i++) {
|
||||
p->uid[i - 2] = p->uid[i];
|
||||
}
|
||||
p->uid[sizeof(p->uid) - 2] = 0;
|
||||
p->uid[sizeof(p->uid) - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_get_uid(int unit_id, char *card) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
|
||||
// copy card
|
||||
memcpy(card, unit->status.uid, 8);
|
||||
|
||||
// set felica flag
|
||||
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
||||
|
||||
// check for error
|
||||
return unit->state != ERR_GETUID;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_get_uid_felica(int unit_id, char *card) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
|
||||
// copy card
|
||||
memcpy(card, unit->status.uid, 8);
|
||||
|
||||
// set felica flag
|
||||
bool felica = is_card_uid_felica(unit->status.uid);
|
||||
card[8] = (char) (felica ? 1 : 0);
|
||||
IS_LAST_CARD_FELICA = felica;
|
||||
|
||||
if (0 < unit->felica_retries) {
|
||||
unit->felica_retries--;
|
||||
}
|
||||
|
||||
// check for error
|
||||
return unit->state != ERR_GETUID;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_is_felica() {
|
||||
return IS_LAST_CARD_FELICA;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_req_uid(int unit_id) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
unit->state = GET_USERID;
|
||||
|
||||
update_card(unit_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_icca_req_uid_isfinished(int unit_id, DWORD *read_state) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
if (avs::game::is_model("JDZ")) {
|
||||
// hack for RA - only ever seen it as this
|
||||
*read_state = 8;
|
||||
return 1;
|
||||
}
|
||||
if (unit->card_in) {
|
||||
if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) {
|
||||
unit->state = END;
|
||||
} else {
|
||||
unit->state = ERR_GETUID;
|
||||
}
|
||||
unit->card_in = false;
|
||||
}
|
||||
*read_state = unit->state;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_icca_send_keep_alive_packet(int a1, int a2, int a3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_icca_workflow_jdz(int workflow, int unit_id) {
|
||||
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
switch (workflow) {
|
||||
// RA uses "STEP" as more of a polling thing it seems
|
||||
case STEP:
|
||||
switch (unit->state_jdz) {
|
||||
case CLOSE_EJECT:
|
||||
unit->state_jdz = CLOSE_E_CHK;
|
||||
break;
|
||||
case CLOSE_E_CHK:
|
||||
unit->state_jdz = CLOSE_END;
|
||||
break;
|
||||
case CLOSE_END:
|
||||
unit->state_jdz = SLEEP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SLEEP:
|
||||
unit->state = SLEEP;
|
||||
break;
|
||||
case START:
|
||||
unit->state_jdz = INIT;
|
||||
break;
|
||||
case INIT:
|
||||
case READY:
|
||||
unit->state_jdz = READY;
|
||||
break;
|
||||
case CLOSE_EJECT:
|
||||
unit->state_jdz = CLOSE_EJECT;
|
||||
return CLOSE_E_CHK;
|
||||
case CLOSE_END:
|
||||
unit->state_jdz = SLEEP;
|
||||
break;
|
||||
default:
|
||||
// should probably log a warning here
|
||||
break;
|
||||
}
|
||||
return unit->state_jdz;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
|
||||
// RA uses a different workflow process for the slotted readers; treat it differently
|
||||
if (avs::game::is_model("JDZ")) {
|
||||
return ac_io_icca_workflow_jdz(workflow, unit_id);
|
||||
}
|
||||
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
switch (workflow) {
|
||||
case STEP:
|
||||
unit->state = STEP;
|
||||
break;
|
||||
case SLEEP:
|
||||
unit->state = SLEEP;
|
||||
break;
|
||||
case INIT:
|
||||
unit->state = READY;
|
||||
break;
|
||||
case START:
|
||||
if (unit->card_in)
|
||||
unit->state = ACTIVE;
|
||||
else
|
||||
unit->state = READY;
|
||||
break;
|
||||
case EJECT:
|
||||
unit->card_in = false;
|
||||
break;
|
||||
case CLOSE_EJECT:
|
||||
unit->state = unit->card_in ? EJECT_CHECK : SLEEP;
|
||||
break;
|
||||
case CLOSE_END:
|
||||
unit->state = SLEEP;
|
||||
break;
|
||||
case GET_USERID:
|
||||
unit->state = GET_USERID;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return unit->state;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_req_status(int a1, char a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_req_status_isfinished(int a1, int *a2) {
|
||||
*a2 = 11;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::ICCAModule::ICCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("ICCA", module, hookMode) {
|
||||
this->status_buffer = (uint8_t*) &ICCA_UNITS[0];
|
||||
this->status_buffer_size = sizeof(ICCA_UNITS);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::ICCAModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// workaround for RA; ac_io_icca_cardunit_init is never called so treat like both are initialized
|
||||
if (avs::game::is_model("JDZ")) {
|
||||
ICCA_UNITS[0].initialized = true;
|
||||
ICCA_UNITS[1].initialized = true;
|
||||
|
||||
CARD_TIMEOUT = 10.0; // keep the card in the slot for longer
|
||||
}
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_crypt_init);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_device_control_iccard_power_supply_off);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_device_control_iccard_power_supply_on);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_device_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_keep_alive_error);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_uid);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_uid_felica);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_is_felica);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_uid);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_uid_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_send_keep_alive_packet);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_workflow);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_status);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_status_isfinished);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
// settings
|
||||
extern bool ICCA_FLIP_ROWS;
|
||||
extern bool ICCA_COMPAT_ACTIVE;
|
||||
|
||||
class ICCAModule : public ACIOModule {
|
||||
public:
|
||||
ICCAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
||||
static inline bool is_card_uid_felica(uint8_t *uid) {
|
||||
return uid[0] != 0xE0 && uid[1] != 0x04;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "j32d.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/ftt/io.h"
|
||||
#include "games/scotto/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint32_t STATUS_BUFFER[20] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static uint32_t STATUS_BUFFER_COUNTER = 1;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_j32d_get_control_status_buffer(size_t a1, void* buffer, int a3) {
|
||||
|
||||
// set counter
|
||||
STATUS_BUFFER[14] = STATUS_BUFFER_COUNTER++;
|
||||
|
||||
// copy buffer
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// FutureTomTom
|
||||
if (avs::game::is_model("MMD")) {
|
||||
|
||||
// process buttons
|
||||
auto &buttons = games::ftt::get_buttons();
|
||||
float pad1_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad1));
|
||||
float pad2_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad2));
|
||||
float pad3_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad3));
|
||||
float pad4_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad4));
|
||||
// FIXME(felix): this logic seems wrong for analog handling but correct for digital inputs
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad1))) {
|
||||
STATUS_BUFFER[6] = (int) (51.f * pad1_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad2))) {
|
||||
STATUS_BUFFER[7] = (int) (51.f * pad2_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad3))) {
|
||||
STATUS_BUFFER[8] = (int) (51.f * pad3_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad4))) {
|
||||
STATUS_BUFFER[9] = (int) (51.f * pad4_vel + 0.5f);
|
||||
}
|
||||
|
||||
// process analogs
|
||||
auto &analogs = games::ftt::get_analogs();
|
||||
auto &pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
||||
auto &pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
||||
auto &pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
||||
auto &pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
||||
if (pad1_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad1_analog) + 0.5f);
|
||||
STATUS_BUFFER[6] = MAX(STATUS_BUFFER[6], val);
|
||||
}
|
||||
if (pad2_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad2_analog) + 0.5f);
|
||||
STATUS_BUFFER[7] = MAX(STATUS_BUFFER[7], val);
|
||||
}
|
||||
if (pad3_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad3_analog) + 0.5f);
|
||||
STATUS_BUFFER[8] = MAX(STATUS_BUFFER[8], val);
|
||||
}
|
||||
if (pad4_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad4_analog) + 0.5f);
|
||||
STATUS_BUFFER[9] = MAX(STATUS_BUFFER[9], val);
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::scotto::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Cup1))) {
|
||||
STATUS_BUFFER[5] |= 0x1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Cup2))) {
|
||||
STATUS_BUFFER[5] |= 0x2;
|
||||
}
|
||||
|
||||
// process button emulation for pads
|
||||
float first_pad_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::FirstPad));
|
||||
float pad_a_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadA));
|
||||
float pad_b_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadB));
|
||||
float pad_c_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadC));
|
||||
float pad_d_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadD));
|
||||
float pad_e_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadE));
|
||||
float pad_f_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadF));
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::FirstPad))) {
|
||||
STATUS_BUFFER[6] = (int) (191.f * first_pad_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadA))) {
|
||||
STATUS_BUFFER[7] = (int) (51.f * pad_a_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadB))) {
|
||||
STATUS_BUFFER[8] = (int) (51.f * pad_b_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadC))) {
|
||||
STATUS_BUFFER[9] = (int) (51.f * pad_c_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadD))) {
|
||||
STATUS_BUFFER[10] = (int) (51.f * pad_d_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadE))) {
|
||||
STATUS_BUFFER[11] = (int) (51.f * pad_e_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadF))) {
|
||||
STATUS_BUFFER[12] = (int) (51.f * pad_f_vel + 0.5f);
|
||||
}
|
||||
|
||||
// TODO(felix): analogs
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::J32DModule::J32DModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("J32D", module, hookMode) {
|
||||
this->status_buffer = (uint8_t*) &STATUS_BUFFER[0];
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::J32DModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_j32d_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_j32d_update_control_status_buffer);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class J32DModule : public ACIOModule {
|
||||
public:
|
||||
J32DModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
#include "kfca.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/bs/io.h"
|
||||
#include "games/nost/io.h"
|
||||
#include "games/scotto/io.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "games/sdvx/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
#define DEBUG_VERBOSE 0
|
||||
|
||||
#if DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
// globals
|
||||
uint8_t KFCA_VOL_SOUND = 96;
|
||||
uint8_t KFCA_VOL_HEADPHONE = 96;
|
||||
uint8_t KFCA_VOL_EXTERNAL = 96;
|
||||
uint8_t KFCA_VOL_WOOFER = 96;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[64] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static unsigned int KFCA_VOLL = 0;
|
||||
static unsigned int KFCA_VOLR = 0;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_kfca_control_button_led(unsigned int button, bool state) {
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// control mapping
|
||||
static const size_t mapping[] = {
|
||||
games::sdvx::Lights::BT_A,
|
||||
games::sdvx::Lights::BT_B,
|
||||
games::sdvx::Lights::BT_C,
|
||||
games::sdvx::Lights::BT_D,
|
||||
games::sdvx::Lights::FX_L,
|
||||
games::sdvx::Lights::FX_R,
|
||||
games::sdvx::Lights::START,
|
||||
games::sdvx::Lights::GENERATOR_B,
|
||||
};
|
||||
|
||||
// check if button is mapped
|
||||
if (button < 8) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
|
||||
// write light
|
||||
float value = state ? 1.f : 0.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[button]), value);
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// control mapping
|
||||
static const size_t mapping[] = {
|
||||
games::scotto::Lights::PAD_F_B,
|
||||
games::scotto::Lights::PAD_E_R,
|
||||
games::scotto::Lights::PAD_E_B,
|
||||
~0u,
|
||||
~0u,
|
||||
~0u,
|
||||
games::scotto::Lights::PAD_F_R,
|
||||
games::scotto::Lights::BUTTON,
|
||||
};
|
||||
|
||||
// check if button is mapped
|
||||
if (button < std::size(mapping) && button[mapping] != ~0u) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::scotto::get_lights();
|
||||
|
||||
// write light
|
||||
float value = state ? 1.f : 0.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[button]), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_control_coin_blocker_close(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_control_coin_blocker_open(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_control_led_bright(uint32_t led_field, uint8_t brightness) {
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
|
||||
// control mapping
|
||||
static const size_t mapping[] {
|
||||
games::sdvx::Lights::WING_LEFT_UP_R,
|
||||
games::sdvx::Lights::WING_LEFT_UP_G,
|
||||
games::sdvx::Lights::WING_LEFT_UP_B,
|
||||
games::sdvx::Lights::WING_RIGHT_UP_R,
|
||||
games::sdvx::Lights::WING_RIGHT_UP_G,
|
||||
games::sdvx::Lights::WING_RIGHT_UP_B,
|
||||
games::sdvx::Lights::WING_LEFT_LOW_R,
|
||||
games::sdvx::Lights::WING_LEFT_LOW_G,
|
||||
games::sdvx::Lights::WING_LEFT_LOW_B,
|
||||
games::sdvx::Lights::WING_RIGHT_LOW_R,
|
||||
games::sdvx::Lights::WING_RIGHT_LOW_G,
|
||||
games::sdvx::Lights::WING_RIGHT_LOW_B,
|
||||
games::sdvx::Lights::WOOFER_R,
|
||||
games::sdvx::Lights::WOOFER_G,
|
||||
games::sdvx::Lights::WOOFER_B,
|
||||
games::sdvx::Lights::CONTROLLER_R,
|
||||
games::sdvx::Lights::CONTROLLER_G,
|
||||
games::sdvx::Lights::CONTROLLER_B,
|
||||
games::sdvx::Lights::GENERATOR_R,
|
||||
games::sdvx::Lights::GENERATOR_G,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 255.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BeatStream
|
||||
if (avs::game::is_model("NBT")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::bs::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
~0u, ~0u, ~0u,
|
||||
games::bs::Lights::RightR,
|
||||
games::bs::Lights::RightG,
|
||||
games::bs::Lights::RightB,
|
||||
games::bs::Lights::LeftR,
|
||||
games::bs::Lights::LeftG,
|
||||
games::bs::Lights::LeftB,
|
||||
games::bs::Lights::BottomR,
|
||||
games::bs::Lights::BottomG,
|
||||
games::bs::Lights::BottomB,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] != ~0u && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::nost::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
~0u, ~0u, ~0u,
|
||||
games::nost::Lights::TitleR,
|
||||
games::nost::Lights::TitleG,
|
||||
games::nost::Lights::TitleB,
|
||||
~0u, ~0u, ~0u,
|
||||
games::nost::Lights::BottomR,
|
||||
games::nost::Lights::BottomG,
|
||||
games::nost::Lights::BottomB,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] != ~0u && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::scotto::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
games::scotto::Lights::CUP_R,
|
||||
games::scotto::Lights::CUP_G,
|
||||
games::scotto::Lights::CUP_B,
|
||||
games::scotto::Lights::PAD_A_R,
|
||||
games::scotto::Lights::PAD_A_G,
|
||||
games::scotto::Lights::PAD_A_B,
|
||||
games::scotto::Lights::PAD_B_R,
|
||||
games::scotto::Lights::PAD_B_G,
|
||||
games::scotto::Lights::PAD_B_B,
|
||||
games::scotto::Lights::PAD_C_R,
|
||||
games::scotto::Lights::PAD_C_G,
|
||||
games::scotto::Lights::PAD_C_B,
|
||||
games::scotto::Lights::PAD_D_R,
|
||||
games::scotto::Lights::PAD_D_G,
|
||||
games::scotto::Lights::PAD_D_B,
|
||||
games::scotto::Lights::FIRST_PAD_R,
|
||||
games::scotto::Lights::FIRST_PAD_G,
|
||||
games::scotto::Lights::FIRST_PAD_B,
|
||||
games::scotto::Lights::PAD_F_G,
|
||||
games::scotto::Lights::PAD_E_G,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 255.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_kfca_current_coinstock(int a1, DWORD *a2) {
|
||||
*a2 = (DWORD) eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_kfca_get_control_status_buffer(void *target_buffer) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(target_buffer, STATUS_BUFFER, 64);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_lock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_kfca_req_volume_control(
|
||||
uint8_t vol_sound, uint8_t vol_headphone, uint8_t vol_external, uint8_t vol_woofer) {
|
||||
|
||||
// update globals
|
||||
KFCA_VOL_SOUND = vol_sound;
|
||||
KFCA_VOL_HEADPHONE = vol_headphone;
|
||||
KFCA_VOL_EXTERNAL = vol_external;
|
||||
KFCA_VOL_WOOFER = vol_woofer;
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_SOUND],
|
||||
(100 - vol_sound) / 100.f);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_HEADPHONE],
|
||||
(100 - vol_headphone) / 100.f);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_EXTERNAL],
|
||||
(100 - vol_external) / 100.f);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_WOOFER],
|
||||
(100 - vol_woofer) / 100.f);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_kfca_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_kfca_unlock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
||||
static const int input_offset = 4;
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, 64);
|
||||
|
||||
// SDVX
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::sdvx::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Test))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Service))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Start))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x08;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_A))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x04;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_B))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x02;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_C))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x01;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_D))) {
|
||||
STATUS_BUFFER[input_offset + 11] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_L))) {
|
||||
STATUS_BUFFER[input_offset + 11] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_R))) {
|
||||
STATUS_BUFFER[input_offset + 11] |= 0x08;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Headphone))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x20;
|
||||
}
|
||||
|
||||
// volume left
|
||||
const auto now = get_performance_milliseconds();
|
||||
const auto vol_l_state = socd::socd_clean(0,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right)),
|
||||
now);
|
||||
if (vol_l_state == socd::SocdCCW) {
|
||||
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
} else if (vol_l_state == socd::SocdCW) {
|
||||
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
const auto vol_r_state = socd::socd_clean(1,
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left)),
|
||||
Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right)),
|
||||
now);
|
||||
if (vol_r_state == socd::SocdCCW) {
|
||||
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
} else if (vol_r_state == socd::SocdCW) {
|
||||
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// update volumes
|
||||
auto &analogs = games::sdvx::get_analogs();
|
||||
auto vol_left = KFCA_VOLL;
|
||||
auto vol_right = KFCA_VOLR;
|
||||
if (analogs.at(0).isSet() || analogs.at(1).isSet()) {
|
||||
vol_left += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_L)) * 1023.99f);
|
||||
vol_right += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_R)) * 1023.99f);
|
||||
}
|
||||
|
||||
// proper loops
|
||||
vol_left %= 1024;
|
||||
vol_right %= 1024;
|
||||
|
||||
log_debug("kfca", "knobs = {} {}", vol_left, vol_right);
|
||||
|
||||
// save volumes in buffer
|
||||
STATUS_BUFFER[input_offset + 16 + 0] |= (unsigned char) ((vol_left << 6) & 0xFF);
|
||||
STATUS_BUFFER[input_offset + 16 + 1] |= (unsigned char) ((vol_left >> 2) & 0xFF);
|
||||
STATUS_BUFFER[input_offset + 16 + 2] |= (unsigned char) ((vol_right << 6) & 0xFF);
|
||||
STATUS_BUFFER[input_offset + 16 + 3] |= (unsigned char) ((vol_right >> 2) & 0xFF);
|
||||
}
|
||||
|
||||
// Beatstream
|
||||
if (avs::game::is_model("NBT")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::bs::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bs::Buttons::Test))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bs::Buttons::Service))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bs::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
// Nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::nost::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::Service))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::Test))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::scotto::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Test)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Service)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::CoinMech)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Start)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Up)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Down)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x08;
|
||||
}
|
||||
|
||||
// the code also checks `input_offset + 9` for 0x01 but that does not trigger any response
|
||||
// in the "I/O CHECK" scene
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_kfca_watchdog_off() {
|
||||
}
|
||||
|
||||
// yes this is spelled "marge" instead of "merge"
|
||||
static int __cdecl ac_io_kfca_set_status_marge_func(void *cb) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::KFCAModule::KFCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("KFCA", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::KFCAModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_button_led);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_watchdog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_set_status_marge_func);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
extern uint8_t KFCA_VOL_SOUND;
|
||||
extern uint8_t KFCA_VOL_HEADPHONE;
|
||||
extern uint8_t KFCA_VOL_EXTERNAL;
|
||||
extern uint8_t KFCA_VOL_WOOFER;
|
||||
|
||||
class KFCAModule : public ACIOModule {
|
||||
public:
|
||||
KFCAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
#include "klpa.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/loveplus/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
static uint8_t STATUS_BUFFER[48];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
static const size_t LOVEPLUS_LIGHTS_MAPPING[] = {
|
||||
games::loveplus::Lights::Red,
|
||||
games::loveplus::Lights::Green,
|
||||
games::loveplus::Lights::Blue,
|
||||
SIZE_MAX,
|
||||
games::loveplus::Lights::Right,
|
||||
games::loveplus::Lights::Left,
|
||||
};
|
||||
|
||||
static char __cdecl ac_io_klpa_consume_coinstock(int a1, DWORD *a2) {
|
||||
*a2 = (DWORD) eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_coin_blocker_close(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_coin_blocker_open(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_led_off(size_t index) {
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP") && index < std::size(LOVEPLUS_LIGHTS_MAPPING)) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
if (LOVEPLUS_LIGHTS_MAPPING[index] != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(LOVEPLUS_LIGHTS_MAPPING[index]), 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_led_on(size_t index) {
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP") && index < std::size(LOVEPLUS_LIGHTS_MAPPING)) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
if (LOVEPLUS_LIGHTS_MAPPING[index] != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(LOVEPLUS_LIGHTS_MAPPING[index]), 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_create_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_klpa_current_coinstock(int a1, DWORD *a2) {
|
||||
|
||||
// check bounds
|
||||
if (a1 < 0 || a1 >= 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*a2 = (DWORD) eamuse_coin_get_stock();
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_destroy_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void* __cdecl ac_io_klpa_get_control_status_buffer(void *a1) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(a1, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_klpa_get_io_command_mode(void *a1) {
|
||||
memset(a1, 0, 4);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_led_reset() {
|
||||
if (avs::game::is_model("KLP")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
for (const auto &mapping : LOVEPLUS_LIGHTS_MAPPING) {
|
||||
if (mapping != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping), 0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_lock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_io_command_mode(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_io_command_mode_is_finished(uint8_t *a1) {
|
||||
*a1 = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_set_led_bright(size_t index, uint8_t brightness) {
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP") && index < std::size(LOVEPLUS_LIGHTS_MAPPING)) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
if (LOVEPLUS_LIGHTS_MAPPING[index] != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(LOVEPLUS_LIGHTS_MAPPING[index]), brightness / 127.f);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_sound_mute(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_sound_mute_is_finished(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_klpa_unlock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// reset buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::loveplus::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Test)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Service)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Left)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Right)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
}
|
||||
|
||||
// x[9] & 0x3F) = volume output level?
|
||||
// x[11] & 0x3F) = volume output level?
|
||||
// x[12] |= (1 << 4) = headphone jack
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::KLPAModule::KLPAModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("KLPA", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::KLPAModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_led_off);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_led_on);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_get_io_command_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_led_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_io_command_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_io_command_mode_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_sound_mute);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_sound_mute_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_update_control_status_buffer);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class KLPAModule : public ACIOModule {
|
||||
public:
|
||||
KLPAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "la9a.h"
|
||||
|
||||
#include "games/pcm/io.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace acio {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct la9a_control_status {
|
||||
uint8_t p1 : 6;
|
||||
uint8_t service_button : 1;
|
||||
uint8_t test_button : 1;
|
||||
uint8_t p2[9];
|
||||
uint8_t lcd_counter;
|
||||
uint8_t p3[5];
|
||||
uint16_t touch_x;
|
||||
uint16_t touch_y;
|
||||
uint16_t touch_z;
|
||||
uint8_t p4[26];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static struct la9a_control_status CONTROL_STATUS {};
|
||||
static bool TOUCH_ATTACHED = false;
|
||||
|
||||
static bool __cdecl ac_io_la9a_set_error_message(int, unsigned int, int) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_la9a_update_control_status_buffer() {
|
||||
CONTROL_STATUS.touch_z = 0xFF;
|
||||
CONTROL_STATUS.test_button = 0;
|
||||
CONTROL_STATUS.service_button = 0;
|
||||
|
||||
// attach touch handler on the first call to this function
|
||||
if (!TOUCH_ATTACHED) {
|
||||
log_misc("la9a", "attach touch handler");
|
||||
|
||||
HWND hwnd = FindWindowBeginsWith("LA9");
|
||||
if (!hwnd) {
|
||||
log_fatal("la9a", "LA9 window not found");
|
||||
}
|
||||
touch_create_wnd(hwnd);
|
||||
graphics_hook_window(hwnd, nullptr);
|
||||
|
||||
if (GRAPHICS_SHOW_CURSOR) {
|
||||
ShowCursor(1);
|
||||
}
|
||||
|
||||
TOUCH_ATTACHED = true;
|
||||
}
|
||||
|
||||
// update touch
|
||||
std::vector<TouchPoint> touch_points;
|
||||
touch_get_points(touch_points);
|
||||
|
||||
if (!touch_points.empty()) {
|
||||
auto &touch_point = touch_points[0];
|
||||
|
||||
// TODO: `x` and `y` should be clamped [0, std::numeric_limits<uint16_t>::max())
|
||||
CONTROL_STATUS.touch_x = static_cast<uint16_t>(touch_point.x);
|
||||
CONTROL_STATUS.touch_y = static_cast<uint16_t>(touch_point.y);
|
||||
CONTROL_STATUS.touch_z = 0;
|
||||
}
|
||||
CONTROL_STATUS.lcd_counter++;
|
||||
|
||||
// update buttons
|
||||
auto &buttons = games::pcm::get_buttons();
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::pcm::Buttons::Test])) {
|
||||
CONTROL_STATUS.test_button = 1;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::pcm::Buttons::Service])) {
|
||||
CONTROL_STATUS.service_button = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_la9a_update_counter(int, int) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_la9a_update_lcd(int) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_la9a_get_control_status_buffer(struct la9a_control_status *control_status) {
|
||||
*control_status = CONTROL_STATUS;
|
||||
}
|
||||
|
||||
LA9AModule::LA9AModule(HMODULE module, HookMode hookMode) : ACIOModule("LA9A", module, hookMode) {
|
||||
//this->status_buffer = STATUS_BUFFER;
|
||||
//this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
//this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void LA9AModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_set_error_message);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_update_lcd);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_update_counter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class LA9AModule : public ACIOModule {
|
||||
public:
|
||||
LA9AModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
#include "mdxf.h"
|
||||
#include "mdxf_poll.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/ddr/ddr.h"
|
||||
#include "games/ddr/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include <mutex>
|
||||
|
||||
#define MDFX_DEBUG_VERBOSE 0
|
||||
|
||||
#if MDFX_DEBUG_VERBOSE
|
||||
#define log_debug(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
#else
|
||||
#define log_debug(module, format_str, ...)
|
||||
#endif
|
||||
|
||||
// constants
|
||||
const size_t STATUS_BUFFER_SIZE = 32;
|
||||
const size_t STATUS_BUFFER_NUM_ENTRIES = 16;
|
||||
|
||||
// static stuff
|
||||
static uint8_t HEAD_P1 = 0;
|
||||
static uint8_t HEAD_P2 = 0;
|
||||
|
||||
static uint16_t PREV_STATE_P1 = 0;
|
||||
static uint16_t PREV_STATE_P2 = 0;
|
||||
static uint64_t PREV_TIME_P1 = 0;
|
||||
static uint64_t PREV_TIME_P2 = 0;
|
||||
|
||||
static std::mutex MUTEX_P1;
|
||||
static std::mutex MUTEX_P2;
|
||||
|
||||
static bool IS_MDXF_ACTIVE = false;
|
||||
|
||||
static const uint8_t BACKFILL_INTERVAL_MS = 4;
|
||||
static const uint8_t BACKFILL_PADDING_MS = 2;
|
||||
|
||||
// These are used to determine if a thread needs to be spun up to keep pad state ring buffers populated with enough recent polls
|
||||
static uint64_t START_TIME = 0;
|
||||
static int CALL_COUNT = 0;
|
||||
static const int THRESHOLD_REFRESH_RATE = 120;
|
||||
|
||||
static std::atomic<bool> IS_REFRESH_RATE_MEASUREMENT_STARTED{false};
|
||||
static std::atomic<bool> IS_REFRESH_RATE_DETERMINED{false};
|
||||
static std::atomic<bool> IS_THREAD_NEEDED{false};
|
||||
static std::atomic<bool> MDXF_THREAD_RUNNING{false};
|
||||
|
||||
static std::thread MDXF_THREAD;
|
||||
|
||||
static constexpr int THREAD_REFRESH_RATE_HZ = 125;
|
||||
static constexpr auto THREAD_PERIOD = std::chrono::milliseconds(1000 / THREAD_REFRESH_RATE_HZ);
|
||||
|
||||
// buffers
|
||||
#pragma pack(push, 1)
|
||||
static struct {
|
||||
uint8_t STATUS_BUFFER_P1[STATUS_BUFFER_NUM_ENTRIES][STATUS_BUFFER_SIZE] {};
|
||||
uint8_t STATUS_BUFFER_P2[STATUS_BUFFER_NUM_ENTRIES][STATUS_BUFFER_SIZE] {};
|
||||
} BUFFERS {};
|
||||
#pragma pack(pop)
|
||||
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
// Decides which method to use for populating ring buffer entries for "padding".
|
||||
// Overwritten in spicecfg using P4IO Buffer Algorithm option.
|
||||
// THREAD_MODE: Spins a thread running at THREAD_REFRESH_RATE_HZ which periodically fills the ring
|
||||
// buffer with auxiliary entries. Falls back on BACKFILL_MODE
|
||||
// BACKFILL_MODE: On every update cycle, fill the ring buffer with entries for the last known state
|
||||
// BACKFILL_INTERVAL_MS apart from each other from the time of the last entry to the
|
||||
// current time before adding the entry for the current state.
|
||||
// AUTO_MODE: thread mode if <120Hz, backfill if >=120Hz
|
||||
acio::MDXFBufferFillMode acio::MDXF_BUFFER_FILL_MODE = acio::MDXFBufferFillMode::AUTO_MODE;
|
||||
|
||||
typedef enum {
|
||||
ARKMDXP4_POLL = 0,
|
||||
INTERNAL_POLL = 1,
|
||||
EXTERNAL_POLL = 2
|
||||
} MDXFPollSource;
|
||||
|
||||
typedef uint64_t (__cdecl *ARK_GET_TICK_TIME64_T)();
|
||||
|
||||
static uint64_t arkGetTickTime64() {
|
||||
static ARK_GET_TICK_TIME64_T getTickTime64 = nullptr;
|
||||
|
||||
if (!getTickTime64) {
|
||||
HMODULE h = avs::game::DLL_INSTANCE;
|
||||
if (h) {
|
||||
getTickTime64 = (ARK_GET_TICK_TIME64_T)GetProcAddress(h, "arkGetTickTime64");
|
||||
}
|
||||
}
|
||||
// this works on 32-bit versions of avs, but not on 64.
|
||||
// it's better than nothing though.
|
||||
return getTickTime64 ? getTickTime64() : timeGetTime();
|
||||
}
|
||||
|
||||
// Used to keep the ring buffer populated with steady updates. 60Hz interval is too slow
|
||||
static void mdxf_thread_start() {
|
||||
bool expected = false;
|
||||
if (!MDXF_THREAD_RUNNING.compare_exchange_strong(expected, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("mdxf", "starting poll thread");
|
||||
|
||||
MDXF_THREAD = std::thread([] {
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||
|
||||
while (MDXF_THREAD_RUNNING.load(std::memory_order_acquire)) {
|
||||
mdxf_poll(false);
|
||||
std::this_thread::sleep_for(THREAD_PERIOD);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void mdxf_thread_stop() {
|
||||
if (!MDXF_THREAD_RUNNING.exchange(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (MDXF_THREAD.joinable()) {
|
||||
MDXF_THREAD.join();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Snaps measured refresh rate to best fit
|
||||
static int snap_refresh_rate(int measured_hz) {
|
||||
static constexpr std::array<int, 6> rates = {
|
||||
60, 120, 144, 165, 180, 240
|
||||
};
|
||||
|
||||
int best = rates[0];
|
||||
int best_err = std::fabs(measured_hz - best);
|
||||
|
||||
for (int r : rates) {
|
||||
int err = std::fabs(measured_hz - r);
|
||||
if (err < best_err) {
|
||||
best = r;
|
||||
best_err = err;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
// Increments the number of times the update function was called,
|
||||
// then calculates the current refresh rate of the game
|
||||
// (20 seconds after the game starts, until 25 seconds)
|
||||
static void count_calls_from_game() {
|
||||
if (IS_REFRESH_RATE_DETERMINED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t current_time = arkGetTickTime64();
|
||||
|
||||
if (!IS_REFRESH_RATE_MEASUREMENT_STARTED) {
|
||||
if (START_TIME == 0) {
|
||||
START_TIME = current_time;
|
||||
}
|
||||
|
||||
// boot screen takes about 10 seconds, so let's wait for double that
|
||||
if ((current_time - START_TIME) < 20000) {
|
||||
// too early, do nothing
|
||||
return;
|
||||
} else {
|
||||
// 20s has passed for the first time, start measuring on next call
|
||||
IS_REFRESH_RATE_MEASUREMENT_STARTED = true;
|
||||
START_TIME = current_time;
|
||||
log_debug("mdxf", "measurement begin");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t elapsed_time = current_time - START_TIME;
|
||||
CALL_COUNT++;
|
||||
if (elapsed_time >= 5000) {
|
||||
double measured_hz = static_cast<double>(CALL_COUNT) * 1000.0 / static_cast<double>(elapsed_time);
|
||||
|
||||
// Account for the main loop calling this twice per iteration
|
||||
measured_hz *= 0.5;
|
||||
const int snapped_hz = snap_refresh_rate(static_cast<int>(measured_hz));
|
||||
|
||||
IS_REFRESH_RATE_DETERMINED = true;
|
||||
IS_THREAD_NEEDED = (snapped_hz < THRESHOLD_REFRESH_RATE);
|
||||
log_info(
|
||||
"mdxf",
|
||||
"detected: {} Hz, best fit: {} Hz",
|
||||
static_cast<int>(measured_hz),
|
||||
snapped_hz);
|
||||
|
||||
if (IS_THREAD_NEEDED) {
|
||||
mdxf_thread_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static uint64_t __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *out, uint8_t index, uint8_t head_in) {
|
||||
|
||||
// Default error value (matches original mask behavior)
|
||||
auto error_ret = static_cast<uint64_t>(node - 0x11) & 0xFFFFFFFFFFFFFF00;
|
||||
|
||||
// Dance Dance Revolution
|
||||
if (avs::game::is_model("MDX")) {
|
||||
// Select player-specific state
|
||||
std::mutex* mutex = nullptr;
|
||||
uint8_t* head = nullptr;
|
||||
uint8_t (*buffer)[STATUS_BUFFER_SIZE];
|
||||
size_t size = STATUS_BUFFER_NUM_ENTRIES;
|
||||
|
||||
if (node == 17 || node == 25) {
|
||||
mutex = &MUTEX_P1;
|
||||
head = &HEAD_P1;
|
||||
buffer = BUFFERS.STATUS_BUFFER_P1;
|
||||
} else if (node == 18 || node == 26) {
|
||||
mutex = &MUTEX_P2;
|
||||
head = &HEAD_P2;
|
||||
buffer = BUFFERS.STATUS_BUFFER_P2;
|
||||
} else {
|
||||
memset(out, 0, STATUS_BUFFER_SIZE);
|
||||
return error_ret;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
|
||||
const uint8_t start_index = (head_in == 0xFF) ? *head : head_in;
|
||||
|
||||
// Compute ring index: walk backwards from start_index as index increases
|
||||
// Assumes ring buffer size is a power of two
|
||||
const size_t mask = size - 1;
|
||||
const size_t offset = static_cast<size_t>(index) & mask;
|
||||
const size_t i = (static_cast<size_t>(start_index) - offset + size) & mask;
|
||||
|
||||
// Copy the chosen entry
|
||||
memcpy(out, buffer[i], STATUS_BUFFER_SIZE);
|
||||
|
||||
// Return the start value actually used
|
||||
return static_cast<uint64_t>(start_index);
|
||||
}
|
||||
|
||||
return error_ret;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2, uint8_t value) {
|
||||
if (avs::game::is_model("MDX")) {
|
||||
static const struct {
|
||||
int a2[4];
|
||||
} mapping[] = {
|
||||
{
|
||||
// a1 == 17
|
||||
{
|
||||
games::ddr::Lights::GOLD_P1_STAGE_UP_RIGHT,
|
||||
games::ddr::Lights::GOLD_P1_STAGE_DOWN_LEFT,
|
||||
games::ddr::Lights::GOLD_P1_STAGE_UP_LEFT,
|
||||
games::ddr::Lights::GOLD_P1_STAGE_DOWN_RIGHT
|
||||
}
|
||||
},
|
||||
{
|
||||
// a1 == 18
|
||||
{
|
||||
games::ddr::Lights::GOLD_P2_STAGE_UP_RIGHT,
|
||||
games::ddr::Lights::GOLD_P2_STAGE_DOWN_LEFT,
|
||||
games::ddr::Lights::GOLD_P2_STAGE_UP_LEFT,
|
||||
games::ddr::Lights::GOLD_P2_STAGE_DOWN_RIGHT
|
||||
}
|
||||
}
|
||||
};
|
||||
if ((a1 == 17 || a1 == 18) && (a2 < 4)) {
|
||||
// get light from mapping
|
||||
const auto light = mapping[a1 - 17].a2[a2];
|
||||
|
||||
// get lights
|
||||
auto &lights = games::ddr::get_lights();
|
||||
|
||||
// write lights
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[light], value / 128.f);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFPollSource source, uint64_t current_time) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dance Dance Revolution
|
||||
if (avs::game::is_model("MDX")) {
|
||||
// Marks this module as actively being used, allowing this function to be called from other sources
|
||||
if (source == ARKMDXP4_POLL) {
|
||||
if (!IS_MDXF_ACTIVE) {
|
||||
log_debug("mdxf", "initializing mdxf I/O support");
|
||||
IS_MDXF_ACTIVE = true;
|
||||
if (acio::MDXF_BUFFER_FILL_MODE == acio::MDXFBufferFillMode::THREAD_MODE) {
|
||||
IS_THREAD_NEEDED = true;
|
||||
mdxf_thread_start();
|
||||
}
|
||||
}
|
||||
if (acio::MDXF_BUFFER_FILL_MODE == acio::MDXFBufferFillMode::AUTO_MODE) {
|
||||
count_calls_from_game();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t (*buffer)[STATUS_BUFFER_SIZE];
|
||||
uint8_t *head = nullptr;
|
||||
uint16_t *prev_state = nullptr;
|
||||
uint64_t *prev_time = nullptr;
|
||||
std::mutex* mutex = nullptr;
|
||||
|
||||
switch (node) {
|
||||
case 17:
|
||||
case 25:
|
||||
mutex = &MUTEX_P1;
|
||||
head = &HEAD_P1;
|
||||
prev_state = &PREV_STATE_P1;
|
||||
prev_time = &PREV_TIME_P1;
|
||||
buffer = BUFFERS.STATUS_BUFFER_P1;
|
||||
break;
|
||||
case 18:
|
||||
case 26:
|
||||
mutex = &MUTEX_P2;
|
||||
head = &HEAD_P2;
|
||||
prev_state = &PREV_STATE_P2;
|
||||
prev_time = &PREV_TIME_P2;
|
||||
buffer = BUFFERS.STATUS_BUFFER_P2;
|
||||
break;
|
||||
default:
|
||||
// return failure on unknown node
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sensor Map (LDUR):
|
||||
// FOOT DOWN = bit 32-35 = byte 4, bit 0-3
|
||||
// FOOT UP = bit 36-39 = byte 4, bit 4-7
|
||||
// FOOT RIGHT = bit 40-43 = byte 5, bit 0-3
|
||||
// FOOT LEFT = bit 44-47 = byte 5, bit 4-7
|
||||
static const size_t buttons_p1[] = {
|
||||
games::ddr::Buttons::P1_PANEL_UP,
|
||||
games::ddr::Buttons::P1_PANEL_DOWN,
|
||||
games::ddr::Buttons::P1_PANEL_LEFT,
|
||||
games::ddr::Buttons::P1_PANEL_RIGHT,
|
||||
};
|
||||
static const size_t buttons_p2[] = {
|
||||
games::ddr::Buttons::P2_PANEL_UP,
|
||||
games::ddr::Buttons::P2_PANEL_DOWN,
|
||||
games::ddr::Buttons::P2_PANEL_LEFT,
|
||||
games::ddr::Buttons::P2_PANEL_RIGHT,
|
||||
};
|
||||
|
||||
// decide on button map
|
||||
const size_t *button_map = nullptr;
|
||||
int player = 0;
|
||||
switch (node) {
|
||||
case 17:
|
||||
case 25:
|
||||
button_map = &buttons_p1[0];
|
||||
player = 1;
|
||||
break;
|
||||
case 18:
|
||||
case 26:
|
||||
button_map = &buttons_p2[0];
|
||||
player = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t current_state;
|
||||
|
||||
// Only call getState() if called externally when actual input events happen, otherwise use previous known state
|
||||
if (source == EXTERNAL_POLL) {
|
||||
// get buttons
|
||||
auto &buttons = games::ddr::get_buttons();
|
||||
|
||||
// get analogs
|
||||
bool analog_left = false;
|
||||
bool analog_right = false;
|
||||
games::ddr::get_analog_x_axis(player, analog_left, analog_right);
|
||||
bool analog_up = false;
|
||||
bool analog_down = false;
|
||||
games::ddr::get_analog_y_axis(player, analog_up, analog_down);
|
||||
|
||||
uint8_t up_down = 0;
|
||||
uint8_t left_right = 0;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[0])) || analog_up) {
|
||||
up_down |= 0xF0;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[1])) || analog_down) {
|
||||
up_down |= 0x0F;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[2])) || analog_left) {
|
||||
left_right |= 0xF0;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[3])) || analog_right) {
|
||||
left_right |= 0x0F;
|
||||
}
|
||||
current_state = (uint16_t(up_down) << 8) | left_right;
|
||||
} else {
|
||||
current_state = *prev_state;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
|
||||
const bool has_state_changed = *prev_state != current_state;
|
||||
const bool has_time_changed = *prev_time < current_time;
|
||||
|
||||
// If state hasn't changed and either the update was triggered externally or the time hasn't changed, then don't advance head pointer or write a new entry
|
||||
if (!has_state_changed && (source == EXTERNAL_POLL || !has_time_changed)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The start and stop time cutoffs for backfilling entries. Min(..) ensures times aren't negative.
|
||||
// The stop time is just before the current_time, set by BACKFILL_PADDING_MS, which avoids the last backfilled entry being too close to current_time.
|
||||
uint64_t start_time = *prev_time;
|
||||
const uint64_t stop_time = current_time - std::min<uint64_t>(current_time, BACKFILL_PADDING_MS);
|
||||
|
||||
// Ensures the first iteration will write the first entry at current_time and not backfill to time 0ms.
|
||||
if (start_time == 0) {
|
||||
start_time = current_time - std::min<uint64_t>(current_time, BACKFILL_INTERVAL_MS);
|
||||
}
|
||||
|
||||
// Ensures only STATUS_BUFFER_NUM_ENTRIES entries at most are backfilled
|
||||
const uint64_t max_backfill = BACKFILL_INTERVAL_MS * STATUS_BUFFER_NUM_ENTRIES;
|
||||
const uint64_t min_time = current_time - std::min<uint64_t>(current_time, max_backfill);
|
||||
if (start_time < min_time) {
|
||||
start_time = min_time;
|
||||
}
|
||||
|
||||
// Don't backfill entries if called externally or if a separate thread is being used to fill auxiliary entries
|
||||
if (source == EXTERNAL_POLL || IS_THREAD_NEEDED) {
|
||||
start_time = stop_time - 1;
|
||||
}
|
||||
|
||||
uint64_t time = start_time;
|
||||
uint16_t state = *prev_state;
|
||||
|
||||
// Backfill entries a fixed interval apart from each other between prev_time and current_time
|
||||
while (time < stop_time) {
|
||||
// Advance head pointer
|
||||
*head = (*head + 1) % STATUS_BUFFER_NUM_ENTRIES;
|
||||
uint8_t* buffer_entry = buffer[*head];
|
||||
|
||||
// Clear buffer
|
||||
memset(buffer_entry, 0, STATUS_BUFFER_SIZE);
|
||||
|
||||
time += BACKFILL_INTERVAL_MS;
|
||||
|
||||
// If the stop time is reached, then write current_time and current_state instead for this final iteration
|
||||
const bool isEdge = (time >= stop_time);
|
||||
if (isEdge) {
|
||||
state = current_state;
|
||||
time = current_time;
|
||||
}
|
||||
|
||||
// Write button state
|
||||
buffer_entry[4] = (state >> 8) & 0xFF;
|
||||
buffer_entry[5] = state & 0xFF;
|
||||
|
||||
// Write game time
|
||||
*(uint64_t*)&buffer_entry[0x18] = time;
|
||||
}
|
||||
|
||||
*prev_state = current_state;
|
||||
*prev_time = current_time;
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
|
||||
return ac_io_mdxf_update_control_status_buffer_impl(node, ARKMDXP4_POLL, arkGetTickTime64());
|
||||
}
|
||||
|
||||
// Used for triggering updates of the controller states from outside arkmdxp4.dll main refresh loop (i.e. within rawinput.cpp on controller events)
|
||||
void mdxf_poll(bool isExternal) {
|
||||
if (IS_MDXF_ACTIVE) {
|
||||
const MDXFPollSource source = isExternal ? EXTERNAL_POLL : INTERNAL_POLL;
|
||||
const uint64_t call_time_ms = arkGetTickTime64();
|
||||
ac_io_mdxf_update_control_status_buffer_impl(17, source, call_time_ms);
|
||||
ac_io_mdxf_update_control_status_buffer_impl(18, source, call_time_ms);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::MDXFModule::MDXFModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("MDXF", module, hookMode) {
|
||||
this->status_buffer = (uint8_t*) &BUFFERS;
|
||||
this->status_buffer_size = sizeof(BUFFERS);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::MDXFModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_set_output_level);
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_update_control_status_buffer);
|
||||
}
|
||||
|
||||
acio::MDXFModule::~MDXFModule() {
|
||||
if (IS_THREAD_NEEDED) {
|
||||
mdxf_thread_stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
enum class MDXFBufferFillMode {
|
||||
// backfill mode, but if <120Hz, enable poll thread
|
||||
AUTO_MODE,
|
||||
|
||||
// forces poll thread
|
||||
THREAD_MODE,
|
||||
|
||||
// forces backfill mode (no poll thread)
|
||||
BACKFILL_MODE
|
||||
};
|
||||
|
||||
extern MDXFBufferFillMode MDXF_BUFFER_FILL_MODE;
|
||||
|
||||
class MDXFModule : public ACIOModule {
|
||||
public:
|
||||
MDXFModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
~MDXFModule() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// mdxf_poll.h
|
||||
#pragma once
|
||||
|
||||
// Called from rawinput thread whenever inputs have just been updated
|
||||
void mdxf_poll(bool isExternal);
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "module.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
const char *acio::hook_mode_str(acio::HookMode hook_mode) {
|
||||
switch (hook_mode) {
|
||||
case HookMode::INLINE:
|
||||
return "Inline";
|
||||
case HookMode::IAT:
|
||||
return "IAT";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hook functions depending on the specified mode.
|
||||
* We don't care about errors here since different versions of libacio contain different feature sets,
|
||||
* which means that not all hooks must/can succeed.
|
||||
*/
|
||||
void acio::ACIOModule::hook(void *func, const char *func_name) {
|
||||
switch (this->hook_mode) {
|
||||
case HookMode::INLINE:
|
||||
detour::inline_hook(func, libutils::try_proc(this->module, func_name));
|
||||
break;
|
||||
case HookMode::IAT:
|
||||
detour::iat_try(func_name, func);
|
||||
break;
|
||||
default:
|
||||
log_warning("acio", "unable to hook using mode {}", hook_mode_str(this->hook_mode));
|
||||
}
|
||||
}
|
||||
|
||||
void acio::ACIOModule::attach() {
|
||||
log_info("acio", "module attach: {} {}", this->name, hook_mode_str(this->hook_mode));
|
||||
this->attached = true;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
// macro for lazy typing of hooks
|
||||
#define ACIO_MODULE_HOOK(f) this->hook(reinterpret_cast<void *>(f), #f)
|
||||
|
||||
namespace acio {
|
||||
|
||||
/*
|
||||
* Hook Modes
|
||||
* Since some versions can't handle inline hooking
|
||||
*/
|
||||
enum class HookMode {
|
||||
INLINE,
|
||||
IAT
|
||||
};
|
||||
|
||||
// this makes logging easier
|
||||
const char *hook_mode_str(HookMode hook_mode);
|
||||
|
||||
/*
|
||||
* The ACIO module itself
|
||||
* Inherit this for extending our libacio implementation
|
||||
*/
|
||||
class ACIOModule {
|
||||
protected:
|
||||
|
||||
// the magic
|
||||
void hook(void* func, const char *func_name);
|
||||
|
||||
public:
|
||||
|
||||
ACIOModule(std::string name, HMODULE module, HookMode hook_mode) :
|
||||
name(std::move(name)),
|
||||
module(module),
|
||||
hook_mode(hook_mode) {};
|
||||
|
||||
virtual ~ACIOModule() = default;
|
||||
|
||||
virtual void attach();
|
||||
|
||||
// settings
|
||||
std::string name;
|
||||
HMODULE module;
|
||||
HookMode hook_mode;
|
||||
bool attached = false;
|
||||
|
||||
// buffer state (optional)
|
||||
uint8_t *status_buffer = nullptr;
|
||||
size_t status_buffer_size = 0;
|
||||
bool *status_buffer_freeze = nullptr;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "nddb.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[4] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static void __cdecl ac_io_nddb_control_pwm(int a1, int a2) {
|
||||
log_misc("acio::nddb", "ac_io_nddb_control_pwm({}, {})", a1, a2);
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_nddb_control_solenoide(int a1, int a2) {
|
||||
log_misc("acio::nddb", "ac_io_nddb_control_solenoide({}, {})", a1, a2);
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_destroy_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_nddb_get_control_status_buffer(void *buffer) {
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_req_solenoide_control(uint8_t *buffer) {
|
||||
log_misc("acio::nddb", "ac_io_nddb_req_solenoide_control");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_update_control_status_buffer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::NDDBModule::NDDBModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("NDDB", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::NDDBModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_control_pwm);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_control_solenoide);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_req_solenoide_control);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_update_control_status_buffer);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class NDDBModule : public ACIOModule {
|
||||
public:
|
||||
NDDBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
#include "panb.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/nost/io.h"
|
||||
#include "games/nost/nost.h"
|
||||
#include "util/logging.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
// std::min
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[277];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static long __cdecl ac_io_panb_control_led_bright(size_t index, uint8_t value) {
|
||||
|
||||
// nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::nost::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
games::nost::Lights::Key1R, games::nost::Lights::Key1G, games::nost::Lights::Key1B,
|
||||
games::nost::Lights::Key2R, games::nost::Lights::Key2G, games::nost::Lights::Key2B,
|
||||
games::nost::Lights::Key3R, games::nost::Lights::Key3G, games::nost::Lights::Key3B,
|
||||
games::nost::Lights::Key4R, games::nost::Lights::Key4G, games::nost::Lights::Key4B,
|
||||
games::nost::Lights::Key5R, games::nost::Lights::Key5G, games::nost::Lights::Key5B,
|
||||
games::nost::Lights::Key6R, games::nost::Lights::Key6G, games::nost::Lights::Key6B,
|
||||
games::nost::Lights::Key7R, games::nost::Lights::Key7G, games::nost::Lights::Key7B,
|
||||
games::nost::Lights::Key8R, games::nost::Lights::Key8G, games::nost::Lights::Key8B,
|
||||
games::nost::Lights::Key9R, games::nost::Lights::Key9G, games::nost::Lights::Key9B,
|
||||
games::nost::Lights::Key10R, games::nost::Lights::Key10G, games::nost::Lights::Key10B,
|
||||
games::nost::Lights::Key11R, games::nost::Lights::Key11G, games::nost::Lights::Key11B,
|
||||
games::nost::Lights::Key12R, games::nost::Lights::Key12G, games::nost::Lights::Key12B,
|
||||
games::nost::Lights::Key13R, games::nost::Lights::Key13G, games::nost::Lights::Key13B,
|
||||
games::nost::Lights::Key14R, games::nost::Lights::Key14G, games::nost::Lights::Key14B,
|
||||
games::nost::Lights::Key15R, games::nost::Lights::Key15G, games::nost::Lights::Key15B,
|
||||
games::nost::Lights::Key16R, games::nost::Lights::Key16G, games::nost::Lights::Key16B,
|
||||
games::nost::Lights::Key17R, games::nost::Lights::Key17G, games::nost::Lights::Key17B,
|
||||
games::nost::Lights::Key18R, games::nost::Lights::Key18G, games::nost::Lights::Key18B,
|
||||
games::nost::Lights::Key19R, games::nost::Lights::Key19G, games::nost::Lights::Key19B,
|
||||
games::nost::Lights::Key20R, games::nost::Lights::Key20G, games::nost::Lights::Key20B,
|
||||
games::nost::Lights::Key21R, games::nost::Lights::Key21G, games::nost::Lights::Key21B,
|
||||
games::nost::Lights::Key22R, games::nost::Lights::Key22G, games::nost::Lights::Key22B,
|
||||
games::nost::Lights::Key23R, games::nost::Lights::Key23G, games::nost::Lights::Key23B,
|
||||
games::nost::Lights::Key24R, games::nost::Lights::Key24G, games::nost::Lights::Key24B,
|
||||
games::nost::Lights::Key25R, games::nost::Lights::Key25G, games::nost::Lights::Key25B,
|
||||
games::nost::Lights::Key26R, games::nost::Lights::Key26G, games::nost::Lights::Key26B,
|
||||
games::nost::Lights::Key27R, games::nost::Lights::Key27G, games::nost::Lights::Key27B,
|
||||
games::nost::Lights::Key28R, games::nost::Lights::Key28G, games::nost::Lights::Key28B,
|
||||
};
|
||||
|
||||
// write light
|
||||
if (index < std::size(mapping)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[index]), value / 127.f);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_panb_control_reset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void* __cdecl ac_io_panb_get_control_status_buffer(uint8_t* buffer) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_panb_start_auto_input() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t panb_get_button_velocity(Button& button, Button& button_soft, Button& button_medium, Button& button_hard) {
|
||||
const auto velocity = Buttons::getVelocity(RI_MGR, button);
|
||||
const auto velocity_soft = Buttons::getVelocity(RI_MGR, button_soft);
|
||||
const auto velocity_medium = Buttons::getVelocity(RI_MGR, button_medium);
|
||||
const auto velocity_hard = Buttons::getVelocity(RI_MGR, button_hard);
|
||||
|
||||
// note that the digital values have been obtained via trial-and-error in recital mode
|
||||
// based on Op3:
|
||||
// * soft presses glow blue, should trigger Elegant in blue sections
|
||||
// * hard presses glow red/orange, should trigger Elegant in yellow sections
|
||||
// * default (medium) presses glow green, triggers Elegant in both blue and yellow sections
|
||||
|
||||
// digital-only values
|
||||
if (velocity_hard > 0.f) {
|
||||
// do NOT use 15 here!! 14 properly registers as a hard press, but 15 does not
|
||||
return 14;
|
||||
} else if (velocity_medium > 0.f) {
|
||||
return 11;
|
||||
} else if (velocity_soft > 0.f) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// digital or midi (velocity-sensitive) values
|
||||
return std::min((uint8_t)(velocity * 15.999f), (uint8_t)14);
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_panb_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, 277);
|
||||
|
||||
/*
|
||||
* first byte is number of input data
|
||||
* when it's set to 0 the game will not update it's key states
|
||||
* setting it too high will make the game read over the buffer
|
||||
*
|
||||
* unsure why you would send more than one set of data, so
|
||||
* we just set it to 1 and provide our current status
|
||||
*/
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
// Nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get buttons/analogs
|
||||
auto &buttons = games::nost::get_buttons();
|
||||
auto &analogs = games::nost::get_analogs();
|
||||
|
||||
// mappings
|
||||
// "normal" buttons - these are velocity sensitive (digital or MIDI)
|
||||
static const size_t button_mapping[] = {
|
||||
games::nost::Buttons::Key1, games::nost::Buttons::Key2,
|
||||
games::nost::Buttons::Key3, games::nost::Buttons::Key4,
|
||||
games::nost::Buttons::Key5, games::nost::Buttons::Key6,
|
||||
games::nost::Buttons::Key7, games::nost::Buttons::Key8,
|
||||
games::nost::Buttons::Key9, games::nost::Buttons::Key10,
|
||||
games::nost::Buttons::Key11, games::nost::Buttons::Key12,
|
||||
games::nost::Buttons::Key13, games::nost::Buttons::Key14,
|
||||
games::nost::Buttons::Key15, games::nost::Buttons::Key16,
|
||||
games::nost::Buttons::Key17, games::nost::Buttons::Key18,
|
||||
games::nost::Buttons::Key19, games::nost::Buttons::Key20,
|
||||
games::nost::Buttons::Key21, games::nost::Buttons::Key22,
|
||||
games::nost::Buttons::Key23, games::nost::Buttons::Key24,
|
||||
games::nost::Buttons::Key25, games::nost::Buttons::Key26,
|
||||
games::nost::Buttons::Key27, games::nost::Buttons::Key28,
|
||||
};
|
||||
|
||||
// soft (digital) button - always registers as soft press
|
||||
static const size_t soft_button_mapping[] = {
|
||||
games::nost::Buttons::Key1Soft, games::nost::Buttons::Key2Soft,
|
||||
games::nost::Buttons::Key3Soft, games::nost::Buttons::Key4Soft,
|
||||
games::nost::Buttons::Key5Soft, games::nost::Buttons::Key6Soft,
|
||||
games::nost::Buttons::Key7Soft, games::nost::Buttons::Key8Soft,
|
||||
games::nost::Buttons::Key9Soft, games::nost::Buttons::Key10Soft,
|
||||
games::nost::Buttons::Key11Soft, games::nost::Buttons::Key12Soft,
|
||||
games::nost::Buttons::Key13Soft, games::nost::Buttons::Key14Soft,
|
||||
games::nost::Buttons::Key15Soft, games::nost::Buttons::Key16Soft,
|
||||
games::nost::Buttons::Key17Soft, games::nost::Buttons::Key18Soft,
|
||||
games::nost::Buttons::Key19Soft, games::nost::Buttons::Key20Soft,
|
||||
games::nost::Buttons::Key21Soft, games::nost::Buttons::Key22Soft,
|
||||
games::nost::Buttons::Key23Soft, games::nost::Buttons::Key24Soft,
|
||||
games::nost::Buttons::Key25Soft, games::nost::Buttons::Key26Soft,
|
||||
games::nost::Buttons::Key27Soft, games::nost::Buttons::Key28Soft,
|
||||
};
|
||||
|
||||
// medium (digital) button - always registers as medium press
|
||||
static const size_t medium_button_mapping[] = {
|
||||
games::nost::Buttons::Key1Medium, games::nost::Buttons::Key2Medium,
|
||||
games::nost::Buttons::Key3Medium, games::nost::Buttons::Key4Medium,
|
||||
games::nost::Buttons::Key5Medium, games::nost::Buttons::Key6Medium,
|
||||
games::nost::Buttons::Key7Medium, games::nost::Buttons::Key8Medium,
|
||||
games::nost::Buttons::Key9Medium, games::nost::Buttons::Key10Medium,
|
||||
games::nost::Buttons::Key11Medium, games::nost::Buttons::Key12Medium,
|
||||
games::nost::Buttons::Key13Medium, games::nost::Buttons::Key14Medium,
|
||||
games::nost::Buttons::Key15Medium, games::nost::Buttons::Key16Medium,
|
||||
games::nost::Buttons::Key17Medium, games::nost::Buttons::Key18Medium,
|
||||
games::nost::Buttons::Key19Medium, games::nost::Buttons::Key20Medium,
|
||||
games::nost::Buttons::Key21Medium, games::nost::Buttons::Key22Medium,
|
||||
games::nost::Buttons::Key23Medium, games::nost::Buttons::Key24Medium,
|
||||
games::nost::Buttons::Key25Medium, games::nost::Buttons::Key26Medium,
|
||||
games::nost::Buttons::Key27Medium, games::nost::Buttons::Key28Medium,
|
||||
};
|
||||
|
||||
// hard (digital) button - always registers as hard press
|
||||
static const size_t hard_button_mapping[] = {
|
||||
games::nost::Buttons::Key1Hard, games::nost::Buttons::Key2Hard,
|
||||
games::nost::Buttons::Key3Hard, games::nost::Buttons::Key4Hard,
|
||||
games::nost::Buttons::Key5Hard, games::nost::Buttons::Key6Hard,
|
||||
games::nost::Buttons::Key7Hard, games::nost::Buttons::Key8Hard,
|
||||
games::nost::Buttons::Key9Hard, games::nost::Buttons::Key10Hard,
|
||||
games::nost::Buttons::Key11Hard, games::nost::Buttons::Key12Hard,
|
||||
games::nost::Buttons::Key13Hard, games::nost::Buttons::Key14Hard,
|
||||
games::nost::Buttons::Key15Hard, games::nost::Buttons::Key16Hard,
|
||||
games::nost::Buttons::Key17Hard, games::nost::Buttons::Key18Hard,
|
||||
games::nost::Buttons::Key19Hard, games::nost::Buttons::Key20Hard,
|
||||
games::nost::Buttons::Key21Hard, games::nost::Buttons::Key22Hard,
|
||||
games::nost::Buttons::Key23Hard, games::nost::Buttons::Key24Hard,
|
||||
games::nost::Buttons::Key25Hard, games::nost::Buttons::Key26Hard,
|
||||
games::nost::Buttons::Key27Hard, games::nost::Buttons::Key28Hard,
|
||||
};
|
||||
static const size_t analog_mapping[] = {
|
||||
games::nost::Analogs::Key1, games::nost::Analogs::Key2,
|
||||
games::nost::Analogs::Key3, games::nost::Analogs::Key4,
|
||||
games::nost::Analogs::Key5, games::nost::Analogs::Key6,
|
||||
games::nost::Analogs::Key7, games::nost::Analogs::Key8,
|
||||
games::nost::Analogs::Key9, games::nost::Analogs::Key10,
|
||||
games::nost::Analogs::Key11, games::nost::Analogs::Key12,
|
||||
games::nost::Analogs::Key13, games::nost::Analogs::Key14,
|
||||
games::nost::Analogs::Key15, games::nost::Analogs::Key16,
|
||||
games::nost::Analogs::Key17, games::nost::Analogs::Key18,
|
||||
games::nost::Analogs::Key19, games::nost::Analogs::Key20,
|
||||
games::nost::Analogs::Key21, games::nost::Analogs::Key22,
|
||||
games::nost::Analogs::Key23, games::nost::Analogs::Key24,
|
||||
games::nost::Analogs::Key25, games::nost::Analogs::Key26,
|
||||
games::nost::Analogs::Key27, games::nost::Analogs::Key28,
|
||||
};
|
||||
|
||||
// iterate pairs of keys
|
||||
for (size_t key_pair = 0; key_pair < 28 / 2; key_pair++) {
|
||||
|
||||
// default states
|
||||
uint8_t state0 = 0;
|
||||
uint8_t state1 = 0;
|
||||
|
||||
// check analogs
|
||||
//
|
||||
// while 15 is technically allowed by the I/O board & is recognized correctly in test
|
||||
// menu, when you play recital mode, 15 fails to register Elegant in yellow sections.
|
||||
// therefore, cap the value at 14. tested with Nostroller.
|
||||
auto &analog0 = analogs.at(analog_mapping[key_pair * 2 + 0]);
|
||||
auto &analog1 = analogs.at(analog_mapping[key_pair * 2 + 1]);
|
||||
if (analog0.isSet()) {
|
||||
state0 = std::min((uint8_t)(Analogs::getState(RI_MGR, analog0) * 15.999f), (uint8_t)14);
|
||||
}
|
||||
if (analog1.isSet()) {
|
||||
state1 = std::min((uint8_t)(Analogs::getState(RI_MGR, analog1) * 15.999f), (uint8_t)14);
|
||||
}
|
||||
|
||||
// check digital buttons
|
||||
const auto button0 = panb_get_button_velocity(
|
||||
buttons.at(button_mapping[key_pair * 2 + 0]),
|
||||
buttons.at(soft_button_mapping[key_pair * 2 + 0]),
|
||||
buttons.at(medium_button_mapping[key_pair * 2 + 0]),
|
||||
buttons.at(hard_button_mapping[key_pair * 2 + 0])
|
||||
);
|
||||
if (button0 > 0) {
|
||||
state0 = button0;
|
||||
}
|
||||
|
||||
const auto button1 = panb_get_button_velocity(
|
||||
buttons.at(button_mapping[key_pair * 2 + 1]),
|
||||
buttons.at(soft_button_mapping[key_pair * 2 + 1]),
|
||||
buttons.at(medium_button_mapping[key_pair * 2 + 1]),
|
||||
buttons.at(hard_button_mapping[key_pair * 2 + 1])
|
||||
);
|
||||
if (button1 > 0) {
|
||||
state1 = button1;
|
||||
}
|
||||
|
||||
// build value
|
||||
uint8_t value = 0;
|
||||
value |= state0 << 4;
|
||||
value |= state1 & 0xF;
|
||||
|
||||
// set value
|
||||
STATUS_BUFFER[key_pair + 3] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::PANBModule::PANBModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PANB", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PANBModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_panb_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_control_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_start_auto_input);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_update_control_status_buffer);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PANBModule : public ACIOModule {
|
||||
public:
|
||||
PANBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,520 @@
|
||||
#include "pix.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/museca/io.h"
|
||||
#include "games/bbc/io.h"
|
||||
#include "util/utils.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static int ACIO_PIX_WARMUP = 0;
|
||||
static uint8_t STATUS_BUFFER[60];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static char __cdecl ac_io_pix_begin(char a1, long long a2, int a3, int a4, int a5, int a6) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_begin_get_status(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_end(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_end_get_status(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_get_firmware_update_device_index(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_get_node_no(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_get_recv_log(long long a1, void *a2, int a3) {
|
||||
return a2;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_get_rs232c_status(void *a1, int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_get_send_log(long long a1, void *a2, int a3) {
|
||||
return a2;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_get_version(void *a1, int a2, int a3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *__cdecl ac_io_pix_get_version_string() {
|
||||
static const char *version = "1.25.0";
|
||||
return version;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_go_firmware_update(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_is_active(int a1, int a2) {
|
||||
return (char) (++ACIO_PIX_WARMUP > 601 ? 1 : 0);
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_is_active2(int a1, int *a2, int a3) {
|
||||
ACIO_PIX_WARMUP = 601;
|
||||
*a2 = 6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_is_active_device(int a1, int a2) {
|
||||
return (char) (a1 != 5);
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_reset(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_change_expand_mode(char a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_control_led_bright(uint32_t led_field, uint8_t brightness) {
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::museca::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::museca::Lights::Spinner1R,
|
||||
games::museca::Lights::Spinner1G,
|
||||
games::museca::Lights::Spinner1B,
|
||||
games::museca::Lights::Spinner2R,
|
||||
games::museca::Lights::Spinner2G,
|
||||
games::museca::Lights::Spinner2B,
|
||||
games::museca::Lights::Spinner3R,
|
||||
games::museca::Lights::Spinner3G,
|
||||
games::museca::Lights::Spinner3B,
|
||||
games::museca::Lights::Spinner4R,
|
||||
games::museca::Lights::Spinner4G,
|
||||
games::museca::Lights::Spinner4B,
|
||||
games::museca::Lights::Spinner5R,
|
||||
games::museca::Lights::Spinner5G,
|
||||
games::museca::Lights::Spinner5B,
|
||||
games::museca::Lights::TitleR,
|
||||
games::museca::Lights::TitleG,
|
||||
games::museca::Lights::TitleB
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::bbc::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::bbc::Lights::P1_DISC_R,
|
||||
games::bbc::Lights::P1_DISC_G,
|
||||
games::bbc::Lights::P1_DISC_B,
|
||||
games::bbc::Lights::P3_DISC_R,
|
||||
games::bbc::Lights::P3_DISC_G,
|
||||
games::bbc::Lights::P3_DISC_B,
|
||||
games::bbc::Lights::P2_DISC_R,
|
||||
games::bbc::Lights::P2_DISC_G,
|
||||
games::bbc::Lights::P2_DISC_B,
|
||||
games::bbc::Lights::P4_DISC_R,
|
||||
games::bbc::Lights::P4_DISC_G,
|
||||
games::bbc::Lights::P4_DISC_B,
|
||||
games::bbc::Lights::P1_R,
|
||||
games::bbc::Lights::P1_B,
|
||||
-1, -1, -1, -1, -1, -1,
|
||||
games::bbc::Lights::P2_R,
|
||||
games::bbc::Lights::P2_B,
|
||||
games::bbc::Lights::P3_R,
|
||||
games::bbc::Lights::P3_B,
|
||||
games::bbc::Lights::P4_R,
|
||||
games::bbc::Lights::P4_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 255.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_control_reset() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_destroy_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_rvol_get_control_status_buffer(void *a1) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(a1, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_get_watchdog_status() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_pix_rvol_get_watchdog_time_min() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_pix_rvol_get_watchdog_time_now() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_modify_auto_input_get(long long a1, long long a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_rvol_req_get_control_status(DWORD *a1) {
|
||||
*a1 = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_req_volume_control(char a1, char a2, char a3, char a4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_req_volume_control_isfinished(DWORD *a1) {
|
||||
*a1 = 5;
|
||||
return true;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_set_framing_err_packet_send_interval(long long a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get input
|
||||
auto &buttons = games::museca::get_buttons();
|
||||
|
||||
// get slowdown status
|
||||
bool slowdown = Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::AnalogSlowdown));
|
||||
|
||||
// update disk buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk1Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 107);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk2Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 104);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk3Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 123);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk4Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 42);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk5Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 44);
|
||||
|
||||
// foot pedal (inverted)
|
||||
if (!Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::FootPedal)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 43);
|
||||
|
||||
// update analogs
|
||||
static uint8_t analogs[5] = { 0, 0, 0, 0, 0 };
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk1Minus))) {
|
||||
analogs[0] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk1Plus))) {
|
||||
analogs[0] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk2Minus))) {
|
||||
analogs[1] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk2Plus))) {
|
||||
analogs[1] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk3Minus))) {
|
||||
analogs[2] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk3Plus))) {
|
||||
analogs[2] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk4Minus))) {
|
||||
analogs[3] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk4Plus))) {
|
||||
analogs[3] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk5Minus))) {
|
||||
analogs[4] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk5Plus))) {
|
||||
analogs[4] += slowdown ? 3 : 12;
|
||||
}
|
||||
|
||||
// raw input analogs
|
||||
auto &analog_list = games::museca::get_analogs();
|
||||
size_t analog_mapping[] = {
|
||||
games::museca::Analogs::Disk1,
|
||||
games::museca::Analogs::Disk2,
|
||||
games::museca::Analogs::Disk3,
|
||||
games::museca::Analogs::Disk4,
|
||||
games::museca::Analogs::Disk5,
|
||||
};
|
||||
uint8_t set_values[5];
|
||||
std::copy(std::begin(analogs), std::end(analogs), std::begin(set_values));
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
auto &analog_item = analog_list.at(analog_mapping[i]);
|
||||
if (analog_item.isSet()) {
|
||||
set_values[i] = analogs[i] + (uint8_t) (Analogs::getState(RI_MGR, analog_item) * 255.99f);
|
||||
}
|
||||
}
|
||||
|
||||
// set analogs
|
||||
for (int i = 0; i < 5; i++)
|
||||
STATUS_BUFFER[20 + i] = set_values[i];
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get input
|
||||
auto &buttons = games::bbc::get_buttons();
|
||||
auto &analogs = games::bbc::get_analogs();
|
||||
|
||||
// get slowdown status
|
||||
bool slowdown1 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_DiskSlowdown));
|
||||
bool slowdown2 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_DiskSlowdown));
|
||||
bool slowdown3 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_DiskSlowdown));
|
||||
bool slowdown4 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_DiskSlowdown));
|
||||
|
||||
// update buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_R)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 44);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 107);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_B)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 41);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_R)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 39);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 123);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_B)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 55);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_R)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 71);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 104);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_B)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 87);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_R)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 103);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 42);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_B)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 119);
|
||||
}
|
||||
|
||||
// update analogs
|
||||
static uint8_t analog_states[4] = { 0, 0, 0, 0 };
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[0] -= slowdown1 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[0] += slowdown1 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[1] -= slowdown2 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[1] += slowdown2 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[2] -= slowdown3 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[2] += slowdown3 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[3] -= slowdown4 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[3] += slowdown4 ? 3 : 12;
|
||||
}
|
||||
|
||||
// raw input analogs
|
||||
uint8_t set_values[4];
|
||||
size_t analog_mappings[] = {
|
||||
games::bbc::Analogs::P1_Disk,
|
||||
games::bbc::Analogs::P2_Disk,
|
||||
games::bbc::Analogs::P3_Disk,
|
||||
games::bbc::Analogs::P4_Disk,
|
||||
};
|
||||
std::copy(std::begin(analog_states), std::end(analog_states), std::begin(set_values));
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
auto &analog_item = analogs.at(analog_mappings[i]);
|
||||
if (analog_item.isSet()) {
|
||||
set_values[i] = analog_states[i] + (uint8_t) (Analogs::getState(RI_MGR, analog_item) * 255.99f);
|
||||
}
|
||||
}
|
||||
|
||||
// flip disk 2/3
|
||||
set_values[1] ^= set_values[2];
|
||||
set_values[2] ^= set_values[1];
|
||||
set_values[1] ^= set_values[2];
|
||||
|
||||
// set analogs
|
||||
for (int i = 0; i < 4; i++) {
|
||||
STATUS_BUFFER[20 + i] = set_values[i];
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_pix_rvol_watchdog_off() {
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_secplug_set_encodedpasswd(void *a1, unsigned int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_set_get_status_device(void *a1, int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_set_soft_watch_dog(void *a1, int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_soft_watch_dog_off(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_soft_watch_dog_on(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_update(long long a1) {
|
||||
|
||||
// flush outputs
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char* __cdecl ac_io_pix_version() {
|
||||
static const char *version = "Version: 1.25.0\nBuild Date: Sep 20 2016 15:16:13\nBuild Host: DEMETER\n";
|
||||
return version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::PIXModule::PIXModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PIX", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PIXModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_pix_begin);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_begin_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_end);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_end_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_firmware_update_device_index);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_node_no);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_recv_log);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_rs232c_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_send_log);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_version);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_version_string);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_go_firmware_update);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_is_active);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_is_active2);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_is_active_device);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_change_expand_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_control_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_modify_auto_input_get);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_req_get_control_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_req_volume_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_set_framing_err_packet_send_interval);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_watchdog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_secplug_set_encodedpasswd);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_set_get_status_device);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_set_soft_watch_dog);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_soft_watch_dog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_soft_watch_dog_on);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_update);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_version);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PIXModule : public ACIOModule {
|
||||
public:
|
||||
PIXModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "pjec.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "avs/game.h"
|
||||
#include "games/we/io.h"
|
||||
|
||||
//using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[72];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_pjec_get_ps2() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_pjec_get_control_status_buffer(uint8_t *buffer) {
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjec_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// Winning Eleven
|
||||
if (avs::game::is_model({ "KCK", "NCK" })) {
|
||||
auto &buttons = games::we::get_buttons();
|
||||
auto &analogs = games::we::get_analogs();
|
||||
|
||||
/*
|
||||
* Device Types
|
||||
* 0x00 - Unknown Device
|
||||
* 0x01 - Mouse
|
||||
* 0x02 - Rotate Controller
|
||||
* 0x03 - Gun Controller K
|
||||
* 0x04 - Digital Controller <- Accepted
|
||||
* 0x05 - Analog Joystick
|
||||
* 0x06 - Gun Controller N
|
||||
* 0x07 - Analog Controller <- Accepted
|
||||
* 0x08 - USB Analog Controller
|
||||
*/
|
||||
|
||||
// set device type
|
||||
STATUS_BUFFER[0] = 0x07;
|
||||
|
||||
// set device present
|
||||
STATUS_BUFFER[2] = 0x5A;
|
||||
|
||||
// reset analogs to center
|
||||
STATUS_BUFFER[8] = 0x7F;
|
||||
STATUS_BUFFER[9] = 0x7F;
|
||||
STATUS_BUFFER[10] = 0x7F;
|
||||
STATUS_BUFFER[11] = 0x7F;
|
||||
|
||||
// apply analogs
|
||||
if (analogs[games::we::Analogs::PadStickLeftX].isSet()) {
|
||||
STATUS_BUFFER[8] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickLeftX]) * 255.9999f);
|
||||
}
|
||||
if (analogs[games::we::Analogs::PadStickLeftY].isSet()) {
|
||||
STATUS_BUFFER[9] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickLeftY]) * 255.9999f);
|
||||
}
|
||||
if (analogs[games::we::Analogs::PadStickRightX].isSet()) {
|
||||
STATUS_BUFFER[10] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickRightX]) * 255.9999f);
|
||||
}
|
||||
if (analogs[games::we::Analogs::PadStickRightY].isSet()) {
|
||||
STATUS_BUFFER[11] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickRightY]) * 255.9999f);
|
||||
}
|
||||
|
||||
// apply buttons
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadStart])) {
|
||||
STATUS_BUFFER[4] |= 0x08;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadSelect])) {
|
||||
STATUS_BUFFER[4] |= 0x01;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadUp])) {
|
||||
STATUS_BUFFER[4] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadDown])) {
|
||||
STATUS_BUFFER[4] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadLeft])) {
|
||||
STATUS_BUFFER[4] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadRight])) {
|
||||
STATUS_BUFFER[4] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadTriangle])) {
|
||||
STATUS_BUFFER[5] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadCross])) {
|
||||
STATUS_BUFFER[5] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadSquare])) {
|
||||
STATUS_BUFFER[5] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadCircle])) {
|
||||
STATUS_BUFFER[5] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadL1])) {
|
||||
STATUS_BUFFER[5] |= 0x04;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadL2])) {
|
||||
STATUS_BUFFER[5] |= 0x01;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadL3])) {
|
||||
STATUS_BUFFER[4] |= 0x02;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadR1])) {
|
||||
STATUS_BUFFER[5] |= 0x08;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadR2])) {
|
||||
STATUS_BUFFER[5] |= 0x02;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadR3])) {
|
||||
STATUS_BUFFER[4] |= 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::PJECModule::PJECModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PJEC", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PJECModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_pjec_get_ps2);
|
||||
ACIO_MODULE_HOOK(ac_io_pjec_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pjec_update_control_status_buffer);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJECModule : public ACIOModule {
|
||||
public:
|
||||
PJECModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "pjei.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/we/io.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
//using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[40];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_pjei_current_coinstock(int a1, uint32_t *coinstock) {
|
||||
*coinstock = eamuse_coin_get_stock();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_consume_coinstock(int a1, uint32_t amount) {
|
||||
return eamuse_coin_consume(amount);
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_get_softwareid(char *dst) {
|
||||
static char DATA[] = "0140FFFFFFFFFFFFFFFF";
|
||||
memcpy(dst, DATA, sizeof(DATA));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_get_systemid(char *dst) {
|
||||
static char DATA[] = "0140FFFFFFFFFFFFFFFF";
|
||||
memcpy(dst, DATA, sizeof(DATA));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// Winning Eleven
|
||||
if (avs::game::is_model({ "KCK", "NCK" })) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::we::get_buttons();
|
||||
|
||||
// apply buttons
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Service])) {
|
||||
STATUS_BUFFER[16] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Test])) {
|
||||
STATUS_BUFFER[16] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::CoinMech])) {
|
||||
STATUS_BUFFER[16] |= 0x04;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Start])) {
|
||||
STATUS_BUFFER[4] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Up])) {
|
||||
STATUS_BUFFER[4] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Down])) {
|
||||
STATUS_BUFFER[4] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Left])) {
|
||||
STATUS_BUFFER[4] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Right])) {
|
||||
STATUS_BUFFER[4] |= 0x08;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonA])) {
|
||||
STATUS_BUFFER[4] |= 0x04;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonB])) {
|
||||
STATUS_BUFFER[4] |= 0x02;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonC])) {
|
||||
STATUS_BUFFER[4] |= 0x01;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonD])) {
|
||||
STATUS_BUFFER[6] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonE])) {
|
||||
STATUS_BUFFER[6] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonF])) {
|
||||
STATUS_BUFFER[6] |= 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ac_io_pjei_get_control_status_buffer(uint8_t *buffer) {
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_check() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_check_isfinished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_missing_check() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_missing_check_isfinished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_lock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_unlock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_coin_blocker_on(bool a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_coin_blocker_off(bool a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method for easily setting the light values
|
||||
*/
|
||||
static void ac_io_pjei_control_lamp_set(uint8_t lamp_bits, uint8_t brightness) {
|
||||
auto &lights = games::we::get_lights();
|
||||
float value = CLAMP(brightness / 31.f, 0.f, 1.f);
|
||||
if (lamp_bits & 0x20) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::LeftRed], value);
|
||||
}
|
||||
if (lamp_bits & 0x10) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::LeftGreen], value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::LeftBlue], value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::RightRed], value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::RightGreen], value);
|
||||
}
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::RightBlue], value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_on(uint8_t lamp_bits) {
|
||||
ac_io_pjei_control_lamp_set(lamp_bits, 31);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_off(uint8_t lamp_bits) {
|
||||
ac_io_pjei_control_lamp_set(lamp_bits, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_bright(uint8_t lamp_bit, uint8_t brightness) {
|
||||
ac_io_pjei_control_lamp_set(lamp_bit, brightness);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_mode(int mode) {
|
||||
// mode -> [0,1] (0 is static, 1 is brightness?)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
acio::PJEIModule::PJEIModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PJEI", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PJEIModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_check);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_coin_blocker_on);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_coin_blocker_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_on);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_mode);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJEIModule : public ACIOModule {
|
||||
public:
|
||||
PJEIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "bi2x.h"
|
||||
|
||||
namespace acio2emu::firmware {
|
||||
bool BI2XNode::handle_packet(const acio2emu::Packet &in, std::vector<uint8_t> &out) {
|
||||
auto cur = in.payload.begin();
|
||||
while ((cur + 1) < in.payload.end()) {
|
||||
auto cmd = (cur[0] << 8) | cur[1];
|
||||
out.push_back(*cur++);
|
||||
out.push_back(*cur++);
|
||||
out.push_back(0);
|
||||
|
||||
switch (cmd) {
|
||||
case 2: // query firmware version
|
||||
read_firmware_version(out);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 16:
|
||||
out.push_back(2);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 800:
|
||||
case 802:
|
||||
case 19:
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 120:
|
||||
out.push_back(3);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 801:
|
||||
out.push_back(33);
|
||||
out.push_back(0);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 784: // poll input
|
||||
if (!read_input(out)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 785: { // write output
|
||||
auto count = write_output(std::span{&*cur, static_cast<size_t>(in.payload.end() - cur)});
|
||||
if (count < 0) {
|
||||
return false;
|
||||
}
|
||||
cur += count;
|
||||
break;
|
||||
}
|
||||
|
||||
case 786:
|
||||
cur += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("bi2x", "unknown command: {}", cmd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/node.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace acio2emu::firmware {
|
||||
class BI2XNode : public Node {
|
||||
virtual void read_firmware_version(std::vector<uint8_t> &buffer) = 0;
|
||||
|
||||
virtual bool read_input(std::vector<uint8_t> &buffer) = 0;
|
||||
virtual int write_output(std::span<const uint8_t> buffer) = 0;
|
||||
|
||||
/*
|
||||
* acio2emu::Node
|
||||
*/
|
||||
bool handle_packet(const acio2emu::Packet &in, std::vector<uint8_t> &out) override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "handle.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h" // ws2s
|
||||
|
||||
namespace acio2emu {
|
||||
class MasterNode : public Node {
|
||||
private:
|
||||
const IOBHandle *iob_;
|
||||
|
||||
public:
|
||||
MasterNode(const IOBHandle *iob) : iob_(iob) { }
|
||||
|
||||
bool handle_packet(const Packet &in, std::vector<uint8_t> &out) {
|
||||
// were we sent a command?
|
||||
if (in.payload.size() >= 2) {
|
||||
if (in.payload[0] != 0 || in.payload[1] != 1) {
|
||||
// unknown command
|
||||
return false;
|
||||
}
|
||||
|
||||
// assign node ids
|
||||
out.push_back(0);
|
||||
out.push_back(1);
|
||||
for (int i = 0; i < iob_->number_of_nodes(); i++) {
|
||||
out.push_back(i * 16);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
IOBHandle::IOBHandle(LPCWSTR device) : device_(device) {
|
||||
nodes_[0] = std::make_unique<MasterNode>(this);
|
||||
}
|
||||
|
||||
bool IOBHandle::register_node(std::unique_ptr<Node> node) {
|
||||
if ((number_of_nodes_ - 1) >= 16) {
|
||||
// too many nodes
|
||||
return false;
|
||||
}
|
||||
|
||||
nodes_[number_of_nodes_++] = std::move(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
int IOBHandle::number_of_nodes() const {
|
||||
// don't include the master node
|
||||
return number_of_nodes_ - 1;
|
||||
}
|
||||
|
||||
void IOBHandle::forward_packet_(const Packet &packet) {
|
||||
// clear the output queue
|
||||
output_ = {};
|
||||
|
||||
auto node = packet.node / 2;
|
||||
if (node >= number_of_nodes_) {
|
||||
log_warning("acio2emu", "cannot forward packet: node out of range: {} >= {}", node, number_of_nodes_);
|
||||
return;
|
||||
}
|
||||
|
||||
// forward the packet to the node
|
||||
std::vector<uint8_t> payload;
|
||||
if (!nodes_[node]->handle_packet(packet, payload)) {
|
||||
// error in handler
|
||||
return;
|
||||
}
|
||||
|
||||
// encode the response
|
||||
encode_packet(output_, node, packet.tag, payload);
|
||||
}
|
||||
|
||||
/*
|
||||
* CustomHandle
|
||||
*/
|
||||
|
||||
bool IOBHandle::open(LPCWSTR lpFileName) {
|
||||
if (device_ != lpFileName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("acio2emu", "Opened {} (ACIO2)", ws2s(device_));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOBHandle::close() {
|
||||
log_info("acio2emu", "Closed {} (ACIO2)", ws2s(device_));
|
||||
return true;
|
||||
}
|
||||
|
||||
int IOBHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
DWORD i = 0;
|
||||
|
||||
while (!output_.empty() && i < nNumberOfBytesToRead) {
|
||||
buffer[i++] = output_.front();
|
||||
output_.pop();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int IOBHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
if (decoder_.update(buffer[i])) {
|
||||
// forward the packet to a node
|
||||
forward_packet_(decoder_.packet());
|
||||
}
|
||||
}
|
||||
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int IOBHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <queue>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/packet.h"
|
||||
#include "acio2emu/node.h"
|
||||
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace acio2emu {
|
||||
class IOBHandle : public CustomHandle {
|
||||
private:
|
||||
std::wstring device_;
|
||||
|
||||
std::array<std::unique_ptr<Node>, 17> nodes_;
|
||||
// the first node is reserved for the "master" node
|
||||
int number_of_nodes_ = 1;
|
||||
|
||||
PacketDecoder decoder_;
|
||||
std::queue<uint8_t> output_;
|
||||
|
||||
void forward_packet_(const Packet &packet);
|
||||
|
||||
public:
|
||||
IOBHandle(LPCWSTR device);
|
||||
|
||||
bool register_node(std::unique_ptr<Node> node);
|
||||
int number_of_nodes() const;
|
||||
|
||||
/*
|
||||
* CustomHandle
|
||||
*/
|
||||
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize) override;
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace acio2emu::detail {
|
||||
inline uint8_t crc4_lgp_c(uint8_t crc, const uint8_t *data, size_t len) {
|
||||
static constexpr uint8_t tbl[] = {
|
||||
0x00, 0x0D, 0x03, 0x0E,
|
||||
0x06, 0x0B, 0x05, 0x08,
|
||||
0x0C, 0x01, 0x0F, 0x02,
|
||||
0x0A, 0x07, 0x09, 0x04,
|
||||
};
|
||||
|
||||
crc &= 15;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto b = data[i];
|
||||
crc = (((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) >> 4) ^ tbl[(((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) ^ (b >> 4)) & 0x0F];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline uint8_t crc7_lgp_48(uint8_t crc, const uint8_t *data, size_t len) {
|
||||
static constexpr uint8_t tbl[] = {
|
||||
0x00, 0x09, 0x12, 0x1B,
|
||||
0x24, 0x2D, 0x36, 0x3F,
|
||||
0x48, 0x41, 0x5A, 0x53,
|
||||
0x6C, 0x65, 0x7E, 0x77
|
||||
};
|
||||
|
||||
crc &= 127;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto b = data[i];
|
||||
crc = (((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) >> 4) ^ tbl[(((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) ^ (b >> 4)) & 0x0F];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <cstdint>
|
||||
|
||||
namespace acio2emu::detail {
|
||||
class InflateTransformer {
|
||||
private:
|
||||
std::queue<uint8_t> output_;
|
||||
|
||||
uint8_t flags_ = 0, flag_shift_ = 0;
|
||||
|
||||
uint8_t window_[85] = {};
|
||||
int window_offset_ = 81;
|
||||
|
||||
enum class inflateStep {
|
||||
readFlags,
|
||||
processFlags,
|
||||
copyStored,
|
||||
copyFromWindow,
|
||||
} step_ = inflateStep::readFlags;
|
||||
|
||||
void window_put_(uint8_t b) {
|
||||
window_[window_offset_++] = b;
|
||||
window_offset_ %= sizeof(window_);
|
||||
}
|
||||
|
||||
uint8_t window_get_(int offset) {
|
||||
return window_[offset % sizeof(window_)];
|
||||
}
|
||||
|
||||
public:
|
||||
void put(uint8_t b) {
|
||||
auto consumed = false;
|
||||
|
||||
while (true) {
|
||||
switch (step_) {
|
||||
case inflateStep::readFlags:
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
flags_ = b;
|
||||
flag_shift_ = 0;
|
||||
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
|
||||
case inflateStep::processFlags:
|
||||
// have we processed every flag?
|
||||
if (flag_shift_ > 6) {
|
||||
step_ = inflateStep::readFlags;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags_ & (1 << flag_shift_)) {
|
||||
flag_shift_++;
|
||||
|
||||
if (flags_ & (1 << flag_shift_)) {
|
||||
// emit 0xAA when both bits are set
|
||||
output_.push(0xAA);
|
||||
}
|
||||
else {
|
||||
// copy from the window when only the lower bit is set
|
||||
step_ = inflateStep::copyFromWindow;
|
||||
}
|
||||
}
|
||||
else {
|
||||
step_ = inflateStep::copyStored;
|
||||
}
|
||||
flag_shift_++;
|
||||
|
||||
break;
|
||||
|
||||
case inflateStep::copyFromWindow: {
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
// determine the match size, default is 2-bytes
|
||||
auto offset = b;
|
||||
auto size = 2;
|
||||
|
||||
if (offset >= 0xAA) {
|
||||
// 4-byte match
|
||||
size = 4;
|
||||
offset -= 0xAB;
|
||||
}
|
||||
else if (offset >= 0x55) {
|
||||
// 3-byte match
|
||||
size = 3;
|
||||
offset -= 0x55;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < size; i ++) {
|
||||
auto cur = window_get_(offset + i);
|
||||
|
||||
window_put_(cur);
|
||||
output_.push(cur);
|
||||
}
|
||||
|
||||
// continue processing flags
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
}
|
||||
|
||||
case inflateStep::copyStored:
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
window_put_(b);
|
||||
output_.push(b);
|
||||
|
||||
// continue processing flags
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get() {
|
||||
if (output_.empty()) {
|
||||
// output queue is empty
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto b = output_.front();
|
||||
output_.pop();
|
||||
|
||||
return b;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "acio2emu/packet.h"
|
||||
|
||||
namespace acio2emu {
|
||||
class Node {
|
||||
public:
|
||||
virtual ~Node() {}
|
||||
|
||||
virtual bool handle_packet(const Packet &in, std::vector<uint8_t> &out) = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
#include "packet.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "acio2emu/internal/crc.h"
|
||||
|
||||
namespace acio2emu {
|
||||
static constexpr uint8_t SOF = 0xAA;
|
||||
static constexpr uint8_t ESC = 0xFF;
|
||||
|
||||
static void encode_payload_(std::queue<uint8_t> &out, const std::vector<uint8_t> &payload) {
|
||||
for (auto b : payload) {
|
||||
if (b == SOF || b == ESC) {
|
||||
out.push(ESC);
|
||||
b = ~b;
|
||||
}
|
||||
out.push(b);
|
||||
}
|
||||
// compute and write the payload's CRC
|
||||
out.push(detail::crc7_lgp_48(0x7F, payload.data(), payload.size()) ^ 0x7F);
|
||||
}
|
||||
|
||||
bool encode_packet(std::queue<uint8_t> &out, uint8_t node, uint8_t tag, const std::vector<uint8_t> &payload) {
|
||||
auto size = payload.size();
|
||||
if (size > 127) {
|
||||
log_warning("acio2emu", "cannot encode packet: payload too large: {} > 127", payload.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
// build the header
|
||||
uint8_t header[5] = {
|
||||
SOF,
|
||||
static_cast<uint8_t>(node * 3),
|
||||
tag,
|
||||
static_cast<uint8_t>(size),
|
||||
0,
|
||||
};
|
||||
// compute the header's CRC
|
||||
header[4] = detail::crc4_lgp_c(0x0F, &header[1], sizeof(header) - 1) ^ 0x0F;
|
||||
// push the header to the output queue
|
||||
for (size_t i = 0; i < sizeof(header); i++) {
|
||||
out.push(header[i]);
|
||||
}
|
||||
|
||||
encode_payload_(out, payload);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketDecoder::set_step_(readStep s) {
|
||||
#ifndef NDEBUG
|
||||
auto valid = true;
|
||||
switch (s) {
|
||||
case readStep::idle:
|
||||
case readStep::readNode:
|
||||
// transition from any step/state allowed
|
||||
break;
|
||||
|
||||
case readStep::readTag:
|
||||
if (step_ != readStep::readNode) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayloadSize:
|
||||
if (step_ != readStep::readTag) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayloadFlags:
|
||||
if (step_ != readStep::readPayloadSize) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readReplacementByte:
|
||||
if (step_ != readStep::readPayloadFlags) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayload:
|
||||
if (step_ != readStep::readPayloadFlags &&
|
||||
step_ != readStep::readReplacementByte &&
|
||||
step_ != readStep::readEscaped
|
||||
) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readEscaped:
|
||||
if (step_ != readStep::readPayload) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_fatal(
|
||||
"acio2emu",
|
||||
"cannot set step: unknown value: {}",
|
||||
static_cast<uint32_t>(s));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
log_fatal(
|
||||
"acio2emu",
|
||||
"illegal transition detected: {} -> {}",
|
||||
static_cast<uint32_t>(step_),
|
||||
static_cast<uint32_t>(s));
|
||||
}
|
||||
#endif
|
||||
step_ = s;
|
||||
}
|
||||
|
||||
int PacketDecoder::update_payload_size_(uint8_t b) {
|
||||
if ((b & 0x80) == 0) {
|
||||
payload_size_ = (payload_size_ << 7) | (b & 0x7F);
|
||||
// finished
|
||||
return 0;
|
||||
}
|
||||
else if ((b & 0x40) != 0 && payload_size_count_ < 5) {
|
||||
payload_size_count_++;
|
||||
payload_size_count_ = (payload_size_count_ << 6) | (b & 0x3F);
|
||||
// continuation required
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// invalid value or invalid state
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PacketDecoder::deobfuscate_(uint8_t b) {
|
||||
if ((b ^ 0xAA) == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
auto mask = 0x55;
|
||||
if ((b & 0x80) == 0) {
|
||||
mask = 0x7F;
|
||||
}
|
||||
|
||||
return (b ^ lcg_()) & mask;
|
||||
}
|
||||
|
||||
void PacketDecoder::reset_(readStep s) {
|
||||
set_step_(s);
|
||||
packet_ = {};
|
||||
payload_size_ = 0;
|
||||
payload_size_count_ = 0;
|
||||
}
|
||||
|
||||
bool PacketDecoder::update(uint8_t b) {
|
||||
// is this the start of a packet?
|
||||
if (b == SOF) {
|
||||
reset_(readStep::readNode);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (step_) {
|
||||
case readStep::readNode:
|
||||
packet_.node = b;
|
||||
|
||||
set_step_(readStep::readTag);
|
||||
break;
|
||||
|
||||
case readStep::readTag:
|
||||
packet_.tag = b;
|
||||
|
||||
set_step_(readStep::readPayloadSize);
|
||||
break;
|
||||
|
||||
case readStep::readPayloadSize: {
|
||||
auto status = update_payload_size_(b);
|
||||
if (status == 0) {
|
||||
// finished reading payload size
|
||||
packet_.payload.reserve(payload_size_);
|
||||
set_step_(readStep::readPayloadFlags);
|
||||
}
|
||||
else if (status == -1) {
|
||||
// reset on error
|
||||
reset_(readStep::idle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case readStep::readPayloadFlags:
|
||||
obfuscated_ = (b & (1 << 4)) != 0;
|
||||
encoding_ = static_cast<payloadEncoding>(b >> 5);
|
||||
|
||||
if (obfuscated_) {
|
||||
lcg_.seed(packet_.tag ^ 0x55);
|
||||
}
|
||||
|
||||
if (encoding_ == payloadEncoding::replace) {
|
||||
set_step_(readStep::readReplacementByte);
|
||||
}
|
||||
else {
|
||||
set_step_(readStep::readPayload);
|
||||
|
||||
if (encoding_ == payloadEncoding::lz) {
|
||||
// reset the InflateTransformer
|
||||
inflate_ = {};
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readReplacementByte:
|
||||
substitute_ = b;
|
||||
|
||||
set_step_(readStep::readPayload);
|
||||
break;
|
||||
|
||||
case readStep::readPayload:
|
||||
// do we need to deobfuscate?
|
||||
if (obfuscated_) {
|
||||
b = deobfuscate_(b);
|
||||
}
|
||||
|
||||
if (encoding_ == payloadEncoding::lz) {
|
||||
inflate_.put(b);
|
||||
for (int i = inflate_.get(); i >= 0; i = inflate_.get()) {
|
||||
packet_.payload.push_back(i);
|
||||
}
|
||||
}
|
||||
else if (encoding_ == payloadEncoding::replace && b == substitute_) {
|
||||
packet_.payload.push_back(SOF);
|
||||
}
|
||||
else if (encoding_ == payloadEncoding::byteStuffing && b == ESC) {
|
||||
set_step_(readStep::readEscaped);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
packet_.payload.push_back(b);
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readEscaped:
|
||||
b = ~b;
|
||||
if (obfuscated_) {
|
||||
b = deobfuscate_(b);
|
||||
}
|
||||
packet_.payload.push_back(b);
|
||||
|
||||
set_step_(readStep::readPayload);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((step_ == readStep::readPayload || step_ == readStep::readPayloadFlags) &&
|
||||
(packet_.payload.size() >= payload_size_)) {
|
||||
set_step_(readStep::idle);
|
||||
// finished reading packet
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Packet &PacketDecoder::packet() {
|
||||
return packet_;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <random> // std::linear_congruential_engine
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/internal/lz.h"
|
||||
|
||||
namespace acio2emu {
|
||||
struct Packet {
|
||||
uint8_t node;
|
||||
uint8_t tag;
|
||||
std::vector<uint8_t> payload;
|
||||
};
|
||||
|
||||
class PacketDecoder {
|
||||
private:
|
||||
Packet packet_ = {};
|
||||
|
||||
// order matters, don't change this enum!
|
||||
enum payloadEncoding {
|
||||
byteStuffing,
|
||||
raw,
|
||||
unknown,
|
||||
replace,
|
||||
lz,
|
||||
} encoding_;
|
||||
|
||||
uint32_t payload_size_ = 0, payload_size_count_ = 0;
|
||||
|
||||
// payloadEncoding::replace state
|
||||
uint8_t substitute_;
|
||||
|
||||
// payloadEncoding::lz state
|
||||
detail::InflateTransformer inflate_;
|
||||
|
||||
// deobfuscation state
|
||||
bool obfuscated_;
|
||||
std::linear_congruential_engine<uint32_t, 1103515245, 12345, 0> lcg_;
|
||||
|
||||
enum class readStep {
|
||||
idle,
|
||||
readNode,
|
||||
readTag,
|
||||
readPayloadSize,
|
||||
readPayloadFlags,
|
||||
readReplacementByte,
|
||||
readPayload,
|
||||
readEscaped,
|
||||
} step_ = readStep::idle;
|
||||
|
||||
void set_step_(readStep s);
|
||||
void reset_(readStep s);
|
||||
|
||||
int update_payload_size_(uint8_t b);
|
||||
uint8_t deobfuscate_(uint8_t b);
|
||||
|
||||
public:
|
||||
bool update(uint8_t b);
|
||||
const Packet &packet();
|
||||
};
|
||||
|
||||
bool encode_packet(std::queue<uint8_t> &out, uint8_t node, uint8_t tag, const std::vector<uint8_t> &payload);
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
#include "acioemu.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
ACIOEmu::ACIOEmu() {
|
||||
this->devices = new std::vector<ACIODeviceEmu *>();
|
||||
this->response_buffer = new circular_buffer<uint8_t>(4096);
|
||||
this->read_buffer = new circular_buffer<uint8_t>(1024);
|
||||
}
|
||||
|
||||
ACIOEmu::~ACIOEmu() {
|
||||
|
||||
// delete devices
|
||||
for (auto device : *this->devices) {
|
||||
delete device;
|
||||
}
|
||||
delete this->devices;
|
||||
|
||||
// delete buffers
|
||||
delete this->response_buffer;
|
||||
delete this->read_buffer;
|
||||
}
|
||||
|
||||
void ACIOEmu::add_device(ACIODeviceEmu *device) {
|
||||
this->devices->push_back(device);
|
||||
}
|
||||
|
||||
void ACIOEmu::write(uint8_t byte) {
|
||||
|
||||
// insert into buffer
|
||||
if (!invert) {
|
||||
if (byte == ACIO_ESCAPE) {
|
||||
invert = true;
|
||||
} else {
|
||||
this->read_buffer->put(byte);
|
||||
}
|
||||
} else {
|
||||
byte = ~byte;
|
||||
invert = false;
|
||||
this->read_buffer->put(byte);
|
||||
}
|
||||
|
||||
// clean garbage
|
||||
while (!this->read_buffer->empty() && this->read_buffer->peek() != 0xAA) {
|
||||
this->read_buffer->get();
|
||||
}
|
||||
while (this->read_buffer->size() > 1 && this->read_buffer->peek(1) == 0xAA) {
|
||||
this->read_buffer->get();
|
||||
}
|
||||
|
||||
// handshake counter
|
||||
static unsigned int handshake_counter = 0;
|
||||
if (byte == 0xAA) {
|
||||
handshake_counter++;
|
||||
} else {
|
||||
handshake_counter = 0;
|
||||
}
|
||||
|
||||
// check for handshake
|
||||
if (handshake_counter > 1) {
|
||||
|
||||
/*
|
||||
* small hack - BIO2 seems to expect more bytes here - sending two bytes each time fixes it
|
||||
* TODO replace this handshake code with something better
|
||||
*/
|
||||
this->response_buffer->put(ACIO_SOF);
|
||||
this->response_buffer->put(ACIO_SOF);
|
||||
handshake_counter--;
|
||||
return;
|
||||
}
|
||||
|
||||
// parse
|
||||
if (!this->read_buffer->empty() && this->read_buffer->size() >= 6) {
|
||||
bool is_complete = false;
|
||||
|
||||
// check if broadcast
|
||||
if (this->read_buffer->peek(1) == ACIO_BROADCAST) {
|
||||
|
||||
// check msg data size
|
||||
auto data_size = this->read_buffer->peek(2);
|
||||
|
||||
// check if msg is complete (SOF + checksum + broadcast header + data_size)
|
||||
is_complete = this->read_buffer->size() >= 2u + 2u + data_size;
|
||||
} else {
|
||||
|
||||
// check msg data size
|
||||
auto data_size = this->read_buffer->peek(5);
|
||||
|
||||
// check if msg is complete (SOF + checksum + command header + data_size)
|
||||
is_complete = this->read_buffer->size() >= 2u + MSG_HEADER_SIZE + data_size;
|
||||
}
|
||||
|
||||
// parse message if complete
|
||||
if (is_complete) {
|
||||
this->msg_parse();
|
||||
this->read_buffer->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint8_t> ACIOEmu::read() {
|
||||
if (this->response_buffer->empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return this->response_buffer->get();
|
||||
}
|
||||
|
||||
size_t ACIOEmu::bytes_available() {
|
||||
return this->response_buffer->size();
|
||||
}
|
||||
|
||||
void ACIOEmu::msg_parse() {
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "MSG RECV: {}", bin2hex(*this->read_buffer));
|
||||
#endif
|
||||
|
||||
// calculate checksum
|
||||
uint8_t chk = 0;
|
||||
size_t max = this->read_buffer->size() - 1;
|
||||
for (size_t i = 1; i < max; i++) {
|
||||
chk += this->read_buffer->peek(i);
|
||||
}
|
||||
|
||||
// check checksum
|
||||
uint8_t chk_receive = this->read_buffer->peek(this->read_buffer->size() - 1);
|
||||
if (chk != chk_receive) {
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "detected wrong checksum: {}/{}", chk, chk_receive);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// get message data
|
||||
auto msg_data = this->read_buffer->peek_all();
|
||||
auto msg_in = (MessageData *) &msg_data[1];
|
||||
|
||||
// correct cmd code endianness if this is not a broadcast
|
||||
if (msg_in->addr != ACIO_BROADCAST) {
|
||||
msg_in->cmd.code = acio_u16(msg_in->cmd.code);
|
||||
}
|
||||
|
||||
// pass to applicable device
|
||||
uint8_t node_offset = 0;
|
||||
for (auto device : *this->devices) {
|
||||
if (device->is_applicable(node_offset, msg_in->addr)) {
|
||||
auto cur_offset = msg_in->addr - node_offset - 1;
|
||||
if (cur_offset < 0) {
|
||||
break;
|
||||
}
|
||||
if (device->parse_msg(msg_in, this->response_buffer)) {
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node_offset += device->node_count;
|
||||
}
|
||||
|
||||
// ignore broadcast messages by default
|
||||
if (msg_in->addr == ACIO_BROADCAST) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default Behavior
|
||||
* If you want to do anything different, just handle the
|
||||
* commands in your own device implementation.
|
||||
*/
|
||||
switch (msg_in->cmd.code) {
|
||||
|
||||
// node count report
|
||||
case ACIO_CMD_ASSIGN_ADDRS: {
|
||||
if (msg_in->addr == 0x00 && node_offset > 0) {
|
||||
auto msg = ACIODeviceEmu::create_msg(msg_in, 1, &node_offset);
|
||||
ACIODeviceEmu::write_msg(msg, this->response_buffer);
|
||||
delete msg;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// status 0 defaults
|
||||
case ACIO_CMD_CLEAR:
|
||||
case ACIO_CMD_STARTUP:
|
||||
case 0x80: // KEEPALIVE
|
||||
case 0xFF: // BROADCAST
|
||||
{
|
||||
// send status 0
|
||||
auto msg = ACIODeviceEmu::create_msg_status(msg_in, 0);
|
||||
ACIODeviceEmu::write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "UNHANDLED MSG FOR ADDR: {}, CMD: 0x{:x}), DATA: {}",
|
||||
msg_in->addr,
|
||||
msg_in->cmd.code,
|
||||
bin2hex(*this->read_buffer));
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "util/circular_buffer.h"
|
||||
|
||||
#include "device.h"
|
||||
#include "icca.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
class ACIOEmu {
|
||||
private:
|
||||
std::vector<ACIODeviceEmu *> *devices;
|
||||
circular_buffer<uint8_t> *response_buffer;
|
||||
circular_buffer<uint8_t> *read_buffer;
|
||||
bool invert = false;
|
||||
|
||||
void msg_parse();
|
||||
|
||||
public:
|
||||
|
||||
explicit ACIOEmu();
|
||||
~ACIOEmu();
|
||||
|
||||
void add_device(ACIODeviceEmu *device);
|
||||
|
||||
void write(uint8_t byte);
|
||||
std::optional<uint8_t> read();
|
||||
size_t bytes_available();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
#include "device.h"
|
||||
#include "hooks/sleephook.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct bio2_bi2a_state_in {
|
||||
uint8_t pad0[3];
|
||||
uint8_t panel[4];
|
||||
uint8_t deck_switch[14];
|
||||
uint8_t pad21[2];
|
||||
uint8_t led_ticker[9];
|
||||
uint8_t spot_light_1[4];
|
||||
uint8_t neon_light;
|
||||
uint8_t spot_light_2[4];
|
||||
uint8_t pad41[7];
|
||||
};
|
||||
|
||||
struct bio2_bi2a_status {
|
||||
uint8_t slider_1;
|
||||
uint8_t system;
|
||||
uint8_t slider_2;
|
||||
uint8_t pad3;
|
||||
uint8_t slider_3;
|
||||
uint8_t pad5;
|
||||
uint8_t slider_4;
|
||||
uint8_t slider_5;
|
||||
uint8_t pad8;
|
||||
uint8_t panel;
|
||||
uint8_t pad10[6];
|
||||
uint8_t tt_p1;
|
||||
uint8_t tt_p2;
|
||||
uint8_t p1_s1;
|
||||
uint8_t pad20;
|
||||
uint8_t p1_s2;
|
||||
uint8_t pad22;
|
||||
uint8_t p1_s3;
|
||||
uint8_t pad24;
|
||||
uint8_t p1_s4;
|
||||
uint8_t pad26;
|
||||
uint8_t p1_s5;
|
||||
uint8_t pad28;
|
||||
uint8_t p1_s6;
|
||||
uint8_t pad30;
|
||||
uint8_t p1_s7;
|
||||
uint8_t pad32;
|
||||
uint8_t p2_s1;
|
||||
uint8_t pad34;
|
||||
uint8_t p2_s2;
|
||||
uint8_t pad36;
|
||||
uint8_t p2_s3;
|
||||
uint8_t pad38;
|
||||
uint8_t p2_s4;
|
||||
uint8_t pad40;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class BI2A : public ACIODeviceEmu {
|
||||
private:
|
||||
uint8_t coin_counter = 0;
|
||||
|
||||
public:
|
||||
explicit BI2A(bool type_new, bool flip_order, bool keypad_thread, uint8_t node_count);
|
||||
~BI2A() override;
|
||||
|
||||
bool parse_msg(unsigned int node_offset,
|
||||
MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) override;
|
||||
|
||||
void update_card(int unit);
|
||||
void update_keypad(int unit, bool update_edge);
|
||||
void update_status(int unit);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#include "device.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
void ACIODeviceEmu::set_header(MessageData* data, uint8_t addr, uint16_t code, uint8_t pid,
|
||||
uint8_t data_size)
|
||||
{
|
||||
// flag as response
|
||||
if (addr != 0) {
|
||||
addr |= ACIO_RESPONSE_FLAG;
|
||||
}
|
||||
|
||||
// set header data
|
||||
data->addr = addr;
|
||||
data->cmd.code = acio_u16(code);
|
||||
data->cmd.pid = pid;
|
||||
data->cmd.data_size = data_size;
|
||||
}
|
||||
|
||||
void ACIODeviceEmu::set_version(MessageData* data, uint32_t type, uint8_t flag,
|
||||
uint8_t ver_major, uint8_t ver_minor, uint8_t ver_rev, std::string code)
|
||||
{
|
||||
|
||||
// set version data
|
||||
auto data_version = &data->cmd.data_version;
|
||||
data_version->type = type;
|
||||
data_version->flag = flag;
|
||||
data_version->ver_major = ver_major;
|
||||
data_version->ver_minor = ver_minor;
|
||||
data_version->ver_rev = ver_rev;
|
||||
strncpy(data_version->code, code.c_str(), sizeof(data_version->code));
|
||||
strncpy(data_version->date, __DATE__, sizeof(data_version->date));
|
||||
strncpy(data_version->time, __TIME__, sizeof(data_version->time));
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg(uint8_t addr, uint16_t code, uint8_t pid, size_t data_size,
|
||||
uint8_t *data)
|
||||
{
|
||||
// check data size
|
||||
if (data_size > 0xFF) {
|
||||
log_warning("acio", "data size > 255: {}", data_size);
|
||||
data_size = 0xFF;
|
||||
}
|
||||
|
||||
// allocate data
|
||||
auto data_raw = new uint8_t[MSG_HEADER_SIZE + data_size];
|
||||
|
||||
// set header
|
||||
auto msg = (MessageData *) &data_raw[0];
|
||||
set_header(msg, addr, code, pid, (uint8_t) data_size);
|
||||
|
||||
// set data
|
||||
if (data) {
|
||||
memcpy(data_raw + MSG_HEADER_SIZE, data, data_size);
|
||||
} else {
|
||||
memset(data_raw + MSG_HEADER_SIZE, 0, data_size);
|
||||
}
|
||||
|
||||
// return prepared message
|
||||
return msg;
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg(MessageData *msg_in, size_t data_size, uint8_t *data) {
|
||||
return create_msg(msg_in->addr, msg_in->cmd.code, msg_in->cmd.pid, data_size, data);
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg_status(uint8_t addr, uint16_t code, uint8_t pid, uint8_t status) {
|
||||
return create_msg(addr, code, pid, 1, &status);
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg_status(MessageData *msg_in, uint8_t status) {
|
||||
return create_msg_status(msg_in->addr, msg_in->cmd.code, msg_in->cmd.pid, status);
|
||||
}
|
||||
|
||||
bool ACIODeviceEmu::is_applicable(uint8_t node_offset, uint8_t node) {
|
||||
return node > node_offset && node <= node_offset + this->node_count;
|
||||
}
|
||||
|
||||
void ACIODeviceEmu::write_msg(const uint8_t *data, size_t size, circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
// header
|
||||
for (int i = 0; i < 2; i++) {
|
||||
response_buffer->put(ACIO_SOF);
|
||||
}
|
||||
|
||||
// msg data and checksum
|
||||
uint8_t b, chk = 0;
|
||||
for (size_t i = 0; i <= size; i++) {
|
||||
|
||||
// set byte to data or checksum
|
||||
if (i < size) {
|
||||
b = data[i];
|
||||
chk += b;
|
||||
} else {
|
||||
b = chk;
|
||||
}
|
||||
|
||||
// check for escape
|
||||
if (b == ACIO_SOF || b == ACIO_ESCAPE) {
|
||||
response_buffer->put(ACIO_ESCAPE);
|
||||
response_buffer->put(~b);
|
||||
} else {
|
||||
response_buffer->put(b);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "ACIO MSG OUT: AA{}{:02X}", bin2hex(data, size), chk);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ACIODeviceEmu::write_msg(MessageData *msg, circular_buffer<uint8_t> *response_buffer) {
|
||||
auto data = reinterpret_cast<const uint8_t *>(msg);
|
||||
write_msg(data, MSG_HEADER_SIZE + msg->cmd.data_size, response_buffer);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/circular_buffer.h"
|
||||
|
||||
// convert big-endian to little-endian
|
||||
#define acio_u16 _byteswap_ushort
|
||||
#define acio_u32 _byteswap_ulong
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
constexpr uint8_t ACIO_SOF = 0xAA;
|
||||
constexpr uint8_t ACIO_ESCAPE = 0xFF;
|
||||
constexpr uint8_t ACIO_BROADCAST = 0x70;
|
||||
constexpr uint8_t ACIO_RESPONSE_FLAG = 0x80;
|
||||
|
||||
// general command codes
|
||||
enum acio_cmd_codes {
|
||||
ACIO_CMD_ASSIGN_ADDRS = 0x0001,
|
||||
ACIO_CMD_GET_VERSION = 0x0002,
|
||||
ACIO_CMD_STARTUP = 0x0003,
|
||||
ACIO_CMD_KEEPALIVE = 0x0080,
|
||||
ACIO_CMD_CLEAR = 0x0100,
|
||||
};
|
||||
|
||||
// message structs
|
||||
#pragma pack(push, 1)
|
||||
struct VersionData {
|
||||
uint32_t type;
|
||||
uint8_t flag;
|
||||
uint8_t ver_major;
|
||||
uint8_t ver_minor;
|
||||
uint8_t ver_rev;
|
||||
char code[4];
|
||||
char date[16];
|
||||
char time[16];
|
||||
};
|
||||
|
||||
struct MessageData {
|
||||
uint8_t addr;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint16_t code;
|
||||
uint8_t pid;
|
||||
uint8_t data_size;
|
||||
|
||||
union {
|
||||
uint8_t raw[0xFF];
|
||||
uint8_t status;
|
||||
VersionData data_version;
|
||||
};
|
||||
} cmd;
|
||||
|
||||
struct {
|
||||
uint8_t data_size;
|
||||
uint8_t raw[0xFF];
|
||||
} broadcast;
|
||||
};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// message sizes
|
||||
constexpr size_t MSG_HEADER_SIZE = 5;
|
||||
constexpr size_t MSG_VERSION_SIZE = sizeof(VersionData);
|
||||
|
||||
class ACIODeviceEmu {
|
||||
public:
|
||||
|
||||
// attributes
|
||||
uint8_t node_count = 0;
|
||||
|
||||
/*
|
||||
* Helper functions for getting/setting the message contents
|
||||
*/
|
||||
static void set_header(MessageData* data, uint8_t addr, uint16_t code, uint8_t pid, uint8_t data_size);
|
||||
static void set_version(MessageData* data, uint32_t type, uint8_t flag,
|
||||
uint8_t ver_major, uint8_t ver_minor, uint8_t ver_rev,
|
||||
std::string code);
|
||||
|
||||
/*
|
||||
* This function creates a basic message with optional parameter data.
|
||||
* If data is set to null, the parameter data will be initialized with 0x00
|
||||
*/
|
||||
static MessageData* create_msg(uint8_t addr, uint16_t cmd, uint8_t pid,
|
||||
size_t data_size, uint8_t *data = nullptr);
|
||||
static MessageData* create_msg(MessageData* msg_in, size_t data_size, uint8_t *data = nullptr);
|
||||
|
||||
/*
|
||||
* Helper functions for generating messages
|
||||
*/
|
||||
static MessageData* create_msg_status(uint8_t addr, uint16_t code, uint8_t pid, uint8_t status);
|
||||
static MessageData* create_msg_status(MessageData* msg_in, uint8_t status);
|
||||
|
||||
virtual ~ACIODeviceEmu() = default;
|
||||
|
||||
virtual bool is_applicable(uint8_t node_offset, uint8_t node);
|
||||
virtual bool parse_msg(MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) = 0;
|
||||
static void write_msg(const uint8_t *data, size_t size, circular_buffer<uint8_t> *response_buffer);
|
||||
static void write_msg(MessageData *msg, circular_buffer<uint8_t> *response_buffer);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "handle.h"
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount) {
|
||||
this->com_port = lpCOMPort;
|
||||
this->icca_node_count = iccaNodeCount;
|
||||
}
|
||||
|
||||
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, com_port) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
|
||||
|
||||
// ACIO device
|
||||
acio_emu.add_device(new acioemu::ICCADevice(false, true, icca_node_count));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int acioemu::ACIOHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
|
||||
// read from emu
|
||||
DWORD bytes_read = 0;
|
||||
while (bytes_read < nNumberOfBytesToRead) {
|
||||
auto cur_byte = acio_emu.read();
|
||||
|
||||
if (cur_byte.has_value()) {
|
||||
buffer[bytes_read++] = cur_byte.value();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return amount of bytes read
|
||||
return (int) bytes_read;
|
||||
}
|
||||
|
||||
int acioemu::ACIOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
// write to emu
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
acio_emu.write(buffer[i]);
|
||||
}
|
||||
|
||||
// return all data written
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int acioemu::ACIOHandle::device_io(
|
||||
DWORD dwIoControlCode,
|
||||
LPVOID lpInBuffer,
|
||||
DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t acioemu::ACIOHandle::bytes_available() {
|
||||
return acio_emu.bytes_available();
|
||||
}
|
||||
|
||||
bool acioemu::ACIOHandle::close() {
|
||||
log_info("acioemu", "Closed {} (ACIO)", ws2s(com_port));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
class ACIOHandle : public CustomHandle {
|
||||
private:
|
||||
LPCWSTR com_port;
|
||||
|
||||
uint8_t icca_node_count;
|
||||
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
public:
|
||||
ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount = 2);
|
||||
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
size_t bytes_available() override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
#include "icca.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
namespace acioemu {
|
||||
bool ICCA_DEVICE_HACK = false;
|
||||
}
|
||||
|
||||
ICCADevice::ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count) {
|
||||
|
||||
// init defaults
|
||||
this->type_new = false;
|
||||
this->flip_order = flip_order;
|
||||
this->node_count = node_count;
|
||||
this->cards = new uint8_t *[node_count] {};
|
||||
this->cards_time = new time_t[node_count] {};
|
||||
this->status = new uint8_t[node_count * 16] {};
|
||||
this->accept = new bool[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->accept[i] = true;
|
||||
}
|
||||
this->hold = new bool[node_count] {};
|
||||
this->keydown = new uint8_t[node_count] {};
|
||||
this->keypad = new uint16_t[node_count] {};
|
||||
this->keypad_last = new bool*[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->keypad_last[i] = new bool[12] {};
|
||||
}
|
||||
this->keypad_capture = new uint8_t[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->keypad_capture[i] = 0x08;
|
||||
}
|
||||
this->crypt = new std::optional<Crypt>[node_count] {};
|
||||
this->counter = new uint8_t[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->counter[i] = 2;
|
||||
}
|
||||
|
||||
// keypad thread for faster polling
|
||||
if (keypad_thread) {
|
||||
this->keypad_thread = new std::thread([this]() {
|
||||
while (this->cards) {
|
||||
for (int unit = 0; unit < this->node_count; unit++) {
|
||||
this->update_keypad(unit, false);
|
||||
}
|
||||
Sleep(7);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ICCADevice::~ICCADevice() {
|
||||
|
||||
// stop thread
|
||||
delete keypad_thread;
|
||||
|
||||
// delete cards in array
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
delete cards[i];
|
||||
}
|
||||
|
||||
// delete the rest
|
||||
delete[] cards;
|
||||
delete[] cards_time;
|
||||
delete[] status;
|
||||
delete[] accept;
|
||||
delete[] hold;
|
||||
delete[] keydown;
|
||||
delete[] keypad;
|
||||
delete[] keypad_last;
|
||||
delete[] keypad_capture;
|
||||
delete[] crypt;
|
||||
delete[] counter;
|
||||
}
|
||||
|
||||
bool ICCADevice::parse_msg(MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
// get unit
|
||||
int unit = msg_in->addr - 1;
|
||||
if (this->flip_order) {
|
||||
unit = this->node_count - unit - 1;
|
||||
}
|
||||
if (unit != 0 && unit != 1) {
|
||||
log_fatal("icca", "invalid unit: {}", unit);
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "ICCA ADDR: {}, CMD: 0x{:04x}", unit, msg_in->cmd.code);
|
||||
#endif
|
||||
|
||||
// check command
|
||||
switch (msg_in->cmd.code) {
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
if (avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
||||
games::sdvx::is_valkyrie_model()) {
|
||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
||||
} else if (avs::game::is_model("VFG")) {
|
||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
||||
} else {
|
||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||
}
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0130: { // REINITIALIZE
|
||||
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0131: { // READ CARD UID
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 16);
|
||||
|
||||
// update things
|
||||
update_card(unit);
|
||||
update_keypad(unit, true);
|
||||
update_status(unit);
|
||||
|
||||
// copy status
|
||||
memcpy(msg->cmd.raw, &status[unit * 16], 16);
|
||||
|
||||
// explicitly set no card since this is just read
|
||||
msg->cmd.raw[0] = 0x01;
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0135: { // SET ACTION
|
||||
|
||||
// check for data
|
||||
if (msg_in->cmd.data_size >= 2) {
|
||||
|
||||
// subcommand
|
||||
switch (msg_in->cmd.raw[1]) {
|
||||
case 0x00: // ACCEPT DISABLE
|
||||
this->accept[unit] = false;
|
||||
break;
|
||||
case 0x11: // ACCEPT ENABLE
|
||||
this->accept[unit] = true;
|
||||
break;
|
||||
case 0x12: // EJECT
|
||||
if (this->cards[unit] != nullptr) {
|
||||
delete this->cards[unit];
|
||||
}
|
||||
this->cards[unit] = nullptr;
|
||||
this->hold[unit] = false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no break, return status
|
||||
[[fallthrough]];
|
||||
}
|
||||
case 0x0134: { // GET STATUS
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 16);
|
||||
|
||||
// update things
|
||||
update_card(unit);
|
||||
update_keypad(unit, true);
|
||||
update_status(unit);
|
||||
|
||||
// copy status
|
||||
memcpy(msg->cmd.raw, &status[unit * 16], 16);
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0160: { // KEY EXCHANGE
|
||||
|
||||
// if this cmd is called, the reader type must be new
|
||||
this->type_new = true;
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 4);
|
||||
|
||||
// set key
|
||||
msg->cmd.raw[0] = 0xBE;
|
||||
msg->cmd.raw[1] = 0xEF;
|
||||
msg->cmd.raw[2] = 0xCA;
|
||||
msg->cmd.raw[3] = 0xFE;
|
||||
|
||||
// convert keys
|
||||
uint32_t game_key =
|
||||
msg_in->cmd.raw[0] << 24 |
|
||||
msg_in->cmd.raw[1] << 16 |
|
||||
msg_in->cmd.raw[2] << 8 |
|
||||
msg_in->cmd.raw[3];
|
||||
uint32_t reader_key =
|
||||
msg->cmd.raw[0] << 24 |
|
||||
msg->cmd.raw[1] << 16 |
|
||||
msg->cmd.raw[2] << 8 |
|
||||
msg->cmd.raw[3];
|
||||
|
||||
log_info("icca", "client key: {:08x}", game_key);
|
||||
log_info("icca", "reader key: {:08x}", reader_key);
|
||||
|
||||
this->crypt[unit].emplace();
|
||||
this->crypt[unit]->set_keys(reader_key, game_key);
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0161: { // READ CARD UID NEW
|
||||
|
||||
// if this cmd is called, the reader type must be new
|
||||
this->type_new = true;
|
||||
|
||||
// decide on answer
|
||||
int answer_type = 0;
|
||||
//if (avs::game::is_model("LDJ"))
|
||||
//answer_type = 1;
|
||||
// SDVX Old cabinet mode
|
||||
if (avs::game::is_model("KFC") && avs::game::SPEC[0] != 'G' && avs::game::SPEC[0] != 'H')
|
||||
answer_type = 1;
|
||||
if (avs::game::is_model("L44"))
|
||||
answer_type = 2;
|
||||
|
||||
// check answer type
|
||||
switch (answer_type) {
|
||||
case 1: {
|
||||
|
||||
// send status 1
|
||||
auto msg = this->create_msg_status(msg_in, 1);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 16);
|
||||
|
||||
// update card
|
||||
update_card(unit);
|
||||
|
||||
// check for card
|
||||
if (this->cards[unit] != nullptr) {
|
||||
|
||||
// copy into data buffer
|
||||
memcpy(msg->cmd.raw, this->cards[unit], 8);
|
||||
|
||||
// delete card
|
||||
delete this->cards[unit];
|
||||
this->cards[unit] = nullptr;
|
||||
this->hold[unit] = false;
|
||||
}
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
// send response with no data
|
||||
auto msg = this->create_msg(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0164: { // GET STATUS ENC
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 18);
|
||||
|
||||
// update things
|
||||
update_card(unit);
|
||||
update_keypad(unit, true);
|
||||
update_status(unit);
|
||||
|
||||
// copy status
|
||||
memcpy(msg->cmd.raw, &status[unit * 16], 16);
|
||||
|
||||
if (this->crypt[unit].has_value()) {
|
||||
auto &crypt = this->crypt[unit];
|
||||
uint16_t crc = crypt->crc(msg->cmd.raw, 16);
|
||||
|
||||
msg->cmd.raw[16] = (uint8_t) (crc >> 8);
|
||||
msg->cmd.raw[17] = (uint8_t) crc;
|
||||
|
||||
crypt->crypt(msg->cmd.raw, 18);
|
||||
} else {
|
||||
log_warning("icca", "'GET STATUS ENC' message received with no crypt keys initialized");
|
||||
}
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x013A: { // POWER CONTROL (tentative name, used in 1.7 firmware)
|
||||
// TODO(felix): isolate this logic to LDJ and/or firmware 1.7 emulation
|
||||
|
||||
if (this->counter[unit] > 0) {
|
||||
this->counter[unit]--;
|
||||
}
|
||||
//log_info("icca", "counter[{}] = {}", unit, this->counter[unit]);
|
||||
|
||||
auto msg = this->create_msg_status(msg_in, this->counter[unit]);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_STARTUP:
|
||||
case ACIO_CMD_CLEAR:
|
||||
case 0x30: // GetBoardProductNumber
|
||||
case 0x31: // GetMicomInfo
|
||||
case 0x3A: // ???
|
||||
case 0x0116: // ???
|
||||
case 0x0120: // ???
|
||||
case 0xFF: // BROADCAST
|
||||
{
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
|
||||
void ICCADevice::update_card(int unit) {
|
||||
|
||||
// wavepass timeout after 10s
|
||||
if (this->cards[unit] != nullptr) {
|
||||
time_t t_now;
|
||||
time(&t_now);
|
||||
|
||||
if (difftime(t_now, this->cards_time[unit]) >= 10.f) {
|
||||
if (this->cards[unit] != nullptr) {
|
||||
delete this->cards[unit];
|
||||
}
|
||||
this->cards[unit] = nullptr;
|
||||
this->hold[unit] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool kb_insert_press = false;
|
||||
|
||||
// eamio keypress
|
||||
kb_insert_press |= static_cast<bool>(eamuse_get_keypad_state((size_t) unit) & (1 << EAM_IO_INSERT));
|
||||
|
||||
// check for card
|
||||
if (this->cards[unit] == nullptr && (eamuse_card_insert_consume(this->node_count, unit) || kb_insert_press)) {
|
||||
auto card = new uint8_t[8];
|
||||
|
||||
if (!eamuse_get_card(this->node_count, unit, card)) {
|
||||
|
||||
// invalid card found
|
||||
delete[] card;
|
||||
|
||||
} else {
|
||||
this->cards[unit] = card;
|
||||
time(&this->cards_time[unit]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int KEYPAD_EAMUSE_MAPPING[] = {
|
||||
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
|
||||
};
|
||||
|
||||
// map for KEYPAD_KEY_CODES:
|
||||
// 7 8 9 | 800 8000 8
|
||||
// 4 5 6 | 400 4000 4
|
||||
// 1 2 3 | 200 2000 2
|
||||
// 0 00 . | 100 1000 1
|
||||
static int KEYPAD_KEY_CODES[]{
|
||||
0x100, // 0
|
||||
0x200, // 1
|
||||
0x2000, // 2
|
||||
2, // 3
|
||||
0x400, // 4
|
||||
0x4000, // 5
|
||||
4, // 6
|
||||
0x800, // 7
|
||||
0x8000, // 8
|
||||
8, // 9
|
||||
1, // .
|
||||
0x1000 // 00
|
||||
};
|
||||
|
||||
// map for KEYPAD_KEY_CODES_ALT:
|
||||
// 7 8 9 | 8 80 800
|
||||
// 4 5 6 | 4 40 400
|
||||
// 1 2 3 | 2 20 200
|
||||
// 0 00 . | 1 10 100
|
||||
//
|
||||
// note that the only game that needs this (SDVX VM) does not accept decimal,
|
||||
// so that key is untested
|
||||
static int KEYPAD_KEY_CODES_ALT[]{
|
||||
1, // 0
|
||||
2, // 1
|
||||
0x20, // 2
|
||||
0x200, // 3
|
||||
4, // 4
|
||||
0x40, // 5
|
||||
0x400, // 6
|
||||
8, // 7
|
||||
0x80, // 8
|
||||
0x800, // 9
|
||||
0x100, // .
|
||||
0x10 // 00
|
||||
};
|
||||
static uint8_t KEYPAD_KEY_CODE_NUMS[]{
|
||||
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
|
||||
};
|
||||
|
||||
void ICCADevice::update_keypad(int unit, bool update_edge) {
|
||||
|
||||
// lock keypad so threads can't interfere
|
||||
std::lock_guard<std::mutex> lock(this->keypad_mutex);
|
||||
|
||||
// reset unit
|
||||
this->keypad[unit] = 0;
|
||||
|
||||
// get eamu key states
|
||||
uint16_t eamu_state = eamuse_get_keypad_state((size_t) unit);
|
||||
|
||||
// iterate keypad
|
||||
bool edge = false;
|
||||
for (int n = 0; n < 12; n++) {
|
||||
int i = n;
|
||||
|
||||
// check if pressed
|
||||
if (eamu_state & (1 << KEYPAD_EAMUSE_MAPPING[i])) {
|
||||
|
||||
if (ICCA_DEVICE_HACK) {
|
||||
this->keypad[unit] |= KEYPAD_KEY_CODES_ALT[i];
|
||||
} else {
|
||||
this->keypad[unit] |= KEYPAD_KEY_CODES[i];
|
||||
}
|
||||
|
||||
if (!this->keypad_last[unit][i] && update_edge) {
|
||||
this->keydown[unit] = (this->keypad_capture[unit] << 4) | KEYPAD_KEY_CODE_NUMS[n];
|
||||
this->keypad_last[unit][i] = true;
|
||||
edge = true;
|
||||
}
|
||||
} else {
|
||||
this->keypad_last[unit][i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// update keypad capture
|
||||
if (update_edge && edge) {
|
||||
this->keypad_capture[unit]++;
|
||||
this->keypad_capture[unit] |= 0x08;
|
||||
} else {
|
||||
this->keydown[unit] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ICCADevice::update_status(int unit) {
|
||||
|
||||
// get buffer
|
||||
uint8_t *buffer = &this->status[unit * 16];
|
||||
|
||||
// clear buffer
|
||||
memset(buffer, 0x00, 16);
|
||||
|
||||
// check for card
|
||||
bool card = false;
|
||||
if (this->cards[unit] != nullptr) {
|
||||
|
||||
// copy card into buffer
|
||||
memcpy(buffer + 2, this->cards[unit], 8);
|
||||
card = true;
|
||||
}
|
||||
|
||||
// check for reader type
|
||||
if (this->type_new) {
|
||||
|
||||
// check for card
|
||||
if (card) {
|
||||
|
||||
// set status to card present
|
||||
buffer[0] = 0x02;
|
||||
|
||||
/*
|
||||
* set card type
|
||||
* 0x00 - ISO15696
|
||||
* 0x01 - FELICA
|
||||
*/
|
||||
bool felica = buffer[2] != 0xE0 && buffer[3] != 0x04;
|
||||
buffer[1] = felica ? 0x01 : 0x00;
|
||||
buffer[10] = felica ? 0x01 : 0x00;
|
||||
|
||||
} else if (avs::game::is_model({"LDJ", "TBS", "XIF"}) || games::sdvx::is_valkyrie_model()) {
|
||||
// set status to 0 otherwise reader power on fails
|
||||
buffer[0] = 0x00;
|
||||
} else {
|
||||
|
||||
// set status to no card present (1 or 4)
|
||||
buffer[0] = 0x04;
|
||||
}
|
||||
} else { // old reader
|
||||
|
||||
// check for card
|
||||
if (card && accept[unit]) {
|
||||
this->hold[unit] = true;
|
||||
}
|
||||
|
||||
// check for hold
|
||||
if (this->hold[unit]) {
|
||||
|
||||
// set status to card present
|
||||
buffer[0] = 0x02;
|
||||
|
||||
/*
|
||||
* sensors
|
||||
* 0x10 - OLD READER FRONT
|
||||
* 0x20 - OLD READER BACK
|
||||
*/
|
||||
|
||||
// activate both sensors
|
||||
buffer[1] = 0x30;
|
||||
} else {
|
||||
|
||||
// card present but reader isn't accepting it
|
||||
if (card) {
|
||||
|
||||
// set card present
|
||||
buffer[0] = 0x02;
|
||||
|
||||
// set front sensor
|
||||
buffer[1] = 0x10;
|
||||
|
||||
} else {
|
||||
|
||||
// no card present
|
||||
buffer[0] = 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
// card type not present for old reader
|
||||
buffer[10] = 0x00;
|
||||
}
|
||||
|
||||
// other flags
|
||||
buffer[11] = 0x03;
|
||||
buffer[12] = keydown[unit];
|
||||
buffer[13] = 0x00;
|
||||
buffer[14] = (uint8_t) (keypad[unit] >> 8);
|
||||
buffer[15] = (uint8_t) (keypad[unit] & 0xFF);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include "device.h"
|
||||
#include "hooks/sleephook.h"
|
||||
#include "reader/crypt.h"
|
||||
|
||||
namespace acioemu {
|
||||
extern bool ICCA_DEVICE_HACK;
|
||||
|
||||
class ICCADevice : public ACIODeviceEmu {
|
||||
private:
|
||||
bool type_new;
|
||||
bool flip_order;
|
||||
std::thread *keypad_thread;
|
||||
std::mutex keypad_mutex;
|
||||
uint8_t **cards;
|
||||
time_t *cards_time;
|
||||
uint8_t *status;
|
||||
bool *accept;
|
||||
bool *hold;
|
||||
uint8_t *keydown;
|
||||
uint16_t *keypad;
|
||||
bool **keypad_last;
|
||||
uint8_t *keypad_capture;
|
||||
std::optional<Crypt> *crypt;
|
||||
uint8_t *counter;
|
||||
|
||||
public:
|
||||
explicit ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count);
|
||||
~ICCADevice() override;
|
||||
|
||||
bool parse_msg(MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) override;
|
||||
|
||||
void update_card(int unit);
|
||||
void update_keypad(int unit, bool update_edge);
|
||||
void update_status(int unit);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "cfg/configurator.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/analogs.h"
|
||||
#include "modules/buttons.h"
|
||||
#include "modules/card.h"
|
||||
#include "modules/capture.h"
|
||||
#include "modules/coin.h"
|
||||
#include "modules/control.h"
|
||||
#include "modules/ddr.h"
|
||||
#include "modules/drs.h"
|
||||
#include "modules/iidx.h"
|
||||
#include "modules/info.h"
|
||||
#include "modules/keypads.h"
|
||||
#include "modules/lcd.h"
|
||||
#include "modules/lights.h"
|
||||
#include "modules/memory.h"
|
||||
#include "modules/touch.h"
|
||||
#include "modules/resize.h"
|
||||
#include "request.h"
|
||||
#include "response.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
using namespace api;
|
||||
|
||||
Controller::Controller(unsigned short port, std::string password, bool pretty)
|
||||
: port(port), password(std::move(password)), pretty(pretty)
|
||||
{
|
||||
if (!crypt::INITIALIZED && !this->password.empty()) {
|
||||
log_fatal("api", "API server with password cannot be used without crypt module");
|
||||
}
|
||||
|
||||
// WSA startup
|
||||
WSADATA wsa_data;
|
||||
int error;
|
||||
if ((error = WSAStartup(MAKEWORD(2, 2), &wsa_data)) != 0) {
|
||||
log_warning("api", "WSAStartup() returned {}", error);
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// create socket
|
||||
this->server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (this->server == INVALID_SOCKET) {
|
||||
log_warning("api", "could not create listener socket: {}", get_last_error_string());
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// configure socket
|
||||
int opt_enable = 1;
|
||||
if (setsockopt(this->server, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char *>(&opt_enable), sizeof(int)) == -1)
|
||||
{
|
||||
log_warning("api", "could not set socket option SO_REUSEADDR: {}", get_last_error_string());
|
||||
}
|
||||
if (setsockopt(this->server, IPPROTO_TCP, TCP_NODELAY,
|
||||
reinterpret_cast<const char *>(&opt_enable), sizeof(int)) == -1)
|
||||
{
|
||||
log_warning("api", "could not set socket option TCP_NODELAY: {}", get_last_error_string());
|
||||
}
|
||||
|
||||
// create address
|
||||
sockaddr_in server_address{};
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_port = htons(this->port);
|
||||
server_address.sin_addr.s_addr = INADDR_ANY;
|
||||
memset(&server_address.sin_zero, 0, sizeof(server_address.sin_zero));
|
||||
|
||||
// bind socket to address
|
||||
if (bind(this->server, (sockaddr *) &server_address, sizeof(sockaddr)) == -1) {
|
||||
log_warning("api", "could not bind socket on port {}: {}", port, get_last_error_string());
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// set socket to listen
|
||||
if (listen(this->server, server_backlog) == -1) {
|
||||
log_warning("api", "could not listen to socket on port {}: {}", port, get_last_error_string());
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// start workers
|
||||
this->server_running = true;
|
||||
for (int i = 0; i < server_worker_count; i++) {
|
||||
this->server_workers.emplace_back(std::thread([this] {
|
||||
this->server_worker();
|
||||
}));
|
||||
}
|
||||
|
||||
// log success
|
||||
log_info("api", "API server is listening on port: {}", this->port);
|
||||
log_info("api", "Using password: {}", this->password.empty() ? "no" : "yes");
|
||||
|
||||
// start websocket on next port
|
||||
this->websocket = new WebSocketController(this, port + 1);
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
|
||||
// stop websocket
|
||||
delete this->websocket;
|
||||
|
||||
// stop serial controllers
|
||||
for (auto &s : this->serial) {
|
||||
delete s;
|
||||
}
|
||||
|
||||
// mark server stop
|
||||
this->server_running = false;
|
||||
|
||||
// close socket
|
||||
if (this->server != INVALID_SOCKET) {
|
||||
closesocket(this->server);
|
||||
}
|
||||
|
||||
// lock handlers
|
||||
std::lock_guard<std::mutex> handlers_guard(this->server_handlers_m);
|
||||
|
||||
// join threads
|
||||
for (auto &worker : this->server_workers) {
|
||||
worker.join();
|
||||
}
|
||||
for (auto &handler : this->server_handlers) {
|
||||
handler.join();
|
||||
}
|
||||
|
||||
// cleanup WSA
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void Controller::listen_serial(std::string port, DWORD baud) {
|
||||
this->serial.push_back(new SerialController(this, port, baud));
|
||||
}
|
||||
|
||||
void Controller::server_worker() {
|
||||
|
||||
// connection loop
|
||||
while (this->server_running) {
|
||||
|
||||
// create client state
|
||||
ClientState client_state {};
|
||||
|
||||
// accept connection
|
||||
int socket_in_size = sizeof(sockaddr_in);
|
||||
client_state.socket = accept(this->server, (sockaddr *) &client_state.address, &socket_in_size);
|
||||
if (client_state.socket == INVALID_SOCKET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// lock handlers
|
||||
std::lock_guard<std::mutex> handlers_guard(this->server_handlers_m);
|
||||
|
||||
// check connection limit
|
||||
if (this->server_handlers.size() >= server_connection_limit) {
|
||||
log_warning("api", "connection limit hit");
|
||||
closesocket(client_state.socket);
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle connection
|
||||
this->server_handlers.emplace_back(std::thread([this, client_state] {
|
||||
this->connection_handler(client_state);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::connection_handler(api::ClientState client_state) {
|
||||
|
||||
// get address string
|
||||
char client_address_str_data[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &client_state.address.sin_addr, client_address_str_data, INET_ADDRSTRLEN);
|
||||
std::string client_address_str(client_address_str_data);
|
||||
|
||||
// log connection
|
||||
log_info("api", "client connected: {}", client_address_str);
|
||||
client_states_m.lock();
|
||||
client_states.emplace_back(&client_state);
|
||||
client_states_m.unlock();
|
||||
|
||||
// init state
|
||||
init_state(&client_state);
|
||||
|
||||
// listen loop
|
||||
std::vector<char> message_buffer;
|
||||
char receive_buffer[server_receive_buffer_size];
|
||||
while (this->server_running && !client_state.close) {
|
||||
|
||||
// receive data
|
||||
int received_length = recv(client_state.socket, receive_buffer, server_receive_buffer_size, 0);
|
||||
if (received_length < 0) {
|
||||
|
||||
// if the received length is < 0, we've got an error
|
||||
log_warning("api", "receive error: {}", WSAGetLastError());
|
||||
break;
|
||||
} else if (received_length == 0) {
|
||||
|
||||
// if the received length is 0, the connection is closed
|
||||
break;
|
||||
}
|
||||
|
||||
// cipher
|
||||
if (client_state.cipher != nullptr) {
|
||||
client_state.cipher->crypt(
|
||||
(uint8_t *) receive_buffer,
|
||||
(size_t) received_length
|
||||
);
|
||||
}
|
||||
|
||||
// put into buffer
|
||||
for (int i = 0; i < received_length; i++) {
|
||||
|
||||
// check for escape byte
|
||||
if (receive_buffer[i] == 0) {
|
||||
|
||||
// get response
|
||||
std::vector<char> send_buffer;
|
||||
this->process_request(&client_state, &message_buffer, &send_buffer);
|
||||
|
||||
// clear message buffer
|
||||
message_buffer.clear();
|
||||
|
||||
// check send buffer for content
|
||||
if (!send_buffer.empty()) {
|
||||
|
||||
// cipher
|
||||
if (client_state.cipher != nullptr) {
|
||||
client_state.cipher->crypt(
|
||||
(uint8_t *) send_buffer.data(),
|
||||
(size_t) send_buffer.size()
|
||||
);
|
||||
}
|
||||
|
||||
// send data
|
||||
send(client_state.socket, send_buffer.data(), (int) send_buffer.size(), 0);
|
||||
|
||||
// check for password change
|
||||
process_password_change(&client_state);
|
||||
}
|
||||
} else {
|
||||
|
||||
// append to message
|
||||
message_buffer.push_back(receive_buffer[i]);
|
||||
|
||||
// check buffer size
|
||||
if (message_buffer.size() > server_message_buffer_max_size) {
|
||||
message_buffer.clear();
|
||||
client_state.close = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log disconnect
|
||||
log_info("api", "client disconnected: {}", client_address_str);
|
||||
client_states_m.lock();
|
||||
client_states.erase(std::remove(client_states.begin(), client_states.end(), &client_state));
|
||||
client_states_m.unlock();
|
||||
|
||||
// close connection
|
||||
closesocket(client_state.socket);
|
||||
|
||||
// free state
|
||||
free_state(&client_state);
|
||||
}
|
||||
|
||||
bool Controller::process_request(ClientState *state, std::vector<char> *in, std::vector<char> *out) {
|
||||
return this->process_request(state, &(*in)[0], in->size(), out);
|
||||
}
|
||||
|
||||
bool Controller::process_request(ClientState *state, const char *in, size_t in_size, std::vector<char> *out) {
|
||||
|
||||
// parse document
|
||||
Document document;
|
||||
document.Parse(in, in_size);
|
||||
|
||||
// check for parse error
|
||||
if (document.HasParseError()) {
|
||||
|
||||
// return empty response and close connection
|
||||
out->push_back(0);
|
||||
state->close = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// build request and response
|
||||
Request request(document);
|
||||
Response response(request.id);
|
||||
bool success = true;
|
||||
|
||||
// check if request has parse error
|
||||
if (request.parse_error) {
|
||||
Value module_error("Request parse error (invalid message format?).");
|
||||
response.add_error(module_error);
|
||||
success = false;
|
||||
} else {
|
||||
|
||||
// find module
|
||||
bool module_found = false;
|
||||
for (auto module : state->modules) {
|
||||
if (module->name == request.module) {
|
||||
module_found = true;
|
||||
|
||||
// check password force
|
||||
if (module->password_force && this->password.empty() && request.function != "session_refresh") {
|
||||
Value err("Module requires the password to be set.");
|
||||
response.add_error(err);
|
||||
break;
|
||||
}
|
||||
|
||||
// handle request
|
||||
module->handle(request, response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if module wasn't found
|
||||
if (!module_found) {
|
||||
Value module_error("Unknown module.");
|
||||
response.add_error(module_error);
|
||||
}
|
||||
|
||||
// check for password change
|
||||
if (response.password_changed) {
|
||||
state->password = response.password;
|
||||
state->password_change = true;
|
||||
}
|
||||
}
|
||||
|
||||
// write response
|
||||
auto response_out = response.get_string(this->pretty);
|
||||
out->insert(out->end(), response_out.begin(), response_out.end());
|
||||
out->push_back(0);
|
||||
return success;
|
||||
}
|
||||
|
||||
void Controller::process_password_change(api::ClientState *state) {
|
||||
|
||||
// check for password change
|
||||
if (state->password_change) {
|
||||
state->password_change = false;
|
||||
delete state->cipher;
|
||||
if (state->password.empty()) {
|
||||
state->cipher = nullptr;
|
||||
} else {
|
||||
state->cipher = new util::RC4(
|
||||
(uint8_t *) state->password.c_str(),
|
||||
state->password.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::init_state(api::ClientState *state) {
|
||||
|
||||
// check if already initialized
|
||||
if (!state->modules.empty()) {
|
||||
log_fatal("api", "client state double initialization");
|
||||
}
|
||||
|
||||
// cipher
|
||||
state->cipher = nullptr;
|
||||
state->password = this->password;
|
||||
if (!this->password.empty()) {
|
||||
state->cipher = new util::RC4((uint8_t *) this->password.c_str(), this->password.size());
|
||||
}
|
||||
|
||||
// create module instances
|
||||
state->modules.push_back(new modules::Analogs());
|
||||
state->modules.push_back(new modules::Buttons());
|
||||
state->modules.push_back(new modules::Card());
|
||||
state->modules.push_back(new modules::Capture());
|
||||
state->modules.push_back(new modules::Coin());
|
||||
state->modules.push_back(new modules::Control());
|
||||
state->modules.push_back(new modules::DDR());
|
||||
state->modules.push_back(new modules::DRS());
|
||||
state->modules.push_back(new modules::IIDX());
|
||||
state->modules.push_back(new modules::Info());
|
||||
state->modules.push_back(new modules::Keypads());
|
||||
state->modules.push_back(new modules::LCD());
|
||||
state->modules.push_back(new modules::Lights());
|
||||
state->modules.push_back(new modules::Memory());
|
||||
state->modules.push_back(new modules::Touch());
|
||||
state->modules.push_back(new modules::Resize());
|
||||
}
|
||||
|
||||
void Controller::free_state(api::ClientState *state) {
|
||||
|
||||
// free modules
|
||||
for (auto module : state->modules) {
|
||||
delete module;
|
||||
}
|
||||
|
||||
// free cipher
|
||||
delete state->cipher;
|
||||
}
|
||||
|
||||
void Controller::free_socket() {
|
||||
if (this->server != INVALID_SOCKET) {
|
||||
closesocket(this->server);
|
||||
this->server = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
this->websocket->free_socket();
|
||||
|
||||
for (auto &s : this->serial) {
|
||||
s->free_port();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::obtain_client_states(std::vector<ClientState> *vec) {
|
||||
std::lock_guard<std::mutex> lock(this->client_states_m);
|
||||
for (auto &state : this->client_states) {
|
||||
vec->push_back(*state);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Controller::get_ip_address(sockaddr_in addr) {
|
||||
switch (addr.sin_family) {
|
||||
default:
|
||||
case AF_INET: {
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
auto ret = inet_ntop(AF_INET, &(addr.sin_addr), buf, sizeof(buf));
|
||||
if (ret != nullptr) {
|
||||
return std::string(ret);
|
||||
} else {
|
||||
return "unknown (" + to_string(WSAGetLastError()) + ")";
|
||||
}
|
||||
}
|
||||
case AF_INET6: {
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
auto ret = inet_ntop(AF_INET6, &(addr.sin_addr), buf, sizeof(buf));
|
||||
if (ret != nullptr) {
|
||||
return std::string(ret);
|
||||
} else {
|
||||
return "unknown (" + to_string(WSAGetLastError()) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "util/rc4.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "websocket.h"
|
||||
#include "serial.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct ClientState {
|
||||
SOCKADDR_IN address;
|
||||
SOCKET socket;
|
||||
bool close = false;
|
||||
std::vector<Module*> modules;
|
||||
std::string password;
|
||||
bool password_change = false;
|
||||
util::RC4 *cipher = nullptr;
|
||||
};
|
||||
|
||||
class Controller {
|
||||
private:
|
||||
|
||||
// configuration
|
||||
const static int server_backlog = 16;
|
||||
const static int server_receive_buffer_size = 64 * 1024;
|
||||
const static int server_message_buffer_max_size = 64 * 1024;
|
||||
const static int server_worker_count = 2;
|
||||
const static int server_connection_limit = 4096;
|
||||
|
||||
// settings
|
||||
unsigned short port;
|
||||
std::string password;
|
||||
bool pretty;
|
||||
|
||||
// server
|
||||
WebSocketController *websocket;
|
||||
std::vector<SerialController *> serial;
|
||||
std::vector<std::thread> server_workers;
|
||||
std::vector<std::thread> server_handlers;
|
||||
std::mutex server_handlers_m;
|
||||
std::vector<api::ClientState *> client_states;
|
||||
std::mutex client_states_m;
|
||||
SOCKET server;
|
||||
void server_worker();
|
||||
void connection_handler(ClientState client_state);
|
||||
|
||||
public:
|
||||
|
||||
// state
|
||||
bool server_running;
|
||||
|
||||
// constructor / destructor
|
||||
Controller(unsigned short port, std::string password, bool pretty);
|
||||
~Controller();
|
||||
|
||||
void listen_serial(std::string port, DWORD baud);
|
||||
|
||||
bool process_request(ClientState *state, std::vector<char> *in, std::vector<char> *out);
|
||||
bool process_request(ClientState *state, const char *in, size_t in_size, std::vector<char> *out);
|
||||
static void process_password_change(ClientState *state);
|
||||
|
||||
void init_state(ClientState *state);
|
||||
static void free_state(ClientState *state);
|
||||
|
||||
void free_socket();
|
||||
void obtain_client_states(std::vector<ClientState> *output);
|
||||
|
||||
std::string get_ip_address(sockaddr_in addr);
|
||||
|
||||
inline const std::string &get_password() const {
|
||||
return this->password;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <utility>
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
bool LOGGING = false;
|
||||
|
||||
Module::Module(std::string name, bool password_force) {
|
||||
this->name = std::move(name);
|
||||
this->password_force = password_force;
|
||||
}
|
||||
|
||||
void Module::handle(Request &req, Response &res) {
|
||||
|
||||
// log module access
|
||||
if (LOGGING)
|
||||
log_info("api::" + this->name, "handling request");
|
||||
|
||||
// find function
|
||||
auto pos = functions.find(req.function);
|
||||
if (pos == functions.end())
|
||||
return error_function_unknown(res);
|
||||
|
||||
// call function
|
||||
pos->second(req, res);
|
||||
}
|
||||
|
||||
void Module::error(Response &res, std::string err) {
|
||||
|
||||
// log the warning
|
||||
log_warning("api::" + this->name, "error: {}", err);
|
||||
|
||||
// add error to response
|
||||
Value val(err.c_str(), res.doc()->GetAllocator());
|
||||
res.add_error(val);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "response.h"
|
||||
#include "request.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
extern bool LOGGING;
|
||||
|
||||
// callback
|
||||
typedef std::function<void(Request &, Response &)> ModuleFunctionCallback;
|
||||
|
||||
class Module {
|
||||
protected:
|
||||
|
||||
// map of available functions
|
||||
robin_hood::unordered_map<std::string, ModuleFunctionCallback> functions;
|
||||
|
||||
// default constructor
|
||||
explicit Module(std::string name, bool password_force=false);
|
||||
|
||||
public:
|
||||
|
||||
// virtual deconstructor
|
||||
virtual ~Module() = default;
|
||||
|
||||
// name of the module (should match namespace)
|
||||
std::string name;
|
||||
bool password_force;
|
||||
|
||||
// the magic
|
||||
void handle(Request &req, Response &res);
|
||||
|
||||
/*
|
||||
* Error definitions.
|
||||
*/
|
||||
|
||||
void error(Response &res, std::string err);
|
||||
void error_type(Response &res, const std::string &field, const std::string &type) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be a " << type;
|
||||
error(res, s.str());
|
||||
};
|
||||
void error_size(Response &res, const std::string &field, size_t size) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be of size " << size;
|
||||
error(res, s.str());
|
||||
}
|
||||
void error_unknown(Response &res, const std::string &field, const std::string &name) {
|
||||
std::ostringstream s;
|
||||
s << "Unknown " << field << ": " << name;
|
||||
error(res, s.str());
|
||||
}
|
||||
|
||||
#define ERR(name, err) void error_##name(Response &res) { error(res, err); }
|
||||
ERR(function_unknown, "Unknown function.");
|
||||
ERR(params_insufficient, "Insufficient number of parameters.");
|
||||
#undef ERR
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "analogs.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/analog.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Analogs::Analogs() : Module("analogs") {
|
||||
functions["read"] = std::bind(&Analogs::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Analogs::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Analogs::write_reset, this, _1, _2);
|
||||
analogs = games::get_analogs(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Analogs::read(api::Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
Value state(kArrayType);
|
||||
Value analog_name(analog.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value analog_state(GameAPI::Analogs::getState(RI_MGR, analog));
|
||||
Value analog_enabled(analog.override_enabled);
|
||||
state.PushBack(analog_name, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_state, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Analogs::write(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
auto analog_state = param[1].GetFloat();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog(analog_name, analog_state)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Analogs::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (analogs != nullptr) {
|
||||
for (auto &analog : *this->analogs) {
|
||||
analog.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog_reset(analog_name)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Analogs::write_analog(std::string name, float state) {
|
||||
|
||||
// check analog cache
|
||||
if (!this->analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||
analog.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Analogs::write_analog_reset(std::string name) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Analogs : public Module {
|
||||
public:
|
||||
Analogs();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Analog> *analogs;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_analog(std::string name, float state);
|
||||
bool write_analog_reset(std::string name);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
#include "buttons.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/button.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Buttons::Buttons() : Module("buttons") {
|
||||
functions["read"] = std::bind(&Buttons::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Buttons::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Buttons::write_reset, this, _1, _2);
|
||||
buttons = games::get_buttons(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Buttons::read(api::Request &req, Response &res) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each button
|
||||
for (auto &button : *this->buttons) {
|
||||
Value state(kArrayType);
|
||||
Value button_name(button.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value button_state(GameAPI::Buttons::getVelocity(RI_MGR, button));
|
||||
Value button_enabled(button.override_enabled);
|
||||
state.PushBack(button_name, res.doc()->GetAllocator());
|
||||
state.PushBack(button_state, res.doc()->GetAllocator());
|
||||
state.PushBack(button_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: bool/float], ...)
|
||||
*/
|
||||
void Buttons::write(Request &req, Response &res) {
|
||||
|
||||
// check button cache
|
||||
if (!buttons) {
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsBool() && !param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "bool or float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto button_name = param[0].GetString();
|
||||
auto button_state = param[1].IsBool() ? param[1].GetBool() : param[1].GetFloat() > 0;
|
||||
auto button_velocity = param[1].IsFloat() ? param[1].GetFloat() : (button_state ? 1.f : 0.f);
|
||||
|
||||
// write button state
|
||||
if (!this->write_button(button_name, button_velocity)) {
|
||||
error_unknown(res, "button", button_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Buttons::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (buttons != nullptr) {
|
||||
for (auto &button : *this->buttons) {
|
||||
button.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto button_name = param[0].GetString();
|
||||
|
||||
// write button state
|
||||
if (!this->write_button_reset(button_name)) {
|
||||
error_unknown(res, "button", button_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Buttons::write_button(std::string name, float state) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find button
|
||||
for (auto &button : *this->buttons) {
|
||||
if (button.getName() == name) {
|
||||
button.override_state = state > 0.f ?
|
||||
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
||||
button.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown button
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Buttons::write_button_reset(std::string name) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find button
|
||||
for (auto &button : *this->buttons) {
|
||||
if (button.getName() == name) {
|
||||
button.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown button
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Buttons : public Module {
|
||||
public:
|
||||
Buttons();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Button> *buttons;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_button(std::string name, float state);
|
||||
bool write_button_reset(std::string name);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#include "capture.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/crypt.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
std::optional<uint32_t> CAPTURE_QUALITY;
|
||||
std::optional<uint32_t> CAPTURE_DIVIDE;
|
||||
|
||||
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||
|
||||
Capture::Capture() : Module("capture") {
|
||||
functions["get_screens"] = std::bind(&Capture::get_screens, this, _1, _2);
|
||||
functions["get_jpg"] = std::bind(&Capture::get_jpg, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_screens()
|
||||
*/
|
||||
void Capture::get_screens(Request &req, Response &res) {
|
||||
|
||||
// aquire screens
|
||||
std::vector<int> screens;
|
||||
graphics_screens_get(screens);
|
||||
|
||||
// add screens to response
|
||||
for (auto &screen : screens) {
|
||||
res.add_data(screen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_jpg([screen=0, quality=70, downscale=0, divide=1])
|
||||
* screen: uint specifying the window
|
||||
* quality: uint in range [0, 100]
|
||||
* reduce: uint for dividing image size
|
||||
*/
|
||||
void Capture::get_jpg(Request &req, Response &res) {
|
||||
CAPTURE_BUFFER.reserve(1024 * 128);
|
||||
|
||||
// settings
|
||||
int screen = 0;
|
||||
int quality = 70;
|
||||
int divide = 1;
|
||||
if (req.params.Size() > 0 && req.params[0].IsUint()) {
|
||||
screen = req.params[0].GetUint();
|
||||
}
|
||||
|
||||
if (CAPTURE_QUALITY.has_value()) {
|
||||
quality = CAPTURE_QUALITY.value();
|
||||
} else if (req.params.Size() > 1 && req.params[1].IsUint()) {
|
||||
quality = req.params[1].GetUint();
|
||||
}
|
||||
|
||||
if (CAPTURE_DIVIDE.has_value()) {
|
||||
divide = CAPTURE_DIVIDE.value();
|
||||
} else if (req.params.Size() > 2 && req.params[2].IsUint()) {
|
||||
divide = req.params[2].GetUint();
|
||||
}
|
||||
|
||||
// receive JPEG data
|
||||
uint64_t timestamp = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
graphics_capture_trigger(screen);
|
||||
bool success = graphics_capture_receive_jpeg(screen, [] (uint8_t byte) {
|
||||
CAPTURE_BUFFER.push_back(byte);
|
||||
}, true, quality, true, divide, ×tamp, &width, &height);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// encode to base64
|
||||
auto encoded = crypt::base64_encode(
|
||||
CAPTURE_BUFFER.data(),
|
||||
CAPTURE_BUFFER.size());
|
||||
|
||||
// clear buffer
|
||||
CAPTURE_BUFFER.clear();
|
||||
|
||||
// add data to response
|
||||
Value data;
|
||||
data.SetString(encoded.c_str(), encoded.length(), res.doc()->GetAllocator());
|
||||
res.add_data(timestamp);
|
||||
res.add_data(width);
|
||||
res.add_data(height);
|
||||
res.add_data(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
extern std::optional<uint32_t> CAPTURE_QUALITY;
|
||||
extern std::optional<uint32_t> CAPTURE_DIVIDE;
|
||||
|
||||
class Capture : public Module {
|
||||
public:
|
||||
Capture();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get_screens(Request &req, Response &res);
|
||||
void get_jpg(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "card.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Card::Card() : Module("card") {
|
||||
functions["insert"] = std::bind(&Card::insert, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* insert(index, card_id)
|
||||
* index: uint in range [0, 1]
|
||||
* card_id: hex string of length 16
|
||||
*/
|
||||
void Card::insert(Request &req, Response &res) {
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 2)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsUint())
|
||||
return error_type(res, "index", "uint");
|
||||
if (!req.params[1].IsString())
|
||||
return error_type(res, "card_id", "hex string");
|
||||
if (req.params[1].GetStringLength() != 16)
|
||||
return error_size(res, "card_id", 16);
|
||||
|
||||
// get params
|
||||
auto index = req.params[0].GetUint();
|
||||
auto card_hex = req.params[1].GetString();
|
||||
|
||||
// convert to binary
|
||||
uint8_t card_bin[8] {};
|
||||
if (!hex2bin(card_hex, card_bin)) {
|
||||
return error_type(res, "card_id", "hex string");
|
||||
}
|
||||
|
||||
// log
|
||||
if (LOGGING) {
|
||||
log_info("api::card", "inserting card: {}", card_hex);
|
||||
}
|
||||
|
||||
// insert card
|
||||
eamuse_card_insert(index & 1, card_bin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Card : public Module {
|
||||
public:
|
||||
Card();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void insert(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "coin.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Coin::Coin() : Module("coin") {
|
||||
functions["get"] = std::bind(&Coin::get, this, _1, _2);
|
||||
functions["set"] = std::bind(&Coin::set, this, _1, _2);
|
||||
functions["insert"] = std::bind(&Coin::insert, this, _1, _2);
|
||||
functions["blocker_get"] = std::bind(&Coin::blocker_get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* get()
|
||||
*/
|
||||
void Coin::get(api::Request &req, api::Response &res) {
|
||||
|
||||
// get coin stock
|
||||
auto coin_stock = eamuse_coin_get_stock();
|
||||
|
||||
// insert value
|
||||
Value coin_stock_val(coin_stock);
|
||||
res.add_data(coin_stock_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* set(amount: int)
|
||||
*/
|
||||
void Coin::set(api::Request &req, api::Response &res) {
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "amount", "int");
|
||||
|
||||
// set coin stock
|
||||
eamuse_coin_set_stock(req.params[0].GetInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* insert()
|
||||
* insert(amount: int)
|
||||
*/
|
||||
void Coin::insert(api::Request &req, api::Response &res) {
|
||||
|
||||
// insert()
|
||||
if (req.params.Size() == 0) {
|
||||
eamuse_coin_add();
|
||||
return;
|
||||
}
|
||||
|
||||
// check params
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "amount", "int");
|
||||
|
||||
// add to coin stock
|
||||
eamuse_coin_set_stock(eamuse_coin_get_stock() + std::max(0, req.params[0].GetInt()));
|
||||
}
|
||||
|
||||
/*
|
||||
* blocker_get()
|
||||
*/
|
||||
void Coin::blocker_get(api::Request &req, api::Response &res) {
|
||||
|
||||
// get block status
|
||||
auto block_status = eamuse_coin_get_block();
|
||||
|
||||
// insert value
|
||||
Value block_val(block_status);
|
||||
res.add_data(block_val);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Coin : public Module {
|
||||
public:
|
||||
Coin();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void insert(Request &req, Response &res);
|
||||
void blocker_get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "control.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <functional>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct SignalMapping {
|
||||
int signum;
|
||||
const char* name;
|
||||
};
|
||||
static SignalMapping SIGNAL_MAPPINGS[] = {
|
||||
{ SIGABRT, "SIGABRT" },
|
||||
{ SIGFPE, "SIGFPE" },
|
||||
{ SIGILL, "SIGILL" },
|
||||
{ SIGINT, "SIGINT" },
|
||||
{ SIGSEGV, "SIGSEGV" },
|
||||
{ SIGTERM, "SIGTERM" },
|
||||
};
|
||||
|
||||
Control::Control() : Module("control", true) {
|
||||
functions["raise"] = std::bind(&Control::raise, this, _1, _2);
|
||||
functions["exit"] = std::bind(&Control::exit, this, _1, _2);
|
||||
functions["restart"] = std::bind(&Control::restart, this, _1, _2);
|
||||
functions["session_refresh"] = std::bind(&Control::session_refresh, this, _1, _2);
|
||||
functions["shutdown"] = std::bind(&Control::shutdown, this, _1, _2);
|
||||
functions["reboot"] = std::bind(&Control::reboot, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* raise(signal: str)
|
||||
*/
|
||||
void Control::raise(Request &req, Response &res) {
|
||||
|
||||
// check args
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsString())
|
||||
return error_type(res, "signal", "string");
|
||||
|
||||
// get signal
|
||||
auto signal_str = req.params[0].GetString();
|
||||
int signal_val = -1;
|
||||
for (auto mapping : SIGNAL_MAPPINGS) {
|
||||
if (_stricmp(mapping.name, signal_str) == 0) {
|
||||
signal_val = mapping.signum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if not found
|
||||
if (signal_val < 0)
|
||||
return error_unknown(res, "signal", signal_str);
|
||||
|
||||
// raise signal
|
||||
if (::raise(signal_val))
|
||||
return error(res, "Failed to raise signo " + to_string(signal_val));
|
||||
}
|
||||
|
||||
/**
|
||||
* exit()
|
||||
* exit(code: int)
|
||||
*/
|
||||
void Control::exit(Request &req, Response &res) {
|
||||
|
||||
// exit()
|
||||
if (req.params.Size() == 0) {
|
||||
launcher::shutdown();
|
||||
}
|
||||
|
||||
// check code
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "code", "int");
|
||||
|
||||
// exit
|
||||
launcher::shutdown(req.params[0].GetInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* restart()
|
||||
*/
|
||||
void Control::restart(Request &req, Response &res) {
|
||||
|
||||
// restart launcher
|
||||
launcher::restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* session_refresh()
|
||||
*/
|
||||
void Control::session_refresh(Request &req, Response &res) {
|
||||
|
||||
// generate new password
|
||||
uint8_t password_bin[128];
|
||||
crypt::random_bytes(password_bin, std::size(password_bin));
|
||||
std::string password = bin2hex(&password_bin[0], std::size(password_bin));
|
||||
|
||||
// add to response
|
||||
Value password_val(password.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(password_val);
|
||||
|
||||
// change password
|
||||
res.password_change(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* shutdown()
|
||||
*/
|
||||
void Control::shutdown(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_HYBRID_SHUTDOWN | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to shutdown system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* reboot()
|
||||
*/
|
||||
void Control::reboot(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to reboot system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Control : public Module {
|
||||
public:
|
||||
Control();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void raise(Request &req, Response &res);
|
||||
void exit(Request &req, Response &res);
|
||||
void restart(Request &req, Response &res);
|
||||
void session_refresh(Request &req, Response &res);
|
||||
void shutdown(Request &req, Response &res);
|
||||
void reboot(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "ddr.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/ddr/ddr.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
DDR::DDR() : Module("ddr") {
|
||||
functions["tapeled_get"] = std::bind(&DDR::tapeled_get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows fetching of the RGB LED strips that are gold cabinets, via SpiceAPI
|
||||
*/
|
||||
void DDR::tapeled_get(Request &req, Response &res) {
|
||||
static const char* device_names[11] = {
|
||||
"p1_foot_up",
|
||||
"p1_foot_right",
|
||||
"p1_foot_left",
|
||||
"p1_foot_down",
|
||||
"p2_foot_up",
|
||||
"p2_foot_right",
|
||||
"p2_foot_left",
|
||||
"p2_foot_down",
|
||||
"top_panel",
|
||||
"monitor_left",
|
||||
"monitor_right"
|
||||
};
|
||||
|
||||
Value response_object(kObjectType);
|
||||
|
||||
// Iterate through each device and dump its lights data into the response
|
||||
for (size_t device = 0; device < 11; device++) {
|
||||
size_t num_leds = 25;
|
||||
if (device > 7)
|
||||
num_leds = 50;
|
||||
|
||||
Value light_state(kArrayType);
|
||||
light_state.Reserve(num_leds * 3, res.doc()->GetAllocator());
|
||||
for (size_t led = 0; led < num_leds; led++) {
|
||||
light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][0], res.doc()->GetAllocator());
|
||||
light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][1], res.doc()->GetAllocator());
|
||||
light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][2], res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
response_object.AddMember(StringRef(device_names[device]), light_state, res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
res.add_data(response_object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class DDR : public Module {
|
||||
public:
|
||||
DDR();
|
||||
|
||||
private:
|
||||
// function definitions
|
||||
void tapeled_get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "drs.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/drs/drs.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
DRS::DRS() : Module("drs") {
|
||||
functions["tapeled_get"] = std::bind(&DRS::tapeled_get, this, _1, _2);
|
||||
functions["touch_set"] = std::bind(&DRS::touch_set, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ticker_get()
|
||||
*/
|
||||
void DRS::tapeled_get(Request &req, Response &res) {
|
||||
|
||||
// copy data to array
|
||||
Value tapeled(kArrayType);
|
||||
const size_t tape_len = sizeof(games::drs::DRS_TAPELED);
|
||||
const uint8_t *tape_raw = (uint8_t*) games::drs::DRS_TAPELED;
|
||||
tapeled.Reserve(tape_len, res.doc()->GetAllocator());
|
||||
for (size_t i = 0; i < tape_len; i++) {
|
||||
tapeled.PushBack(tape_raw[i], res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
// add to response
|
||||
res.add_data(tapeled);
|
||||
}
|
||||
|
||||
void DRS::touch_set(Request &req, Response &res) {
|
||||
|
||||
// get all touch points
|
||||
auto touches = std::make_unique<games::drs::drs_touch_t[]>(req.params.Size());
|
||||
size_t i = 0;
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (param.Size() < 6) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsUint()) {
|
||||
error_type(res, "type", "uint");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsUint()) {
|
||||
error_type(res, "id", "uint");
|
||||
continue;
|
||||
}
|
||||
if (!param[2].IsDouble()) {
|
||||
error_type(res, "x", "double");
|
||||
continue;
|
||||
}
|
||||
if (!param[3].IsDouble()) {
|
||||
error_type(res, "y", "double");
|
||||
continue;
|
||||
}
|
||||
if (!param[4].IsDouble()) {
|
||||
error_type(res, "width", "double");
|
||||
continue;
|
||||
}
|
||||
if (!param[5].IsDouble()) {
|
||||
error_type(res, "height", "double");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto touch_type = param[0].GetUint();
|
||||
auto touch_id = param[1].GetUint();
|
||||
auto touch_x = param[2].GetDouble();
|
||||
auto touch_y = param[3].GetDouble();
|
||||
auto width = param[4].GetDouble();
|
||||
auto height = param[5].GetDouble();
|
||||
|
||||
touches[i].type = touch_type;
|
||||
touches[i].id = touch_id;
|
||||
touches[i].x = touch_x;
|
||||
touches[i].y = touch_y;
|
||||
touches[i].width = width;
|
||||
touches[i].height = height;
|
||||
i++;
|
||||
}
|
||||
|
||||
// apply touch points
|
||||
games::drs::fire_touches(touches.get(), i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class DRS : public Module {
|
||||
public:
|
||||
DRS();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void tapeled_get(Request &req, Response &res);
|
||||
void touch_set(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "iidx.h"
|
||||
#include <functional>
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
// settings
|
||||
static const size_t TICKER_SIZE = 9;
|
||||
|
||||
IIDX::IIDX() : Module("iidx") {
|
||||
functions["ticker_get"] = std::bind(&IIDX::ticker_get, this, _1, _2);
|
||||
functions["ticker_set"] = std::bind(&IIDX::ticker_set, this, _1, _2);
|
||||
functions["ticker_reset"] = std::bind(&IIDX::ticker_reset, this, _1, _2);
|
||||
functions["tapeled_get"] = std::bind(&IIDX::tapeled_get, this, _1, _2);
|
||||
|
||||
for (auto &light : games::iidx::TAPELED_MAPPING) {
|
||||
this->lights_by_names.emplace(light.lightName, light);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ticker_get()
|
||||
*/
|
||||
void IIDX::ticker_get(api::Request &req, Response &res) {
|
||||
|
||||
// get led ticker
|
||||
games::iidx::IIDX_LED_TICKER_LOCK.lock();
|
||||
Value led_ticker(StringRef(games::iidx::IIDXIO_LED_TICKER, TICKER_SIZE), res.doc()->GetAllocator());
|
||||
games::iidx::IIDX_LED_TICKER_LOCK.unlock();
|
||||
|
||||
// add to response
|
||||
res.add_data(led_ticker);
|
||||
}
|
||||
|
||||
/**
|
||||
* ticker_set(text: str)
|
||||
*/
|
||||
void IIDX::ticker_set(api::Request &req, api::Response &res) {
|
||||
|
||||
// check param
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsString())
|
||||
return error_type(res, "text", "str");
|
||||
|
||||
// get param
|
||||
auto text = req.params[0].GetString();
|
||||
auto text_len = req.params[0].GetStringLength();
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
|
||||
|
||||
// set to read only
|
||||
games::iidx::IIDXIO_LED_TICKER_READONLY = true;
|
||||
|
||||
// set led ticker
|
||||
memset(games::iidx::IIDXIO_LED_TICKER, ' ', TICKER_SIZE);
|
||||
for (size_t i = 0; i < TICKER_SIZE && i < text_len; i++) {
|
||||
games::iidx::IIDXIO_LED_TICKER[i] = text[i];
|
||||
}
|
||||
}
|
||||
|
||||
void IIDX::ticker_reset(api::Request &req, api::Response &res) {
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
|
||||
|
||||
// disable read only
|
||||
games::iidx::IIDXIO_LED_TICKER_READONLY = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* tapeled_get()
|
||||
* tapeled_get(name: str, ...)
|
||||
*/
|
||||
void IIDX::tapeled_get(Request &req, Response &res) {
|
||||
Value response_object(kObjectType);
|
||||
|
||||
// all tape leds
|
||||
if (req.params.Size() == 0) {
|
||||
// Iterate through each device and dump its lights data into the response
|
||||
for (const auto &mapping : games::iidx::TAPELED_MAPPING) {
|
||||
copy_tapeled_data(res, response_object, mapping);
|
||||
}
|
||||
} else {
|
||||
// specified light names
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
// check params
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
return;
|
||||
}
|
||||
const auto name = param.GetString();
|
||||
if (const auto &it = lights_by_names.find(name); it != lights_by_names.end()) {
|
||||
const auto mapping = it->second.get();
|
||||
copy_tapeled_data(res, response_object, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.add_data(response_object);
|
||||
}
|
||||
|
||||
void IIDX::copy_tapeled_data(Response &res, Value &response_object, const tapeledutils::tape_led &mapping) {
|
||||
// Create an array for the light state
|
||||
Value light_state(kArrayType);
|
||||
light_state.Reserve(mapping.data.capacity() * 3, res.doc()->GetAllocator());
|
||||
for (const auto [r, g, b] : mapping.data) {
|
||||
light_state.PushBack(r, res.doc()->GetAllocator());
|
||||
light_state.PushBack(g, res.doc()->GetAllocator());
|
||||
light_state.PushBack(b, res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
// Can't use StringRef here, turns some strings partially into null bytes for some reason
|
||||
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
|
||||
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class IIDX : public Module {
|
||||
public:
|
||||
IIDX();
|
||||
|
||||
private:
|
||||
// state
|
||||
robin_hood::unordered_map<std::string, std::reference_wrapper<tapeledutils::tape_led>> lights_by_names;
|
||||
|
||||
// function definitions
|
||||
void ticker_get(Request &req, Response &res);
|
||||
void ticker_set(Request &req, Response &res);
|
||||
void ticker_reset(Request &req, Response &res);
|
||||
void tapeled_get(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
void copy_tapeled_data(Response &res, rapidjson::Value &response_object, const tapeledutils::tape_led &mapping);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#include "info.h"
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "avs/game.h"
|
||||
#include "avs/ea3.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "build/defs.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Info::Info() : Module("info") {
|
||||
functions["avs"] = std::bind(&Info::avs, this, _1, _2);
|
||||
functions["launcher"] = std::bind(&Info::launcher, this, _1, _2);
|
||||
functions["memory"] = std::bind(&Info::memory, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* avs()
|
||||
*/
|
||||
void Info::avs(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("model", StringRef(avs::game::MODEL, 3), alloc);
|
||||
info.AddMember("dest", StringRef(avs::game::DEST, 1), alloc);
|
||||
info.AddMember("spec", StringRef(avs::game::SPEC, 1), alloc);
|
||||
info.AddMember("rev", StringRef(avs::game::REV, 1), alloc);
|
||||
info.AddMember("ext", StringRef(avs::game::EXT, 10), alloc);
|
||||
info.AddMember("services", StringRef(avs::ea3::EA3_BOOT_URL.c_str()), alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* launcher()
|
||||
*/
|
||||
void Info::launcher(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build args
|
||||
Value args(kArrayType);
|
||||
for (int count = 0; count < LAUNCHER_ARGC; count++) {
|
||||
auto arg = LAUNCHER_ARGV[count];
|
||||
args.PushBack(StringRef(arg), alloc);
|
||||
}
|
||||
|
||||
// get system time
|
||||
auto t_now = std::time(nullptr);
|
||||
auto tm_now = *std::gmtime(&t_now);
|
||||
auto tm_str = to_string(std::put_time(&tm_now, "%Y-%m-%dT%H:%M:%SZ"));
|
||||
Value system_time(tm_str.c_str(), alloc);
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("version", StringRef(VERSION_STRING), alloc);
|
||||
info.AddMember("compile_date", StringRef(__DATE__), alloc);
|
||||
info.AddMember("compile_time", StringRef(__TIME__), alloc);
|
||||
info.AddMember("system_time", system_time, alloc);
|
||||
info.AddMember("args", args, alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* memory()
|
||||
*/
|
||||
void Info::memory(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("mem_total", memutils::mem_total(), alloc);
|
||||
info.AddMember("mem_total_used", memutils::mem_total_used(), alloc);
|
||||
info.AddMember("mem_used", memutils::mem_used(), alloc);
|
||||
info.AddMember("vmem_total", memutils::vmem_total(), alloc);
|
||||
info.AddMember("vmem_total_used", memutils::vmem_total_used(), alloc);
|
||||
info.AddMember("vmem_used", memutils::vmem_used(), alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Info : public Module {
|
||||
public:
|
||||
Info();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void avs(Request &req, Response &res);
|
||||
void launcher(Request &req, Response &res);
|
||||
void memory(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user