gamebridge: implement simple protocol

This commit is contained in:
Cadey Ratio 2020-05-08 17:54:57 -04:00
parent 7be9a1aef6
commit a8ecacd2ca
8 changed files with 248 additions and 9 deletions

4
.gitignore vendored
View File

@ -71,3 +71,7 @@ sm64config.txt
!/sound/**/*custom*/**/*.aiff
!/assets/**/*custom*.bin
!/assets/**/*custom*/**/*.bin
/target
vblank
input

183
Cargo.lock generated Normal file
View File

@ -0,0 +1,183 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "gamebridge"
version = "0.1.0"
dependencies = [
"anyhow",
"log",
"pretty_env_logger",
]
[[package]]
name = "hermit-abi"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger",
"log",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "regex"
version = "1.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
"winapi-util",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

2
Cargo.toml Normal file
View File

@ -0,0 +1,2 @@
[workspace]
members = [ "./gamebridge" ]

View File

@ -30,7 +30,7 @@ The protocol between the game and the bridge will be as follows (based on the
The game and the bridge will both need to take care that the files are opened in
*unbuffered I/O modes*. This can be done with
=setvbuf( f, (char *)NULL, _IONBF, 0 );= in C and is the default in Rust.
=setvbuf(f, (char *)NULL, _IONBF, 0);= in C and is the default in Rust.
The first one will be called =vblank= and will then be opened by the game in
write mode when it starts. The bridge will open this fifo in read mode.
@ -38,10 +38,13 @@ write mode when it starts. The bridge will open this fifo in read mode.
The second one will be called =input= and will be opened by the game in read
mode. The bridge will open this fifo in write mode.
On every frame, the game will write the text ="OK\n"= to the vblank fifo. This
will signal the bridge that it should write four bytes of data to the input
On every frame, the game *MUST* write the text ="OK\n"= to the vblank fifo. This
will signal the bridge that it *MUST* write four bytes of data to the input
fifo, conforming to the [[http://tasvideos.org/EmulatorResources/Mupen/M64.html#ControllerData][Controller Data]] specification of the Mupen64 format.
This data will be interpreted by the game as actions for Mario to take.
The bridge *MUST* block on waiting for the vblank fifo to be written to by the
game and the game *MUST* block on the input fifo being written to by the bridge.
When the game is exiting, the game *SHOULD* write ="BYE"= to the vblank fifo.
When the bridge recieves a ="BYE"= message, it *MUST* exit.

View File

@ -7,3 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
log = "0.4"
pretty_env_logger = "0.4"

View File

@ -1,3 +1,29 @@
fn main() {
println!("Hello, world!");
use log::{debug, info, warn};
use std::{
fs::{File, OpenOptions},
io::{Read, Write},
str::from_utf8,
};
fn main() -> anyhow::Result<()> {
pretty_env_logger::try_init()?;
let controller_data = [0; 4];
let mut vblank = File::open("vblank")?;
let mut input = OpenOptions::new().write(true).open("input")?;
info!("[gamebridge::rust] ready");
loop {
let mut data = [0; 3];
info!("waiting for vblank");
vblank.read(&mut data)?;
let str = from_utf8(&data)?;
debug!("got data: {}", str);
if str == "BYE" {
warn!("asked to exit by the game");
return Ok(());
}
input.write(&controller_data)?;
}
}

View File

@ -21,10 +21,12 @@ in pkgs.mkShell {
# build tools
gnumake
clang_10
gdb
# gamebridge
rustc
cargo
rls
rustfmt
];
}

View File

@ -17,12 +17,21 @@ static FILE *input;
#define vblank_fname "vblank"
#define input_fname "input"
#define ok "OK\n"
#define bye "BYE"
static void gamebridge_close(void) {
fwrite(bye, 1, strlen(bye), vblank);
}
static void gamebridge_init(void) {
if (!configGameBridge) {
return;
}
printf("[gamebridge] starting...\n");
fflush(stdout);
unlink(vblank_fname);
unlink(input_fname);
@ -40,13 +49,18 @@ static void gamebridge_init(void) {
assert(result < 0);
}
vblank = fopen(vblank_fname, "w");
input = fopen(input_fname, "rb");
vblank = fopen(vblank_fname, "w+");
input = fopen(input_fname, "rb+");
assert(vblank);
assert(input);
setvbuf(vblank, NULL, _IONBF, 0);
setvbuf(input, NULL, _IONBF, 0);
printf("[gamebridge] starting rust daemon\n");
fflush(stdout);
system("./target/debug/gamebridge &");
atexit(gamebridge_close);
}
static void gamebridge_read(OSContPad *pad) {
@ -54,13 +68,15 @@ static void gamebridge_read(OSContPad *pad) {
return;
}
char* ok = "OK\n";
fwrite(ok, 1, sizeof(ok), vblank);
printf("[gamebridge] waiting for input\n");
fwrite(ok, 1, strlen(ok), vblank);
uint8_t bytes[4] = {0};
fread(bytes, 1, 4, input);
pad->button = (bytes[0] << 8) | bytes[1];
pad->stick_x = bytes[2];
pad->stick_y = bytes[3];
printf("[gamebridge] %02x%02x %02x%02x\n", bytes[0], bytes[1], bytes[2], bytes[3]);
fflush(stdout);
}
struct ControllerAPI controller_gamebridge = {