mirror of
https://github.com/spicetools/spicetools.git
synced 2026-03-21 18:04:17 -05:00
chore: CRLF to LF
This commit is contained in:
parent
b7a60638a4
commit
6c914266d9
|
|
@ -1,4 +1,4 @@
|
|||
bin/**
|
||||
dist/**
|
||||
docker/**
|
||||
cmake-build*
|
||||
bin/**
|
||||
dist/**
|
||||
docker/**
|
||||
cmake-build*
|
||||
|
|
|
|||
12
Dockerfile
12
Dockerfile
|
|
@ -1,6 +1,6 @@
|
|||
FROM spicetools/deps
|
||||
WORKDIR /src
|
||||
RUN chown user:user /src
|
||||
USER user
|
||||
COPY --chown=user:user . /src
|
||||
CMD ./build_all.sh
|
||||
FROM spicetools/deps
|
||||
WORKDIR /src
|
||||
RUN chown user:user /src
|
||||
USER user
|
||||
COPY --chown=user:user . /src
|
||||
CMD ./build_all.sh
|
||||
|
|
|
|||
522
README.md
522
README.md
|
|
@ -1,261 +1,261 @@
|
|||
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, "E004000000000000"]
|
||||
}
|
||||
```
|
||||
#### 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
|
||||
|
||||
#### 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
|
||||
|
||||
#### LCD
|
||||
- info()
|
||||
- returns information about the serial LCD controller some games use
|
||||
|
||||
## 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.
|
||||
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, "E004000000000000"]
|
||||
}
|
||||
```
|
||||
#### 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
|
||||
|
||||
#### 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
|
||||
|
||||
#### LCD
|
||||
- info()
|
||||
- returns information about the serial LCD controller some games use
|
||||
|
||||
## 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.
|
||||
|
|
|
|||
36
acio/acio.h
36
acio/acio.h
|
|
@ -1,18 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
// globals
|
||||
extern HINSTANCE DLL_INSTANCE;
|
||||
extern std::vector<acio::ACIOModule *> MODULES;
|
||||
|
||||
void attach();
|
||||
void attach_icca();
|
||||
void detach();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
// globals
|
||||
extern HINSTANCE DLL_INSTANCE;
|
||||
extern std::vector<acio::ACIOModule *> MODULES;
|
||||
|
||||
void attach();
|
||||
void attach_icca();
|
||||
void detach();
|
||||
}
|
||||
|
|
|
|||
1020
acio/bi2a/bi2a.cpp
1020
acio/bi2a/bi2a.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BI2AModule : public ACIOModule {
|
||||
public:
|
||||
BI2AModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BI2AModule : public ACIOModule {
|
||||
public:
|
||||
BI2AModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
1192
acio/bmpu/bmpu.cpp
1192
acio/bmpu/bmpu.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BMPUModule : public ACIOModule {
|
||||
public:
|
||||
BMPUModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BMPUModule : public ACIOModule {
|
||||
public:
|
||||
BMPUModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,190 +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);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class CoreModule : public ACIOModule {
|
||||
public:
|
||||
CoreModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class CoreModule : public ACIOModule {
|
||||
public:
|
||||
CoreModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HBHIModule : public ACIOModule {
|
||||
public:
|
||||
HBHIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HBHIModule : public ACIOModule {
|
||||
public:
|
||||
HBHIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HDXSModule : public ACIOModule {
|
||||
public:
|
||||
HDXSModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HDXSModule : public ACIOModule {
|
||||
public:
|
||||
HDXSModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,122 +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);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HGTHModule : public ACIOModule {
|
||||
public:
|
||||
HGTHModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HGTHModule : public ACIOModule {
|
||||
public:
|
||||
HGTHModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,375 +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");
|
||||
}
|
||||
#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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36GModule : public ACIOModule {
|
||||
public:
|
||||
I36GModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36GModule : public ACIOModule {
|
||||
public:
|
||||
I36GModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36IModule : public ACIOModule {
|
||||
public:
|
||||
I36IModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36IModule : public ACIOModule {
|
||||
public:
|
||||
I36IModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +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;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class J32DModule : public ACIOModule {
|
||||
public:
|
||||
J32DModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class J32DModule : public ACIOModule {
|
||||
public:
|
||||
J32DModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,483 +1,483 @@
|
|||
#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/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// 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
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||
KFCA_VOLL = (KFCA_VOLL - 16) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||
KFCA_VOLL = (KFCA_VOLL + 16) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||
KFCA_VOLR = (KFCA_VOLR - 16) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||
KFCA_VOLR = (KFCA_VOLR + 16) & 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
#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/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// 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
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||
KFCA_VOLL = (KFCA_VOLL - 16) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||
KFCA_VOLL = (KFCA_VOLL + 16) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||
KFCA_VOLR = (KFCA_VOLR - 16) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||
KFCA_VOLR = (KFCA_VOLR + 16) & 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +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;
|
||||
};
|
||||
}
|
||||
#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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,238 +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);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class KLPAModule : public ACIOModule {
|
||||
public:
|
||||
KLPAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class KLPAModule : public ACIOModule {
|
||||
public:
|
||||
KLPAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class MDXFModule : public ACIOModule {
|
||||
public:
|
||||
MDXFModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class MDXFModule : public ACIOModule {
|
||||
public:
|
||||
MDXFModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
110
acio/module.h
110
acio/module.h
|
|
@ -1,55 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#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;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +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);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class NDDBModule : public ACIOModule {
|
||||
public:
|
||||
NDDBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class NDDBModule : public ACIOModule {
|
||||
public:
|
||||
NDDBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PANBModule : public ACIOModule {
|
||||
public:
|
||||
PANBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PANBModule : public ACIOModule {
|
||||
public:
|
||||
PANBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
1040
acio/pix/pix.cpp
1040
acio/pix/pix.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PIXModule : public ACIOModule {
|
||||
public:
|
||||
PIXModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PIXModule : public ACIOModule {
|
||||
public:
|
||||
PIXModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJECModule : public ACIOModule {
|
||||
public:
|
||||
PJECModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJECModule : public ACIOModule {
|
||||
public:
|
||||
PJECModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,223 +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);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJEIModule : public ACIOModule {
|
||||
public:
|
||||
PJEIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJEIModule : public ACIOModule {
|
||||
public:
|
||||
PJEIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,205 +1,205 @@
|
|||
#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();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
#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();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
#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();
|
||||
};
|
||||
}
|
||||
#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();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
160
acioemu/bi2a.h
160
acioemu/bi2a.h
|
|
@ -1,80 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,118 +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);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
206
acioemu/device.h
206
acioemu/device.h
|
|
@ -1,103 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,69 +1,69 @@
|
|||
#include "handle.h"
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) {
|
||||
this->com_port = lpCOMPort;
|
||||
}
|
||||
|
||||
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, 2));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool acioemu::ACIOHandle::close() {
|
||||
log_info("acioemu", "Closed {} (ACIO)", ws2s(com_port));
|
||||
|
||||
return true;
|
||||
}
|
||||
#include "handle.h"
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) {
|
||||
this->com_port = lpCOMPort;
|
||||
}
|
||||
|
||||
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, 2));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool acioemu::ACIOHandle::close() {
|
||||
log_info("acioemu", "Closed {} (ACIO)", ws2s(com_port));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
class ACIOHandle : public CustomHandle {
|
||||
private:
|
||||
LPCWSTR com_port;
|
||||
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
public:
|
||||
ACIOHandle(LPCWSTR lpCOMPort);
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
class ACIOHandle : public CustomHandle {
|
||||
private:
|
||||
LPCWSTR com_port;
|
||||
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
public:
|
||||
ACIOHandle(LPCWSTR lpCOMPort);
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,42 @@
|
|||
#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 {
|
||||
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);
|
||||
};
|
||||
}
|
||||
#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 {
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
164
api/controller.h
164
api/controller.h
|
|
@ -1,82 +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;
|
||||
}
|
||||
};
|
||||
}
|
||||
#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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +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);
|
||||
}
|
||||
}
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
134
api/module.h
134
api/module.h
|
|
@ -1,67 +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
|
||||
};
|
||||
}
|
||||
#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
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,177 +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;
|
||||
}
|
||||
}
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +1,82 @@
|
|||
#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 {
|
||||
|
||||
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 (req.params.Size() > 1 && req.params[1].IsUint())
|
||||
quality = req.params[1].GetUint();
|
||||
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);
|
||||
}
|
||||
}
|
||||
#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 {
|
||||
|
||||
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 (req.params.Size() > 1 && req.params[1].IsUint())
|
||||
quality = req.params[1].GetUint();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Capture : public Module {
|
||||
public:
|
||||
Capture();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get_screens(Request &req, Response &res);
|
||||
void get_jpg(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Capture : public Module {
|
||||
public:
|
||||
Capture();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get_screens(Request &req, Response &res);
|
||||
void get_jpg(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,180 +1,180 @@
|
|||
#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" },
|
||||
};
|
||||
|
||||
static inline bool acquire_shutdown_privs() {
|
||||
|
||||
// check if already acquired
|
||||
static bool acquired = false;
|
||||
if (acquired)
|
||||
return true;
|
||||
|
||||
// get process token
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
|
||||
return false;
|
||||
|
||||
// get the LUID for the shutdown privilege
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// get the shutdown privilege for this process
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
||||
|
||||
// check for error
|
||||
bool success = GetLastError() == ERROR_SUCCESS;
|
||||
if (success)
|
||||
acquired = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
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_POWEROFF | 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);
|
||||
}
|
||||
}
|
||||
#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" },
|
||||
};
|
||||
|
||||
static inline bool acquire_shutdown_privs() {
|
||||
|
||||
// check if already acquired
|
||||
static bool acquired = false;
|
||||
if (acquired)
|
||||
return true;
|
||||
|
||||
// get process token
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
|
||||
return false;
|
||||
|
||||
// get the LUID for the shutdown privilege
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// get the shutdown privilege for this process
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
||||
|
||||
// check for error
|
||||
bool success = GetLastError() == ERROR_SUCCESS;
|
||||
if (success)
|
||||
acquired = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
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_POWEROFF | 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +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);
|
||||
};
|
||||
}
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,176 +1,176 @@
|
|||
#include "keypads.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct KeypadMapping {
|
||||
char character;
|
||||
uint16_t state;
|
||||
};
|
||||
|
||||
static KeypadMapping KEYPAD_MAPPINGS[] = {
|
||||
{ '0', 1 << EAM_IO_KEYPAD_0 },
|
||||
{ '1', 1 << EAM_IO_KEYPAD_1 },
|
||||
{ '2', 1 << EAM_IO_KEYPAD_2 },
|
||||
{ '3', 1 << EAM_IO_KEYPAD_3 },
|
||||
{ '4', 1 << EAM_IO_KEYPAD_4 },
|
||||
{ '5', 1 << EAM_IO_KEYPAD_5 },
|
||||
{ '6', 1 << EAM_IO_KEYPAD_6 },
|
||||
{ '7', 1 << EAM_IO_KEYPAD_7 },
|
||||
{ '8', 1 << EAM_IO_KEYPAD_8 },
|
||||
{ '9', 1 << EAM_IO_KEYPAD_9 },
|
||||
{ 'A', 1 << EAM_IO_KEYPAD_00 },
|
||||
{ 'D', 1 << EAM_IO_KEYPAD_DECIMAL },
|
||||
};
|
||||
|
||||
Keypads::Keypads() : Module("keypads") {
|
||||
functions["write"] = std::bind(&Keypads::write, this, _1, _2);
|
||||
functions["set"] = std::bind(&Keypads::set, this, _1, _2);
|
||||
functions["get"] = std::bind(&Keypads::get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(keypad: uint, input: str)
|
||||
*/
|
||||
void Keypads::write(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, "keypad", "uint");
|
||||
}
|
||||
if (!req.params[1].IsString()) {
|
||||
return error_type(res, "input", "string");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto keypad = req.params[0].GetUint();
|
||||
auto input = std::string(req.params[1].GetString());
|
||||
|
||||
// process all chars
|
||||
for (auto c : input) {
|
||||
uint16_t state = 0;
|
||||
|
||||
// find mapping
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, &c, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "char", std::string("") + c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write input to keypad.
|
||||
* We try to make sure it was accepted by waiting a bit more than two frames.
|
||||
*/
|
||||
DWORD sleep_time = 70;
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// cuz fuck DDR
|
||||
sleep_time = 150;
|
||||
}
|
||||
|
||||
// set
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
Sleep(sleep_time);
|
||||
|
||||
// unset
|
||||
eamuse_set_keypad_overrides(keypad, 0);
|
||||
Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set(keypad: uint, key: char, ...)
|
||||
*/
|
||||
void Keypads::set(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// iterate params
|
||||
uint16_t state = 0;
|
||||
auto params = req.params.GetArray();
|
||||
for (size_t i = 1; i < params.Size(); i++) {
|
||||
auto ¶m = params[i];
|
||||
|
||||
// check key
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "key", "char");
|
||||
}
|
||||
if (param.GetStringLength() < 1) {
|
||||
error_size(res, "key", 1);
|
||||
}
|
||||
|
||||
// find mapping
|
||||
auto key = param.GetString();
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, key, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "key", key);
|
||||
}
|
||||
}
|
||||
|
||||
// set keypad state
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* get(keypad: uint)
|
||||
*/
|
||||
void Keypads::get(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// get keypad state
|
||||
auto state = eamuse_get_keypad_state(keypad);
|
||||
|
||||
// add keys to response
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (state & mapping.state) {
|
||||
Value val(&mapping.character, 1);
|
||||
res.add_data(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "keypads.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct KeypadMapping {
|
||||
char character;
|
||||
uint16_t state;
|
||||
};
|
||||
|
||||
static KeypadMapping KEYPAD_MAPPINGS[] = {
|
||||
{ '0', 1 << EAM_IO_KEYPAD_0 },
|
||||
{ '1', 1 << EAM_IO_KEYPAD_1 },
|
||||
{ '2', 1 << EAM_IO_KEYPAD_2 },
|
||||
{ '3', 1 << EAM_IO_KEYPAD_3 },
|
||||
{ '4', 1 << EAM_IO_KEYPAD_4 },
|
||||
{ '5', 1 << EAM_IO_KEYPAD_5 },
|
||||
{ '6', 1 << EAM_IO_KEYPAD_6 },
|
||||
{ '7', 1 << EAM_IO_KEYPAD_7 },
|
||||
{ '8', 1 << EAM_IO_KEYPAD_8 },
|
||||
{ '9', 1 << EAM_IO_KEYPAD_9 },
|
||||
{ 'A', 1 << EAM_IO_KEYPAD_00 },
|
||||
{ 'D', 1 << EAM_IO_KEYPAD_DECIMAL },
|
||||
};
|
||||
|
||||
Keypads::Keypads() : Module("keypads") {
|
||||
functions["write"] = std::bind(&Keypads::write, this, _1, _2);
|
||||
functions["set"] = std::bind(&Keypads::set, this, _1, _2);
|
||||
functions["get"] = std::bind(&Keypads::get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(keypad: uint, input: str)
|
||||
*/
|
||||
void Keypads::write(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, "keypad", "uint");
|
||||
}
|
||||
if (!req.params[1].IsString()) {
|
||||
return error_type(res, "input", "string");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto keypad = req.params[0].GetUint();
|
||||
auto input = std::string(req.params[1].GetString());
|
||||
|
||||
// process all chars
|
||||
for (auto c : input) {
|
||||
uint16_t state = 0;
|
||||
|
||||
// find mapping
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, &c, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "char", std::string("") + c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write input to keypad.
|
||||
* We try to make sure it was accepted by waiting a bit more than two frames.
|
||||
*/
|
||||
DWORD sleep_time = 70;
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// cuz fuck DDR
|
||||
sleep_time = 150;
|
||||
}
|
||||
|
||||
// set
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
Sleep(sleep_time);
|
||||
|
||||
// unset
|
||||
eamuse_set_keypad_overrides(keypad, 0);
|
||||
Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set(keypad: uint, key: char, ...)
|
||||
*/
|
||||
void Keypads::set(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// iterate params
|
||||
uint16_t state = 0;
|
||||
auto params = req.params.GetArray();
|
||||
for (size_t i = 1; i < params.Size(); i++) {
|
||||
auto ¶m = params[i];
|
||||
|
||||
// check key
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "key", "char");
|
||||
}
|
||||
if (param.GetStringLength() < 1) {
|
||||
error_size(res, "key", 1);
|
||||
}
|
||||
|
||||
// find mapping
|
||||
auto key = param.GetString();
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, key, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "key", key);
|
||||
}
|
||||
}
|
||||
|
||||
// set keypad state
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* get(keypad: uint)
|
||||
*/
|
||||
void Keypads::get(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// get keypad state
|
||||
auto state = eamuse_get_keypad_state(keypad);
|
||||
|
||||
// add keys to response
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (state & mapping.state) {
|
||||
Value val(&mapping.character, 1);
|
||||
res.add_data(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Keypads : public Module {
|
||||
public:
|
||||
Keypads();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Keypads : public Module {
|
||||
public:
|
||||
Keypads();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
#include "lcd.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
LCD::LCD() : Module("lcd") {
|
||||
functions["info"] = std::bind(&LCD::info, this, _1, _2);
|
||||
}
|
||||
|
||||
/*
|
||||
* info()
|
||||
*/
|
||||
void LCD::info(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("enabled", games::shared::LCD_ENABLED, alloc);
|
||||
info.AddMember("csm", StringRef(games::shared::LCD_CSM.c_str()), alloc);
|
||||
info.AddMember("bri", games::shared::LCD_BRI, alloc);
|
||||
info.AddMember("con", games::shared::LCD_CON, alloc);
|
||||
info.AddMember("bl", games::shared::LCD_BL, alloc);
|
||||
info.AddMember("red", games::shared::LCD_RED, alloc);
|
||||
info.AddMember("green", games::shared::LCD_GREEN, alloc);
|
||||
info.AddMember("blue", games::shared::LCD_BLUE, alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
}
|
||||
#include "lcd.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
LCD::LCD() : Module("lcd") {
|
||||
functions["info"] = std::bind(&LCD::info, this, _1, _2);
|
||||
}
|
||||
|
||||
/*
|
||||
* info()
|
||||
*/
|
||||
void LCD::info(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("enabled", games::shared::LCD_ENABLED, alloc);
|
||||
info.AddMember("csm", StringRef(games::shared::LCD_CSM.c_str()), alloc);
|
||||
info.AddMember("bri", games::shared::LCD_BRI, alloc);
|
||||
info.AddMember("con", games::shared::LCD_CON, alloc);
|
||||
info.AddMember("bl", games::shared::LCD_BL, alloc);
|
||||
info.AddMember("red", games::shared::LCD_RED, alloc);
|
||||
info.AddMember("green", games::shared::LCD_GREEN, alloc);
|
||||
info.AddMember("blue", games::shared::LCD_BLUE, alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class LCD : public Module {
|
||||
public:
|
||||
LCD();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void info(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class LCD : public Module {
|
||||
public:
|
||||
LCD();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void info(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,179 +1,179 @@
|
|||
#include "lights.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/light.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Lights::Lights() : Module("lights") {
|
||||
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Lights::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
|
||||
lights = games::get_lights(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Lights::read(api::Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each light
|
||||
for (auto &light : *this->lights) {
|
||||
Value state(kArrayType);
|
||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Lights::write(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
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 light_name = param[0].GetString();
|
||||
auto light_state = param[1].GetFloat();
|
||||
|
||||
// write light state
|
||||
if (!this->write_light(light_name, light_state)) {
|
||||
error_unknown(res, "light", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Lights::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (lights != nullptr) {
|
||||
for (auto &light : *this->lights) {
|
||||
light.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 light_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_light_reset(light_name)) {
|
||||
error_unknown(res, "analog", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Lights::write_light(std::string name, float state) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lights::write_light_reset(std::string name) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#include "lights.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/light.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Lights::Lights() : Module("lights") {
|
||||
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Lights::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
|
||||
lights = games::get_lights(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Lights::read(api::Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each light
|
||||
for (auto &light : *this->lights) {
|
||||
Value state(kArrayType);
|
||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Lights::write(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
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 light_name = param[0].GetString();
|
||||
auto light_state = param[1].GetFloat();
|
||||
|
||||
// write light state
|
||||
if (!this->write_light(light_name, light_state)) {
|
||||
error_unknown(res, "light", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Lights::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (lights != nullptr) {
|
||||
for (auto &light : *this->lights) {
|
||||
light.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 light_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_light_reset(light_name)) {
|
||||
error_unknown(res, "analog", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Lights::write_light(std::string name, float state) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lights::write_light_reset(std::string name) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Lights : public Module {
|
||||
public:
|
||||
Lights();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Light> *lights;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_light(std::string name, float state);
|
||||
bool write_light_reset(std::string name);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Lights : public Module {
|
||||
public:
|
||||
Lights();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Light> *lights;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_light(std::string name, float state);
|
||||
bool write_light_reset(std::string name);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,233 +1,233 @@
|
|||
#include "memory.h"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
// global lock to prevent simultaneous access to memory
|
||||
static std::mutex MEMORY_LOCK;
|
||||
|
||||
Memory::Memory() : Module("memory", true) {
|
||||
functions["write"] = std::bind(&Memory::write, this, _1, _2);
|
||||
functions["read"] = std::bind(&Memory::read, this, _1, _2);
|
||||
functions["signature"] = std::bind(&Memory::signature, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(dll_name: str, data: hex, offset: uint)
|
||||
*/
|
||||
void Memory::write(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "data", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto data = req.params[1].GetString();
|
||||
intptr_t offset = req.params[2].GetUint();
|
||||
|
||||
// convert data to bin
|
||||
size_t data_bin_size = strlen(data) / 2;
|
||||
auto data_bin = std::make_unique<uint8_t[]>(data_bin_size);
|
||||
hex2bin(data, data_bin.get());
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
if (offset + data_bin_size >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
auto data_pos = reinterpret_cast<uint8_t *>(module_info.lpBaseOfDll) + offset;
|
||||
|
||||
// replace data
|
||||
memutils::VProtectGuard guard(data_pos, data_bin_size);
|
||||
memcpy(data_pos, data_bin.get(), data_bin_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* read(dll_name: str, offset: uint, size: uint)
|
||||
*/
|
||||
void Memory::read(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "size", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
intptr_t offset = req.params[1].GetUint();
|
||||
auto size = req.params[2].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
auto max = offset + size;
|
||||
if ((size_t) max >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
|
||||
// read memory to hex (without virtual protect)
|
||||
std::string hex = bin2hex((uint8_t*) module_info.lpBaseOfDll + offset, size);
|
||||
Value hex_val(hex.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(hex_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* signature(
|
||||
* dll_name: str,
|
||||
* signature: hex,
|
||||
* replacement: hex,
|
||||
* offset: uint,
|
||||
* usage: uint)
|
||||
*
|
||||
* Both signature and replacement will ignore bytes specified as "??" in the hex string.
|
||||
* The offset specifies the offset between the found signature and the position to write the replacement to.
|
||||
* The resulting integer is the file offset where the replacement was written to.
|
||||
*/
|
||||
void Memory::signature(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 5) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "string");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "signature", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsString() || (req.params[2].GetStringLength() & 1)) {
|
||||
return error_type(res, "replacement", "hex string");
|
||||
}
|
||||
if (!req.params[3].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[4].IsUint()) {
|
||||
return error_type(res, "usage", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto signature = req.params[1].GetString();
|
||||
auto replacement = req.params[2].GetString();
|
||||
auto offset = req.params[3].GetUint();
|
||||
auto usage = req.params[4].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// execute
|
||||
auto result = replace_pattern(
|
||||
module,
|
||||
signature,
|
||||
replacement,
|
||||
offset,
|
||||
usage
|
||||
);
|
||||
|
||||
// check result
|
||||
if (!result) {
|
||||
return error(res, std::string("Pattern not found in memory of ") + dll_name);
|
||||
}
|
||||
|
||||
// convert to offset
|
||||
auto rva = result - reinterpret_cast<intptr_t>(module);
|
||||
result = libutils::rva2offset(dll_path, rva);
|
||||
if (result == -1) {
|
||||
return error(res, "Couldn't convert RVA to file offset.");
|
||||
}
|
||||
|
||||
// add result
|
||||
Value result_val(result);
|
||||
res.add_data(result_val);
|
||||
}
|
||||
}
|
||||
#include "memory.h"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
// global lock to prevent simultaneous access to memory
|
||||
static std::mutex MEMORY_LOCK;
|
||||
|
||||
Memory::Memory() : Module("memory", true) {
|
||||
functions["write"] = std::bind(&Memory::write, this, _1, _2);
|
||||
functions["read"] = std::bind(&Memory::read, this, _1, _2);
|
||||
functions["signature"] = std::bind(&Memory::signature, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(dll_name: str, data: hex, offset: uint)
|
||||
*/
|
||||
void Memory::write(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "data", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto data = req.params[1].GetString();
|
||||
intptr_t offset = req.params[2].GetUint();
|
||||
|
||||
// convert data to bin
|
||||
size_t data_bin_size = strlen(data) / 2;
|
||||
auto data_bin = std::make_unique<uint8_t[]>(data_bin_size);
|
||||
hex2bin(data, data_bin.get());
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
if (offset + data_bin_size >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
auto data_pos = reinterpret_cast<uint8_t *>(module_info.lpBaseOfDll) + offset;
|
||||
|
||||
// replace data
|
||||
memutils::VProtectGuard guard(data_pos, data_bin_size);
|
||||
memcpy(data_pos, data_bin.get(), data_bin_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* read(dll_name: str, offset: uint, size: uint)
|
||||
*/
|
||||
void Memory::read(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "size", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
intptr_t offset = req.params[1].GetUint();
|
||||
auto size = req.params[2].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
auto max = offset + size;
|
||||
if ((size_t) max >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
|
||||
// read memory to hex (without virtual protect)
|
||||
std::string hex = bin2hex((uint8_t*) module_info.lpBaseOfDll + offset, size);
|
||||
Value hex_val(hex.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(hex_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* signature(
|
||||
* dll_name: str,
|
||||
* signature: hex,
|
||||
* replacement: hex,
|
||||
* offset: uint,
|
||||
* usage: uint)
|
||||
*
|
||||
* Both signature and replacement will ignore bytes specified as "??" in the hex string.
|
||||
* The offset specifies the offset between the found signature and the position to write the replacement to.
|
||||
* The resulting integer is the file offset where the replacement was written to.
|
||||
*/
|
||||
void Memory::signature(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 5) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "string");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "signature", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsString() || (req.params[2].GetStringLength() & 1)) {
|
||||
return error_type(res, "replacement", "hex string");
|
||||
}
|
||||
if (!req.params[3].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[4].IsUint()) {
|
||||
return error_type(res, "usage", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto signature = req.params[1].GetString();
|
||||
auto replacement = req.params[2].GetString();
|
||||
auto offset = req.params[3].GetUint();
|
||||
auto usage = req.params[4].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// execute
|
||||
auto result = replace_pattern(
|
||||
module,
|
||||
signature,
|
||||
replacement,
|
||||
offset,
|
||||
usage
|
||||
);
|
||||
|
||||
// check result
|
||||
if (!result) {
|
||||
return error(res, std::string("Pattern not found in memory of ") + dll_name);
|
||||
}
|
||||
|
||||
// convert to offset
|
||||
auto rva = result - reinterpret_cast<intptr_t>(module);
|
||||
result = libutils::rva2offset(dll_path, rva);
|
||||
if (result == -1) {
|
||||
return error(res, "Couldn't convert RVA to file offset.");
|
||||
}
|
||||
|
||||
// add result
|
||||
Value result_val(result);
|
||||
res.add_data(result_val);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Memory : public Module {
|
||||
public:
|
||||
Memory();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void read(Request &req, Response &res);
|
||||
void signature(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Memory : public Module {
|
||||
public:
|
||||
Memory();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void read(Request &req, Response &res);
|
||||
void signature(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
110
api/request.cpp
110
api/request.cpp
|
|
@ -1,55 +1,55 @@
|
|||
#include "request.h"
|
||||
#include "../util/logging.h"
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
Request::Request(rapidjson::Document &document) {
|
||||
Value::MemberIterator it;
|
||||
this->parse_error = false;
|
||||
|
||||
// get ID
|
||||
it = document.FindMember("id");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsUint64()) {
|
||||
log_warning("api", "Request ID is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->id = (*it).value.GetUint64();
|
||||
|
||||
// get module
|
||||
it = document.FindMember("module");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request module is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->module = (*it).value.GetString();
|
||||
|
||||
// get function
|
||||
it = document.FindMember("function");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request function is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->function = (*it).value.GetString();
|
||||
|
||||
// get params
|
||||
it = document.FindMember("params");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsArray()) {
|
||||
log_warning("api", "Request params is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->params = document["params"];
|
||||
|
||||
// log request
|
||||
if (LOGGING) {
|
||||
log_info("api", "new request > id: {}, module: {}, function: {}",
|
||||
this->id, this->module, this->function);
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "request.h"
|
||||
#include "../util/logging.h"
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
Request::Request(rapidjson::Document &document) {
|
||||
Value::MemberIterator it;
|
||||
this->parse_error = false;
|
||||
|
||||
// get ID
|
||||
it = document.FindMember("id");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsUint64()) {
|
||||
log_warning("api", "Request ID is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->id = (*it).value.GetUint64();
|
||||
|
||||
// get module
|
||||
it = document.FindMember("module");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request module is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->module = (*it).value.GetString();
|
||||
|
||||
// get function
|
||||
it = document.FindMember("function");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request function is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->function = (*it).value.GetString();
|
||||
|
||||
// get params
|
||||
it = document.FindMember("params");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsArray()) {
|
||||
log_warning("api", "Request params is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->params = document["params"];
|
||||
|
||||
// log request
|
||||
if (LOGGING) {
|
||||
log_info("api", "new request > id: {}, module: {}, function: {}",
|
||||
this->id, this->module, this->function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class Request {
|
||||
public:
|
||||
uint64_t id;
|
||||
std::string module;
|
||||
std::string function;
|
||||
rapidjson::Value params;
|
||||
bool parse_error;
|
||||
|
||||
Request(rapidjson::Document &document);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class Request {
|
||||
public:
|
||||
uint64_t id;
|
||||
std::string module;
|
||||
std::string function;
|
||||
rapidjson::Value params;
|
||||
bool parse_error;
|
||||
|
||||
Request(rapidjson::Document &document);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# SpiceAPI Arduino Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To use this library, it's recommended to just copy the Arduino project and start from that.
|
||||
# SpiceAPI Arduino Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To use this library, it's recommended to just copy the Arduino project and start from that.
|
||||
|
|
|
|||
|
|
@ -1,65 +1,65 @@
|
|||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < sizeof(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < sizeof(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % sizeof(s_box);
|
||||
b = (b + s_box[a]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % sizeof(s_box)];
|
||||
}
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < sizeof(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < sizeof(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % sizeof(s_box);
|
||||
b = (b + s_box[a]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % sizeof(s_box)];
|
||||
}
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
|
|
|
|||
|
|
@ -1,172 +1,172 @@
|
|||
/*
|
||||
* SpiceAPI Arduino Example Project
|
||||
*
|
||||
* To enable it in SpiceTools, use "-api 1337 -apipass changeme -apiserial COM1" or similar.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SpiceAPI Wrapper Buffer Sizes
|
||||
*
|
||||
* They should be as big as possible to be able to create/parse
|
||||
* some of the bigger requests/responses. Due to dynamic memory
|
||||
* limitations of some weaker devices, if you set them too high
|
||||
* you will probably experience crashes/bugs/problems, one
|
||||
* example would be "Request ID is invalid" in the log.
|
||||
*/
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE 256
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE_STR 256
|
||||
|
||||
/*
|
||||
* WiFi Support
|
||||
* Uncomment to enable the wireless API interface.
|
||||
*/
|
||||
//#define ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* WiFi Settings
|
||||
* You can ignore these if you don't plan on using WiFi
|
||||
*/
|
||||
#ifdef ENABLE_WIFI
|
||||
#include <ESP8266WiFi.h>
|
||||
WiFiClient client;
|
||||
#define SPICEAPI_INTERFACE client
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_HOST "192.168.178.143"
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_PORT 1337
|
||||
#define WIFI_SSID "MySSID"
|
||||
#define WIFI_PASS "MyWifiPassword"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the interface a serial connection will use.
|
||||
* You can change this to another Serial port, e.g. with an
|
||||
* Arduino Mega you can use Serial1/Serial2/Serial3.
|
||||
*/
|
||||
#ifndef ENABLE_WIFI
|
||||
#define SPICEAPI_INTERFACE Serial
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SpiceAPI Includes
|
||||
*
|
||||
* If you have the JSON strings beforehands or want to craft them
|
||||
* manually, you don't have to import the wrappers at all and can
|
||||
* use Connection::request to send and receive raw JSON strings.
|
||||
*/
|
||||
#include "connection.h"
|
||||
#include "wrappers.h"
|
||||
|
||||
/*
|
||||
* This global object represents the API connection.
|
||||
* The first parameter is the buffer size of the JSON string
|
||||
* we're receiving. So a size of 512 will only be able to
|
||||
* hold a JSON of 512 characters maximum.
|
||||
*
|
||||
* An empty password string means no password is being used.
|
||||
* This is the recommended when using Serial only.
|
||||
*/
|
||||
spiceapi::Connection CON(512, "changeme");
|
||||
|
||||
void setup() {
|
||||
|
||||
#ifdef ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* When using WiFi, we can use the Serial interface for debugging.
|
||||
* You can open Serial Monitor and see what IP it gets assigned to.
|
||||
*/
|
||||
Serial.begin(57600);
|
||||
|
||||
// set WiFi mode to station (disables integrated AP)
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
// now try connecting to our Router/AP
|
||||
Serial.print("Connecting");
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
// print debug info over serial
|
||||
Serial.print("\nLocal IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Since the API makes use of the Serial module, we need to
|
||||
* set it up using our preferred baud rate manually.
|
||||
*/
|
||||
SPICEAPI_INTERFACE.begin(57600);
|
||||
while (!SPICEAPI_INTERFACE);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
/*
|
||||
* Here's a few tests/examples on how to make use of the wrappers.
|
||||
*/
|
||||
|
||||
// insert cards for P1/P2
|
||||
spiceapi::card_insert(CON, 0, "E004012345678901");
|
||||
spiceapi::card_insert(CON, 1, "E004012345678902");
|
||||
|
||||
// insert a single coin / multiple coins
|
||||
spiceapi::coin_insert(CON);
|
||||
spiceapi::coin_insert(CON, 3);
|
||||
|
||||
// get the IIDX led ticker text
|
||||
char ticker[9];
|
||||
if (spiceapi::iidx_ticker_get(CON, ticker)) {
|
||||
// if a function returns true, that means success
|
||||
// now we can do something with the ticker as if it was a string
|
||||
//Serial1.println(ticker);
|
||||
}
|
||||
|
||||
// get AVS info
|
||||
spiceapi::InfoAvs avs_info {};
|
||||
if (spiceapi::info_avs(CON, avs_info)) {
|
||||
//Serial1.println(avs_info.model);
|
||||
}
|
||||
|
||||
// enter some keys on P1 keypad (blocks until sequence is entered fully)
|
||||
spiceapi::keypads_write(CON, 0, "1234");
|
||||
|
||||
// get light states
|
||||
spiceapi::LightState lights[8];
|
||||
size_t lights_size = spiceapi::lights_read(CON, lights, 8);
|
||||
for (size_t i = 0; i < lights_size; i++) {
|
||||
auto &light = lights[i];
|
||||
//Serial1.println(light.name);
|
||||
//Serial1.println(light.value);
|
||||
|
||||
// modify value to full bright
|
||||
light.value = 1.f;
|
||||
}
|
||||
|
||||
// send back modified light states
|
||||
spiceapi::lights_write(CON, lights, lights_size);
|
||||
|
||||
// refresh session (generates new crypt key, not that important for serial)
|
||||
spiceapi::control_session_refresh(CON);
|
||||
|
||||
// you can also manually send requests without the wrappers
|
||||
// this avoids json generation, but you still need to parse it in some way
|
||||
const char *answer_json = CON.request(
|
||||
"{"
|
||||
"\"id\": 0,"
|
||||
"\"module\":\"coin\","
|
||||
"\"function\":\"insert\","
|
||||
"\"params\":[]"
|
||||
"}"
|
||||
);
|
||||
|
||||
/*
|
||||
* For more functions/information, just check out wrappers.h yourself.
|
||||
* Have fun :)
|
||||
*/
|
||||
delay(5000);
|
||||
}
|
||||
/*
|
||||
* SpiceAPI Arduino Example Project
|
||||
*
|
||||
* To enable it in SpiceTools, use "-api 1337 -apipass changeme -apiserial COM1" or similar.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SpiceAPI Wrapper Buffer Sizes
|
||||
*
|
||||
* They should be as big as possible to be able to create/parse
|
||||
* some of the bigger requests/responses. Due to dynamic memory
|
||||
* limitations of some weaker devices, if you set them too high
|
||||
* you will probably experience crashes/bugs/problems, one
|
||||
* example would be "Request ID is invalid" in the log.
|
||||
*/
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE 256
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE_STR 256
|
||||
|
||||
/*
|
||||
* WiFi Support
|
||||
* Uncomment to enable the wireless API interface.
|
||||
*/
|
||||
//#define ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* WiFi Settings
|
||||
* You can ignore these if you don't plan on using WiFi
|
||||
*/
|
||||
#ifdef ENABLE_WIFI
|
||||
#include <ESP8266WiFi.h>
|
||||
WiFiClient client;
|
||||
#define SPICEAPI_INTERFACE client
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_HOST "192.168.178.143"
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_PORT 1337
|
||||
#define WIFI_SSID "MySSID"
|
||||
#define WIFI_PASS "MyWifiPassword"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the interface a serial connection will use.
|
||||
* You can change this to another Serial port, e.g. with an
|
||||
* Arduino Mega you can use Serial1/Serial2/Serial3.
|
||||
*/
|
||||
#ifndef ENABLE_WIFI
|
||||
#define SPICEAPI_INTERFACE Serial
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SpiceAPI Includes
|
||||
*
|
||||
* If you have the JSON strings beforehands or want to craft them
|
||||
* manually, you don't have to import the wrappers at all and can
|
||||
* use Connection::request to send and receive raw JSON strings.
|
||||
*/
|
||||
#include "connection.h"
|
||||
#include "wrappers.h"
|
||||
|
||||
/*
|
||||
* This global object represents the API connection.
|
||||
* The first parameter is the buffer size of the JSON string
|
||||
* we're receiving. So a size of 512 will only be able to
|
||||
* hold a JSON of 512 characters maximum.
|
||||
*
|
||||
* An empty password string means no password is being used.
|
||||
* This is the recommended when using Serial only.
|
||||
*/
|
||||
spiceapi::Connection CON(512, "changeme");
|
||||
|
||||
void setup() {
|
||||
|
||||
#ifdef ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* When using WiFi, we can use the Serial interface for debugging.
|
||||
* You can open Serial Monitor and see what IP it gets assigned to.
|
||||
*/
|
||||
Serial.begin(57600);
|
||||
|
||||
// set WiFi mode to station (disables integrated AP)
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
// now try connecting to our Router/AP
|
||||
Serial.print("Connecting");
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
// print debug info over serial
|
||||
Serial.print("\nLocal IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Since the API makes use of the Serial module, we need to
|
||||
* set it up using our preferred baud rate manually.
|
||||
*/
|
||||
SPICEAPI_INTERFACE.begin(57600);
|
||||
while (!SPICEAPI_INTERFACE);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
/*
|
||||
* Here's a few tests/examples on how to make use of the wrappers.
|
||||
*/
|
||||
|
||||
// insert cards for P1/P2
|
||||
spiceapi::card_insert(CON, 0, "E004012345678901");
|
||||
spiceapi::card_insert(CON, 1, "E004012345678902");
|
||||
|
||||
// insert a single coin / multiple coins
|
||||
spiceapi::coin_insert(CON);
|
||||
spiceapi::coin_insert(CON, 3);
|
||||
|
||||
// get the IIDX led ticker text
|
||||
char ticker[9];
|
||||
if (spiceapi::iidx_ticker_get(CON, ticker)) {
|
||||
// if a function returns true, that means success
|
||||
// now we can do something with the ticker as if it was a string
|
||||
//Serial1.println(ticker);
|
||||
}
|
||||
|
||||
// get AVS info
|
||||
spiceapi::InfoAvs avs_info {};
|
||||
if (spiceapi::info_avs(CON, avs_info)) {
|
||||
//Serial1.println(avs_info.model);
|
||||
}
|
||||
|
||||
// enter some keys on P1 keypad (blocks until sequence is entered fully)
|
||||
spiceapi::keypads_write(CON, 0, "1234");
|
||||
|
||||
// get light states
|
||||
spiceapi::LightState lights[8];
|
||||
size_t lights_size = spiceapi::lights_read(CON, lights, 8);
|
||||
for (size_t i = 0; i < lights_size; i++) {
|
||||
auto &light = lights[i];
|
||||
//Serial1.println(light.name);
|
||||
//Serial1.println(light.value);
|
||||
|
||||
// modify value to full bright
|
||||
light.value = 1.f;
|
||||
}
|
||||
|
||||
// send back modified light states
|
||||
spiceapi::lights_write(CON, lights, lights_size);
|
||||
|
||||
// refresh session (generates new crypt key, not that important for serial)
|
||||
spiceapi::control_session_refresh(CON);
|
||||
|
||||
// you can also manually send requests without the wrappers
|
||||
// this avoids json generation, but you still need to parse it in some way
|
||||
const char *answer_json = CON.request(
|
||||
"{"
|
||||
"\"id\": 0,"
|
||||
"\"module\":\"coin\","
|
||||
"\"function\":\"insert\","
|
||||
"\"params\":[]"
|
||||
"}"
|
||||
);
|
||||
|
||||
/*
|
||||
* For more functions/information, just check out wrappers.h yourself.
|
||||
* Have fun :)
|
||||
*/
|
||||
delay(5000);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,8 +1,8 @@
|
|||
# SpiceAPI C++ Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To include it into your project, it's recommended to just copy the
|
||||
files into your source directory.
|
||||
|
||||
To use the wrappers, RapidJSON is required and you might need to
|
||||
adjust the include paths for your project's build.
|
||||
# SpiceAPI C++ Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To include it into your project, it's recommended to just copy the
|
||||
files into your source directory.
|
||||
|
||||
To use the wrappers, RapidJSON is required and you might need to
|
||||
adjust the include paths for your project's build.
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
|
|
|||
|
|
@ -1,181 +1,181 @@
|
|||
#include <iostream>
|
||||
#include <ws2tcpip.h>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
// settings
|
||||
static const size_t RECEIVE_BUFFER_SIZE = 64 * 1024;
|
||||
static const int RECEIVE_TIMEOUT = 1000;
|
||||
}
|
||||
|
||||
spiceapi::Connection::Connection(std::string host, uint16_t port, std::string password) {
|
||||
this->host = host;
|
||||
this->port = port;
|
||||
this->password = password;
|
||||
this->socket = INVALID_SOCKET;
|
||||
this->cipher = nullptr;
|
||||
|
||||
// WSA startup
|
||||
WSADATA wsa_data;
|
||||
int error = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
if (error) {
|
||||
std::cerr << "Failed to start WSA: " << error << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
spiceapi::Connection::~Connection() {
|
||||
|
||||
// clean up
|
||||
if (this->cipher != nullptr)
|
||||
delete this->cipher;
|
||||
|
||||
// cleanup WSA
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void spiceapi::Connection::cipher_alloc() {
|
||||
|
||||
// delete old cipher
|
||||
if (this->cipher != nullptr) {
|
||||
delete this->cipher;
|
||||
this->cipher = nullptr;
|
||||
}
|
||||
|
||||
// create new cipher if password is set
|
||||
if (this->password.length() > 0) {
|
||||
this->cipher = new RC4(
|
||||
(uint8_t *) this->password.c_str(),
|
||||
strlen(this->password.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
bool spiceapi::Connection::check() {
|
||||
int result = 0;
|
||||
|
||||
// check if socket is invalid
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
|
||||
// get all addresses
|
||||
addrinfo *addr_list;
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
if ((result = getaddrinfo(
|
||||
this->host.c_str(),
|
||||
std::to_string(this->port).c_str(),
|
||||
&hints,
|
||||
&addr_list))) {
|
||||
std::cerr << "getaddrinfo failed: " << result << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check all addresses
|
||||
for (addrinfo *addr = addr_list; addr != NULL; addr = addr->ai_next) {
|
||||
|
||||
// try open socket
|
||||
this->socket = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
|
||||
freeaddrinfo(addr_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
// try connect
|
||||
result = connect(this->socket, addr->ai_addr, (int) addr->ai_addrlen);
|
||||
if (result == SOCKET_ERROR) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
continue;
|
||||
}
|
||||
|
||||
// configure socket
|
||||
int opt_val;
|
||||
opt_val = 1;
|
||||
setsockopt(this->socket, IPPROTO_TCP, TCP_NODELAY, (const char*) &opt_val, sizeof(opt_val));
|
||||
opt_val = RECEIVE_TIMEOUT;
|
||||
setsockopt(this->socket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &opt_val, sizeof(opt_val));
|
||||
|
||||
// connection successful
|
||||
this->cipher_alloc();
|
||||
break;
|
||||
}
|
||||
|
||||
// check if successful
|
||||
freeaddrinfo(addr_list);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// socket probably still valid
|
||||
return true;
|
||||
}
|
||||
|
||||
void spiceapi::Connection::change_pass(std::string password) {
|
||||
this->password = password;
|
||||
this->cipher_alloc();
|
||||
}
|
||||
|
||||
std::string spiceapi::Connection::request(std::string json) {
|
||||
|
||||
// check connection
|
||||
if (!this->check())
|
||||
return "";
|
||||
|
||||
// crypt
|
||||
auto json_len = strlen(json.c_str()) + 1;
|
||||
uint8_t* json_data = new uint8_t[json_len];
|
||||
memcpy(json_data, json.c_str(), json_len);
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(json_data, json_len);
|
||||
|
||||
// send
|
||||
auto send_result = send(this->socket, (const char*) json_data, (int) json_len, 0);
|
||||
delete[] json_data;
|
||||
if (send_result == SOCKET_ERROR || send_result < (int) json_len) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// receive
|
||||
uint8_t receive_data[RECEIVE_BUFFER_SIZE];
|
||||
size_t receive_data_len = 0;
|
||||
int receive_result;
|
||||
while ((receive_result = recv(
|
||||
this->socket,
|
||||
(char*) &receive_data[receive_data_len],
|
||||
sizeof(receive_data) - receive_data_len, 0)) > 0) {
|
||||
|
||||
// check for buffer overflow
|
||||
if (receive_data_len + receive_result >= sizeof(receive_data)) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// crypt
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(&receive_data[receive_data_len], (size_t) receive_result);
|
||||
|
||||
// increase received data length
|
||||
receive_data_len += receive_result;
|
||||
|
||||
// check for message end
|
||||
if (receive_data[receive_data_len - 1] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// return resulting json
|
||||
if (receive_data_len > 0) {
|
||||
return std::string((const char *) &receive_data[0], receive_data_len - 1);
|
||||
} else {
|
||||
|
||||
// receive error
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
#include <iostream>
|
||||
#include <ws2tcpip.h>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
// settings
|
||||
static const size_t RECEIVE_BUFFER_SIZE = 64 * 1024;
|
||||
static const int RECEIVE_TIMEOUT = 1000;
|
||||
}
|
||||
|
||||
spiceapi::Connection::Connection(std::string host, uint16_t port, std::string password) {
|
||||
this->host = host;
|
||||
this->port = port;
|
||||
this->password = password;
|
||||
this->socket = INVALID_SOCKET;
|
||||
this->cipher = nullptr;
|
||||
|
||||
// WSA startup
|
||||
WSADATA wsa_data;
|
||||
int error = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
if (error) {
|
||||
std::cerr << "Failed to start WSA: " << error << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
spiceapi::Connection::~Connection() {
|
||||
|
||||
// clean up
|
||||
if (this->cipher != nullptr)
|
||||
delete this->cipher;
|
||||
|
||||
// cleanup WSA
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void spiceapi::Connection::cipher_alloc() {
|
||||
|
||||
// delete old cipher
|
||||
if (this->cipher != nullptr) {
|
||||
delete this->cipher;
|
||||
this->cipher = nullptr;
|
||||
}
|
||||
|
||||
// create new cipher if password is set
|
||||
if (this->password.length() > 0) {
|
||||
this->cipher = new RC4(
|
||||
(uint8_t *) this->password.c_str(),
|
||||
strlen(this->password.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
bool spiceapi::Connection::check() {
|
||||
int result = 0;
|
||||
|
||||
// check if socket is invalid
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
|
||||
// get all addresses
|
||||
addrinfo *addr_list;
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
if ((result = getaddrinfo(
|
||||
this->host.c_str(),
|
||||
std::to_string(this->port).c_str(),
|
||||
&hints,
|
||||
&addr_list))) {
|
||||
std::cerr << "getaddrinfo failed: " << result << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check all addresses
|
||||
for (addrinfo *addr = addr_list; addr != NULL; addr = addr->ai_next) {
|
||||
|
||||
// try open socket
|
||||
this->socket = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
|
||||
freeaddrinfo(addr_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
// try connect
|
||||
result = connect(this->socket, addr->ai_addr, (int) addr->ai_addrlen);
|
||||
if (result == SOCKET_ERROR) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
continue;
|
||||
}
|
||||
|
||||
// configure socket
|
||||
int opt_val;
|
||||
opt_val = 1;
|
||||
setsockopt(this->socket, IPPROTO_TCP, TCP_NODELAY, (const char*) &opt_val, sizeof(opt_val));
|
||||
opt_val = RECEIVE_TIMEOUT;
|
||||
setsockopt(this->socket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &opt_val, sizeof(opt_val));
|
||||
|
||||
// connection successful
|
||||
this->cipher_alloc();
|
||||
break;
|
||||
}
|
||||
|
||||
// check if successful
|
||||
freeaddrinfo(addr_list);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// socket probably still valid
|
||||
return true;
|
||||
}
|
||||
|
||||
void spiceapi::Connection::change_pass(std::string password) {
|
||||
this->password = password;
|
||||
this->cipher_alloc();
|
||||
}
|
||||
|
||||
std::string spiceapi::Connection::request(std::string json) {
|
||||
|
||||
// check connection
|
||||
if (!this->check())
|
||||
return "";
|
||||
|
||||
// crypt
|
||||
auto json_len = strlen(json.c_str()) + 1;
|
||||
uint8_t* json_data = new uint8_t[json_len];
|
||||
memcpy(json_data, json.c_str(), json_len);
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(json_data, json_len);
|
||||
|
||||
// send
|
||||
auto send_result = send(this->socket, (const char*) json_data, (int) json_len, 0);
|
||||
delete[] json_data;
|
||||
if (send_result == SOCKET_ERROR || send_result < (int) json_len) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// receive
|
||||
uint8_t receive_data[RECEIVE_BUFFER_SIZE];
|
||||
size_t receive_data_len = 0;
|
||||
int receive_result;
|
||||
while ((receive_result = recv(
|
||||
this->socket,
|
||||
(char*) &receive_data[receive_data_len],
|
||||
sizeof(receive_data) - receive_data_len, 0)) > 0) {
|
||||
|
||||
// check for buffer overflow
|
||||
if (receive_data_len + receive_result >= sizeof(receive_data)) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// crypt
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(&receive_data[receive_data_len], (size_t) receive_result);
|
||||
|
||||
// increase received data length
|
||||
receive_data_len += receive_result;
|
||||
|
||||
// check for message end
|
||||
if (receive_data[receive_data_len - 1] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// return resulting json
|
||||
if (receive_data_len > 0) {
|
||||
return std::string((const char *) &receive_data[0], receive_data_len - 1);
|
||||
} else {
|
||||
|
||||
// receive error
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,31 @@
|
|||
#ifndef SPICEAPI_CONNECTION_H
|
||||
#define SPICEAPI_CONNECTION_H
|
||||
|
||||
#include <string>
|
||||
#include <winsock2.h>
|
||||
#include "rc4.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class Connection {
|
||||
private:
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
std::string password;
|
||||
SOCKET socket;
|
||||
RC4* cipher;
|
||||
|
||||
void cipher_alloc();
|
||||
|
||||
public:
|
||||
Connection(std::string host, uint16_t port, std::string password = "");
|
||||
~Connection();
|
||||
|
||||
bool check();
|
||||
void change_pass(std::string password);
|
||||
std::string request(std::string json);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_CONNECTION_H
|
||||
#ifndef SPICEAPI_CONNECTION_H
|
||||
#define SPICEAPI_CONNECTION_H
|
||||
|
||||
#include <string>
|
||||
#include <winsock2.h>
|
||||
#include "rc4.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class Connection {
|
||||
private:
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
std::string password;
|
||||
SOCKET socket;
|
||||
RC4* cipher;
|
||||
|
||||
void cipher_alloc();
|
||||
|
||||
public:
|
||||
Connection(std::string host, uint16_t port, std::string password = "");
|
||||
~Connection();
|
||||
|
||||
bool check();
|
||||
void change_pass(std::string password);
|
||||
std::string request(std::string json);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_CONNECTION_H
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
#include "rc4.h"
|
||||
#include <iterator>
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < std::size(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < std::size(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % std::size(s_box);
|
||||
b = (b + s_box[a]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % std::size(s_box)];
|
||||
}
|
||||
}
|
||||
#include "rc4.h"
|
||||
#include <iterator>
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < std::size(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < std::size(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % std::size(s_box);
|
||||
b = (b + s_box[a]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % std::size(s_box)];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,104 +1,104 @@
|
|||
#ifndef SPICEAPI_WRAPPERS_H
|
||||
#define SPICEAPI_WRAPPERS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
struct AnalogState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct ButtonState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct LightState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct InfoAvs {
|
||||
std::string model, dest, spec, rev, ext;
|
||||
};
|
||||
|
||||
struct InfoLauncher {
|
||||
std::string version;
|
||||
std::string compile_date, compile_time, system_time;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
|
||||
struct InfoMemory {
|
||||
uint64_t mem_total, mem_total_used, mem_used;
|
||||
uint64_t vmem_total, vmem_total_used, vmem_used;
|
||||
};
|
||||
|
||||
struct TouchState {
|
||||
uint64_t id;
|
||||
int64_t x, y;
|
||||
};
|
||||
|
||||
struct LCDInfo {
|
||||
bool enabled;
|
||||
std::string csm;
|
||||
uint8_t bri, con, bl, red, green, blue;
|
||||
};
|
||||
|
||||
uint64_t msg_gen_id();
|
||||
|
||||
bool analogs_read(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write_reset(Connection &con, std::vector<AnalogState> &states);
|
||||
|
||||
bool buttons_read(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write_reset(Connection &con, std::vector<ButtonState> &states);
|
||||
|
||||
bool card_insert(Connection &con, size_t index, const char *card_id);
|
||||
|
||||
bool coin_get(Connection &con, int &coins);
|
||||
bool coin_set(Connection &con, int coins);
|
||||
bool coin_insert(Connection &con, int coins=1);
|
||||
bool coin_blocker_get(Connection &con, bool &closed);
|
||||
|
||||
bool control_raise(Connection &con, const char *signal);
|
||||
bool control_exit(Connection &con);
|
||||
bool control_exit(Connection &con, int exit_code);
|
||||
bool control_restart(Connection &con);
|
||||
bool control_session_refresh(Connection &con);
|
||||
bool control_shutdown(Connection &con);
|
||||
bool control_reboot(Connection &con);
|
||||
|
||||
bool iidx_ticker_get(Connection &con, char *ticker);
|
||||
bool iidx_ticker_set(Connection &con, const char *ticker);
|
||||
bool iidx_ticker_reset(Connection &con);
|
||||
|
||||
bool info_avs(Connection &con, InfoAvs &info);
|
||||
bool info_launcher(Connection &con, InfoLauncher &info);
|
||||
bool info_memory(Connection &con, InfoMemory &info);
|
||||
|
||||
bool keypads_write(Connection &con, unsigned int keypad, const char *input);
|
||||
bool keypads_set(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
bool keypads_get(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
|
||||
bool lights_read(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write_reset(Connection &con, std::vector<LightState> &states);
|
||||
|
||||
bool memory_write(Connection &con, const char *dll_name, const char *hex, uint32_t offset);
|
||||
bool memory_read(Connection &con, const char *dll_name, uint32_t offset, uint32_t size, std::string &hex);
|
||||
bool memory_signature(Connection &con, const char *dll_name, const char *signature, const char *replacement,
|
||||
uint32_t offset, uint32_t usage, uint32_t &file_offset);
|
||||
|
||||
bool touch_read(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write_reset(Connection &con, std::vector<TouchState> &states);
|
||||
|
||||
bool lcd_info(Connection &con, LCDInfo &info);
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_WRAPPERS_H
|
||||
#ifndef SPICEAPI_WRAPPERS_H
|
||||
#define SPICEAPI_WRAPPERS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
struct AnalogState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct ButtonState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct LightState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct InfoAvs {
|
||||
std::string model, dest, spec, rev, ext;
|
||||
};
|
||||
|
||||
struct InfoLauncher {
|
||||
std::string version;
|
||||
std::string compile_date, compile_time, system_time;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
|
||||
struct InfoMemory {
|
||||
uint64_t mem_total, mem_total_used, mem_used;
|
||||
uint64_t vmem_total, vmem_total_used, vmem_used;
|
||||
};
|
||||
|
||||
struct TouchState {
|
||||
uint64_t id;
|
||||
int64_t x, y;
|
||||
};
|
||||
|
||||
struct LCDInfo {
|
||||
bool enabled;
|
||||
std::string csm;
|
||||
uint8_t bri, con, bl, red, green, blue;
|
||||
};
|
||||
|
||||
uint64_t msg_gen_id();
|
||||
|
||||
bool analogs_read(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write_reset(Connection &con, std::vector<AnalogState> &states);
|
||||
|
||||
bool buttons_read(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write_reset(Connection &con, std::vector<ButtonState> &states);
|
||||
|
||||
bool card_insert(Connection &con, size_t index, const char *card_id);
|
||||
|
||||
bool coin_get(Connection &con, int &coins);
|
||||
bool coin_set(Connection &con, int coins);
|
||||
bool coin_insert(Connection &con, int coins=1);
|
||||
bool coin_blocker_get(Connection &con, bool &closed);
|
||||
|
||||
bool control_raise(Connection &con, const char *signal);
|
||||
bool control_exit(Connection &con);
|
||||
bool control_exit(Connection &con, int exit_code);
|
||||
bool control_restart(Connection &con);
|
||||
bool control_session_refresh(Connection &con);
|
||||
bool control_shutdown(Connection &con);
|
||||
bool control_reboot(Connection &con);
|
||||
|
||||
bool iidx_ticker_get(Connection &con, char *ticker);
|
||||
bool iidx_ticker_set(Connection &con, const char *ticker);
|
||||
bool iidx_ticker_reset(Connection &con);
|
||||
|
||||
bool info_avs(Connection &con, InfoAvs &info);
|
||||
bool info_launcher(Connection &con, InfoLauncher &info);
|
||||
bool info_memory(Connection &con, InfoMemory &info);
|
||||
|
||||
bool keypads_write(Connection &con, unsigned int keypad, const char *input);
|
||||
bool keypads_set(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
bool keypads_get(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
|
||||
bool lights_read(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write_reset(Connection &con, std::vector<LightState> &states);
|
||||
|
||||
bool memory_write(Connection &con, const char *dll_name, const char *hex, uint32_t offset);
|
||||
bool memory_read(Connection &con, const char *dll_name, uint32_t offset, uint32_t size, std::string &hex);
|
||||
bool memory_signature(Connection &con, const char *dll_name, const char *signature, const char *replacement,
|
||||
uint32_t offset, uint32_t usage, uint32_t &file_offset);
|
||||
|
||||
bool touch_read(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write_reset(Connection &con, std::vector<TouchState> &states);
|
||||
|
||||
bool lcd_info(Connection &con, LCDInfo &info);
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_WRAPPERS_H
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
library spiceapi;
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:html';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
library spiceapi;
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:html';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
|
|
|
|||
|
|
@ -1,193 +1,193 @@
|
|||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 2);
|
||||
static const _BUFFER_SIZE = 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
WebSocket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// initialize socket
|
||||
this._socket = WebSocket("ws://$host:${port + 1}");
|
||||
this._socket.binaryType = "arraybuffer";
|
||||
|
||||
// listen to events
|
||||
this._socket.onOpen.listen((e) async {
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onMessage.listen((e) {
|
||||
|
||||
// get data
|
||||
var data = e.data;
|
||||
if (data is ByteBuffer)
|
||||
data = data.asUint8List();
|
||||
|
||||
// check type
|
||||
if (data is List<int>) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
this.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._socket.onClose.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onError.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.close();
|
||||
_socket = null;
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.sendByteBuffer(Int8List.fromList(jsonEncoded).buffer);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 2);
|
||||
static const _BUFFER_SIZE = 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
WebSocket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// initialize socket
|
||||
this._socket = WebSocket("ws://$host:${port + 1}");
|
||||
this._socket.binaryType = "arraybuffer";
|
||||
|
||||
// listen to events
|
||||
this._socket.onOpen.listen((e) async {
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onMessage.listen((e) {
|
||||
|
||||
// get data
|
||||
var data = e.data;
|
||||
if (data is ByteBuffer)
|
||||
data = data.asUint8List();
|
||||
|
||||
// check type
|
||||
if (data is List<int>) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
this.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._socket.onClose.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onError.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.close();
|
||||
_socket = null;
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.sendByteBuffer(Int8List.fromList(jsonEncoded).buffer);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
part of spiceapi;
|
||||
|
||||
class Request {
|
||||
|
||||
static int _lastID = 0;
|
||||
|
||||
// contents
|
||||
int _id;
|
||||
String _module;
|
||||
String _function;
|
||||
List _params;
|
||||
|
||||
Request(String module, String function, {id}) {
|
||||
|
||||
// automatic ID iteration
|
||||
if (id == null) {
|
||||
if (++_lastID >= pow(2, 32))
|
||||
_lastID = 1;
|
||||
id = _lastID;
|
||||
} else
|
||||
_lastID = id;
|
||||
|
||||
// build contents
|
||||
this._id = id;
|
||||
this._module = module;
|
||||
this._function = function;
|
||||
this._params = List();
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return jsonEncode(
|
||||
{
|
||||
"id": this._id,
|
||||
"module": this._module,
|
||||
"function": this._function,
|
||||
"params": this._params,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void addParam(param) {
|
||||
this._params.add(param);
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class Request {
|
||||
|
||||
static int _lastID = 0;
|
||||
|
||||
// contents
|
||||
int _id;
|
||||
String _module;
|
||||
String _function;
|
||||
List _params;
|
||||
|
||||
Request(String module, String function, {id}) {
|
||||
|
||||
// automatic ID iteration
|
||||
if (id == null) {
|
||||
if (++_lastID >= pow(2, 32))
|
||||
_lastID = 1;
|
||||
id = _lastID;
|
||||
} else
|
||||
_lastID = id;
|
||||
|
||||
// build contents
|
||||
this._id = id;
|
||||
this._module = module;
|
||||
this._function = function;
|
||||
this._params = List();
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return jsonEncode(
|
||||
{
|
||||
"id": this._id,
|
||||
"module": this._module,
|
||||
"function": this._function,
|
||||
"params": this._params,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void addParam(param) {
|
||||
this._params.add(param);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
part of spiceapi;
|
||||
|
||||
class Response {
|
||||
|
||||
String _json;
|
||||
int _id;
|
||||
List _errors;
|
||||
List _data;
|
||||
|
||||
Response.fromJson(String json) {
|
||||
this._json = json;
|
||||
var obj = jsonDecode(json);
|
||||
this._id = obj["id"];
|
||||
this._errors = obj["errors"];
|
||||
this._data = obj["data"];
|
||||
}
|
||||
|
||||
void validate() {
|
||||
|
||||
// check for errors
|
||||
if (_errors.length > 0) {
|
||||
// TODO: add all errors
|
||||
throw APIError(_errors[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
List getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return _json;
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class Response {
|
||||
|
||||
String _json;
|
||||
int _id;
|
||||
List _errors;
|
||||
List _data;
|
||||
|
||||
Response.fromJson(String json) {
|
||||
this._json = json;
|
||||
var obj = jsonDecode(json);
|
||||
this._id = obj["id"];
|
||||
this._errors = obj["errors"];
|
||||
this._data = obj["data"];
|
||||
}
|
||||
|
||||
void validate() {
|
||||
|
||||
// check for errors
|
||||
if (_errors.length > 0) {
|
||||
// TODO: add all errors
|
||||
throw APIError(_errors[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
List getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return _json;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +1,54 @@
|
|||
part of spiceapi;
|
||||
|
||||
class AnalogState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
AnalogState(this.name, this.state);
|
||||
AnalogState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<AnalogState>> analogsRead(Connection con) {
|
||||
var req = Request("analogs", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<AnalogState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(AnalogState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> analogsWrite(Connection con, List<AnalogState> states) {
|
||||
var req = Request("analogs", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> analogsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("analogs", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class AnalogState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
AnalogState(this.name, this.state);
|
||||
AnalogState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<AnalogState>> analogsRead(Connection con) {
|
||||
var req = Request("analogs", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<AnalogState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(AnalogState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> analogsWrite(Connection con, List<AnalogState> states) {
|
||||
var req = Request("analogs", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> analogsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("analogs", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +1,54 @@
|
|||
part of spiceapi;
|
||||
|
||||
class ButtonState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
ButtonState(this.name, this.state);
|
||||
ButtonState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<ButtonState>> buttonsRead(Connection con) {
|
||||
var req = Request("buttons", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<ButtonState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(ButtonState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> buttonsWrite(Connection con, List<ButtonState> states) {
|
||||
var req = Request("buttons", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> buttonsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("buttons", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class ButtonState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
ButtonState(this.name, this.state);
|
||||
ButtonState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<ButtonState>> buttonsRead(Connection con) {
|
||||
var req = Request("buttons", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<ButtonState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(ButtonState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> buttonsWrite(Connection con, List<ButtonState> states) {
|
||||
var req = Request("buttons", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> buttonsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("buttons", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
part of spiceapi;
|
||||
|
||||
Future<void> cardInsert(Connection con, int unit, String cardID) {
|
||||
var req = Request("card", "insert");
|
||||
req.addParam(unit);
|
||||
req.addParam(cardID);
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> cardInsert(Connection con, int unit, String cardID) {
|
||||
var req = Request("card", "insert");
|
||||
req.addParam(unit);
|
||||
req.addParam(cardID);
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
part of spiceapi;
|
||||
|
||||
Future<int> coinGet(Connection con) {
|
||||
var req = Request("coin", "get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> coinSet(Connection con, int amount) {
|
||||
var req = Request("coin", "set");
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> coinInsert(Connection con, [int amount=1]) {
|
||||
var req = Request("coin", "insert");
|
||||
if (amount != 1)
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<int> coinGet(Connection con) {
|
||||
var req = Request("coin", "get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> coinSet(Connection con, int amount) {
|
||||
var req = Request("coin", "set");
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> coinInsert(Connection con, [int amount=1]) {
|
||||
var req = Request("coin", "insert");
|
||||
if (amount != 1)
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
part of spiceapi;
|
||||
|
||||
Future<void> controlRaise(Connection con, String signal) {
|
||||
var req = Request("control", "raise");
|
||||
req.addParam(signal);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlExit(Connection con, int code) {
|
||||
var req = Request("control", "exit");
|
||||
req.addParam(code);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRestart(Connection con) {
|
||||
var req = Request("control", "restart");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRefreshSession(Connection con) {
|
||||
var rnd = new Random();
|
||||
var req = Request("control", "session_refresh", id: rnd.nextInt(pow(2, 32)));
|
||||
return con.request(req).then((res) {
|
||||
con.changePass(res.getData()[0]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> controlShutdown(Connection con) {
|
||||
var req = Request("control", "shutdown");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlReboot(Connection con) {
|
||||
var req = Request("control", "reboot");
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> controlRaise(Connection con, String signal) {
|
||||
var req = Request("control", "raise");
|
||||
req.addParam(signal);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlExit(Connection con, int code) {
|
||||
var req = Request("control", "exit");
|
||||
req.addParam(code);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRestart(Connection con) {
|
||||
var req = Request("control", "restart");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRefreshSession(Connection con) {
|
||||
var rnd = new Random();
|
||||
var req = Request("control", "session_refresh", id: rnd.nextInt(pow(2, 32)));
|
||||
return con.request(req).then((res) {
|
||||
con.changePass(res.getData()[0]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> controlShutdown(Connection con) {
|
||||
var req = Request("control", "shutdown");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlReboot(Connection con) {
|
||||
var req = Request("control", "reboot");
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
part of spiceapi;
|
||||
|
||||
Future<String> iidxTickerGet(Connection con) {
|
||||
var req = Request("iidx", "ticker_get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> iidxTickerSet(Connection con, String text) {
|
||||
var req = Request("iidx", "ticker_set");
|
||||
req.addParam(text);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> iidxTickerReset(Connection con) {
|
||||
var req = Request("iidx", "ticker_reset");
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<String> iidxTickerGet(Connection con) {
|
||||
var req = Request("iidx", "ticker_get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> iidxTickerSet(Connection con, String text) {
|
||||
var req = Request("iidx", "ticker_set");
|
||||
req.addParam(text);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> iidxTickerReset(Connection con) {
|
||||
var req = Request("iidx", "ticker_reset");
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
part of spiceapi;
|
||||
|
||||
Future<Map> infoAVS(Connection con) {
|
||||
var req = Request("info", "avs");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoLauncher(Connection con) {
|
||||
var req = Request("info", "launcher");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoMemory(Connection con) {
|
||||
var req = Request("info", "memory");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<Map> infoAVS(Connection con) {
|
||||
var req = Request("info", "avs");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoLauncher(Connection con) {
|
||||
var req = Request("info", "launcher");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoMemory(Connection con) {
|
||||
var req = Request("info", "memory");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
part of spiceapi;
|
||||
|
||||
Future<void> keypadsWrite(Connection con, int unit, String input) {
|
||||
var req = Request("keypads", "write");
|
||||
req.addParam(unit);
|
||||
req.addParam(input);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> keypadsSet(Connection con, int unit, String buttons) {
|
||||
var req = Request("keypads", "set");
|
||||
req.addParam(unit);
|
||||
for (int i = 0; i < buttons.length; i++)
|
||||
req.addParam(buttons[i]);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> keypadsGet(Connection con, int unit) {
|
||||
var req = Request("keypads", "get");
|
||||
req.addParam(unit);
|
||||
return con.request(req).then((res) {
|
||||
String buttons = "";
|
||||
for (var obj in res.getData()) {
|
||||
buttons += obj;
|
||||
}
|
||||
return buttons;
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> keypadsWrite(Connection con, int unit, String input) {
|
||||
var req = Request("keypads", "write");
|
||||
req.addParam(unit);
|
||||
req.addParam(input);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> keypadsSet(Connection con, int unit, String buttons) {
|
||||
var req = Request("keypads", "set");
|
||||
req.addParam(unit);
|
||||
for (int i = 0; i < buttons.length; i++)
|
||||
req.addParam(buttons[i]);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> keypadsGet(Connection con, int unit) {
|
||||
var req = Request("keypads", "get");
|
||||
req.addParam(unit);
|
||||
return con.request(req).then((res) {
|
||||
String buttons = "";
|
||||
for (var obj in res.getData()) {
|
||||
buttons += obj;
|
||||
}
|
||||
return buttons;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +1,54 @@
|
|||
part of spiceapi;
|
||||
|
||||
class LightState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
LightState(this.name, this.state);
|
||||
LightState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<LightState>> lightsRead(Connection con) {
|
||||
var req = Request("lights", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<LightState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(LightState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> lightsWrite(Connection con, List<LightState> states) {
|
||||
var req = Request("lights", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> lightsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("lights", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class LightState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
LightState(this.name, this.state);
|
||||
LightState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<LightState>> lightsRead(Connection con) {
|
||||
var req = Request("lights", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<LightState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(LightState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> lightsWrite(Connection con, List<LightState> states) {
|
||||
var req = Request("lights", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> lightsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("lights", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
part of spiceapi;
|
||||
|
||||
Future<void> memoryWrite(Connection con,
|
||||
String dllName, String data, int offset) {
|
||||
var req = Request("memory", "write");
|
||||
req.addParam(dllName);
|
||||
req.addParam(data);
|
||||
req.addParam(offset);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> memoryRead(Connection con,
|
||||
String dllName, int offset, int size) {
|
||||
var req = Request("memory", "read");
|
||||
req.addParam(dllName);
|
||||
req.addParam(offset);
|
||||
req.addParam(size);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> memorySignature(Connection con,
|
||||
String dllName, String signature, String replacement,
|
||||
int offset, int usage) {
|
||||
var req = Request("memory", "signature");
|
||||
req.addParam(dllName);
|
||||
req.addParam(signature);
|
||||
req.addParam(replacement);
|
||||
req.addParam(offset);
|
||||
req.addParam(usage);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> memoryWrite(Connection con,
|
||||
String dllName, String data, int offset) {
|
||||
var req = Request("memory", "write");
|
||||
req.addParam(dllName);
|
||||
req.addParam(data);
|
||||
req.addParam(offset);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> memoryRead(Connection con,
|
||||
String dllName, int offset, int size) {
|
||||
var req = Request("memory", "read");
|
||||
req.addParam(dllName);
|
||||
req.addParam(offset);
|
||||
req.addParam(size);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> memorySignature(Connection con,
|
||||
String dllName, String signature, String replacement,
|
||||
int offset, int usage) {
|
||||
var req = Request("memory", "signature");
|
||||
req.addParam(dllName);
|
||||
req.addParam(signature);
|
||||
req.addParam(replacement);
|
||||
req.addParam(offset);
|
||||
req.addParam(usage);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,53 +1,53 @@
|
|||
part of spiceapi;
|
||||
|
||||
class TouchState {
|
||||
int id;
|
||||
int x, y;
|
||||
|
||||
TouchState(this.id, this.x, this.y);
|
||||
}
|
||||
|
||||
Future<List<TouchState>> touchRead(Connection con) {
|
||||
var req = Request("touch", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<TouchState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(TouchState(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> touchWrite(Connection con, List<TouchState> states) {
|
||||
var req = Request("touch", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.id,
|
||||
state.x,
|
||||
state.y
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteReset(Connection con, List<int> touchIDs) {
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var id in touchIDs)
|
||||
req.addParam(id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class TouchState {
|
||||
int id;
|
||||
int x, y;
|
||||
|
||||
TouchState(this.id, this.x, this.y);
|
||||
}
|
||||
|
||||
Future<List<TouchState>> touchRead(Connection con) {
|
||||
var req = Request("touch", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<TouchState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(TouchState(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> touchWrite(Connection con, List<TouchState> states) {
|
||||
var req = Request("touch", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.id,
|
||||
state.x,
|
||||
state.y
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteReset(Connection con, List<int> touchIDs) {
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var id in touchIDs)
|
||||
req.addParam(id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
library spiceapi;
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/capture.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
library spiceapi;
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/capture.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
|
|
|
|||
|
|
@ -1,192 +1,192 @@
|
|||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 3);
|
||||
static const _BUFFER_SIZE = 1024 * 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
Socket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// connect
|
||||
Socket.connect(host, port, timeout: _TIMEOUT).then((socket) async {
|
||||
|
||||
// remember socket
|
||||
this._socket = socket;
|
||||
|
||||
// listen to data
|
||||
socket.listen((data) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on listen error
|
||||
this.dispose();
|
||||
|
||||
}, onDone: () {
|
||||
|
||||
// dispose on listen done
|
||||
this.dispose();
|
||||
|
||||
});
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on connection error
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.destroy();
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.add(jsonEncoded);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 3);
|
||||
static const _BUFFER_SIZE = 1024 * 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
Socket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// connect
|
||||
Socket.connect(host, port, timeout: _TIMEOUT).then((socket) async {
|
||||
|
||||
// remember socket
|
||||
this._socket = socket;
|
||||
|
||||
// listen to data
|
||||
socket.listen((data) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on listen error
|
||||
this.dispose();
|
||||
|
||||
}, onDone: () {
|
||||
|
||||
// dispose on listen done
|
||||
this.dispose();
|
||||
|
||||
});
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on connection error
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.destroy();
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.add(jsonEncoded);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user