gamebridge: Twitch Control #2

Merged
cadey merged 29 commits from gamebridge into master 2020-05-11 22:40:05 +00:00
8 changed files with 248 additions and 9 deletions
Showing only changes of commit a8ecacd2ca - Show all commits

4
.gitignore vendored
View File

@ -71,3 +71,7 @@ sm64config.txt
!/sound/**/*custom*/**/*.aiff !/sound/**/*custom*/**/*.aiff
!/assets/**/*custom*.bin !/assets/**/*custom*.bin
!/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

@ -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 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. 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 On every frame, the game *MUST* 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 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. 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. 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 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. 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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0"
log = "0.4"
pretty_env_logger = "0.4"

View File

@ -1,3 +1,29 @@
fn main() { use log::{debug, info, warn};
println!("Hello, world!"); 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 # build tools
gnumake gnumake
clang_10 clang_10
gdb
# gamebridge # gamebridge
rustc rustc
cargo cargo
rls rls
rustfmt
]; ];
} }

View File

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