diff --git a/.gitignore b/.gitignore index 6c157d2..701e4ef 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,7 @@ sm64config.txt !/sound/**/*custom*/**/*.aiff !/assets/**/*custom*.bin !/assets/**/*custom*/**/*.bin + +/target +vblank +input diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4c79be1 --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..62d61f0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = [ "./gamebridge" ] diff --git a/doc/gamebridge.org b/doc/gamebridge.org index d2bdfc5..2c2ebcf 100644 --- a/doc/gamebridge.org +++ b/doc/gamebridge.org @@ -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. diff --git a/gamebridge/Cargo.toml b/gamebridge/Cargo.toml index be21824..7bc5eff 100644 --- a/gamebridge/Cargo.toml +++ b/gamebridge/Cargo.toml @@ -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" diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index e7a11a9..c270d81 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -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)?; + } } diff --git a/shell.nix b/shell.nix index d6583eb..feaec63 100644 --- a/shell.nix +++ b/shell.nix @@ -21,10 +21,12 @@ in pkgs.mkShell { # build tools gnumake clang_10 + gdb # gamebridge rustc cargo rls + rustfmt ]; } diff --git a/src/pc/controller/controller_gamebridge.c b/src/pc/controller/controller_gamebridge.c index 222dfe4..a08daf8 100644 --- a/src/pc/controller/controller_gamebridge.c +++ b/src/pc/controller/controller_gamebridge.c @@ -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 = {