diff --git a/Cargo.lock b/Cargo.lock index 2edeed5..3944f49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -207,7 +213,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -417,6 +423,36 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -595,6 +631,27 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -840,6 +897,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "imgui" version = "0.12.0" @@ -903,6 +969,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1105,6 +1180,29 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "native-dialog" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e7038885d2aeab236bd60da9e159a5967b47cde3292da3b15ff1bec27c039f" +dependencies = [ + "ascii", + "block", + "cocoa", + "core-foundation", + "dirs-next", + "objc", + "objc-foundation", + "objc_id", + "once_cell", + "raw-window-handle 0.5.2", + "thiserror", + "versions", + "wfd", + "which", + "winapi", +] + [[package]] name = "ndk" version = "0.8.0" @@ -1130,7 +1228,7 @@ dependencies = [ "log", "ndk-sys 0.6.0+11769913", "num_enum", - "raw-window-handle", + "raw-window-handle 0.6.2", "thiserror", ] @@ -1236,6 +1334,17 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -1439,6 +1548,15 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "oboe" version = "0.6.1" @@ -1637,6 +1755,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -1670,6 +1794,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.1" @@ -1829,7 +1964,8 @@ dependencies = [ "imgui", "imgui-wgpu", "imgui-winit-support", - "itertools", + "itertools 0.13.0", + "native-dialog", "num-derive", "num-traits", "pollster", @@ -2091,6 +2227,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "versions" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73a36bc44e3039f51fbee93e39f41225f6b17b380eb70cc2aab942df06b34dd" +dependencies = [ + "itertools 0.11.0", + "nom", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -2303,6 +2449,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wfd" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "wgpu" version = "22.1.0" @@ -2317,7 +2473,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.6.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -2345,7 +2501,7 @@ dependencies = [ "once_cell", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.6.2", "rustc-hash", "smallvec", "thiserror", @@ -2387,7 +2543,7 @@ dependencies = [ "parking_lot", "profiling", "range-alloc", - "raw-window-handle", + "raw-window-handle 0.6.2", "renderdoc-sys", "rustc-hash", "smallvec", @@ -2409,6 +2565,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "widestring" version = "1.1.0" @@ -2729,7 +2897,7 @@ dependencies = [ "orbclient", "percent-encoding", "pin-project", - "raw-window-handle", + "raw-window-handle 0.6.2", "redox_syscall 0.4.1", "rustix", "sctk-adwaita", diff --git a/Cargo.toml b/Cargo.toml index 6a848fd..e2c6317 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ imgui = "0.12" imgui-wgpu = { git = "https://github.com/Yatekii/imgui-wgpu-rs", rev = "2edd348" } imgui-winit-support = "0.13" itertools = "0.13" +native-dialog = "0.7" num-derive = "0.4" num-traits = "0.2" pollster = "0.4" diff --git a/src/app.rs b/src/app.rs index ae029ef..3403b3b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -350,18 +350,28 @@ impl ApplicationHandler for App { height = ui.window_size()[1]; ui.menu("ROM", || { if ui.menu_item("Open ROM") { - println!("clicked"); + let rom = native_dialog::FileDialog::new() + .add_filter("Virtual Boy ROMs", &["vb", "vbrom"]) + .show_open_single_file() + .unwrap(); + if let Some(path) = rom { + self.client.send_command(EmulatorCommand::LoadGame(path)); + } } if ui.menu_item("Quit") { event_loop.exit(); } }); ui.menu("Emulation", || { - if ui.menu_item("Pause") { - println!("clicked"); + if self.client.is_running() { + if ui.menu_item("Pause") { + self.client.send_command(EmulatorCommand::Pause); + } + } else if ui.menu_item("Resume") { + self.client.send_command(EmulatorCommand::Resume); } if ui.menu_item("Reset") { - println!("clicked"); + self.client.send_command(EmulatorCommand::Reset); } }); }); diff --git a/src/emulator.rs b/src/emulator.rs index e1a4836..e87a9b4 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -1,7 +1,11 @@ use std::{ fs, path::{Path, PathBuf}, - sync::mpsc::{self, TryRecvError}, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{self, TryRecvError}, + Arc, + }, }; use anyhow::Result; @@ -11,6 +15,7 @@ use crate::{audio::Audio, renderer::GameRenderer, shrooms_vb_core::CoreVB}; pub struct EmulatorBuilder { rom: Option, commands: mpsc::Receiver, + running: Arc, } impl EmulatorBuilder { @@ -19,8 +24,12 @@ impl EmulatorBuilder { let builder = Self { rom: None, commands, + running: Arc::new(AtomicBool::new(false)), + }; + let client = EmulatorClient { + queue, + running: builder.running.clone(), }; - let client = EmulatorClient { queue }; (builder, client) } @@ -32,7 +41,7 @@ impl EmulatorBuilder { } pub fn build(self) -> Result { - let mut emulator = Emulator::new(self.commands)?; + let mut emulator = Emulator::new(self.commands, self.running)?; if let Some(path) = self.rom { emulator.load_rom(&path)?; } @@ -45,21 +54,28 @@ pub struct Emulator { audio: Audio, commands: mpsc::Receiver, renderer: Option, + running: Arc, + has_game: bool, } impl Emulator { - fn new(commands: mpsc::Receiver) -> Result { + fn new(commands: mpsc::Receiver, running: Arc) -> Result { Ok(Self { sim: CoreVB::new(), audio: Audio::init()?, commands, renderer: None, + running, + has_game: false, }) } pub fn load_rom(&mut self, path: &Path) -> Result<()> { let bytes = fs::read(path)?; + self.sim.reset(); self.sim.load_rom(bytes)?; + self.has_game = true; + self.running.store(true, Ordering::Release); Ok(()) } @@ -67,7 +83,9 @@ impl Emulator { let mut eye_contents = vec![0u8; 384 * 224 * 2]; let mut audio_samples = vec![]; loop { - self.sim.emulate_frame(); + if self.running.load(Ordering::Acquire) { + self.sim.emulate_frame(); + } if let Some(renderer) = &mut self.renderer { if self.sim.read_pixels(&mut eye_contents) { renderer.render(&eye_contents); @@ -97,6 +115,23 @@ impl Emulator { EmulatorCommand::SetRenderer(renderer) => { self.renderer = Some(renderer); } + EmulatorCommand::LoadGame(path) => { + if let Err(error) = self.load_rom(&path) { + eprintln!("error loading rom: {}", error); + } + } + EmulatorCommand::Pause => { + self.running.store(false, Ordering::Release); + } + EmulatorCommand::Resume => { + if self.has_game { + self.running.store(true, Ordering::Relaxed); + } + } + EmulatorCommand::Reset => { + self.sim.reset(); + self.running.store(true, Ordering::Release); + } EmulatorCommand::PressStart => { self.sim.set_keys(0x1003); } @@ -110,15 +145,23 @@ impl Emulator { #[derive(Debug)] pub enum EmulatorCommand { SetRenderer(GameRenderer), + LoadGame(PathBuf), + Pause, + Resume, + Reset, PressStart, ReleaseStart, } pub struct EmulatorClient { queue: mpsc::Sender, + running: Arc, } impl EmulatorClient { + pub fn is_running(&self) -> bool { + self.running.load(Ordering::Acquire) + } pub fn send_command(&self, command: EmulatorCommand) { if let Err(err) = self.queue.send(command) { eprintln!( diff --git a/src/main.rs b/src/main.rs index b066de0..474bad5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, thread}; +use std::{path::PathBuf, process, thread}; use anyhow::Result; use app::App; @@ -14,16 +14,24 @@ mod shrooms_vb_core; #[derive(Parser)] struct Args { - rom: PathBuf, + rom: Option, } fn main() -> Result<()> { let args = Args::parse(); - let (builder, client) = EmulatorBuilder::new(); - let builder = builder.with_rom(&args.rom); + let (mut builder, client) = EmulatorBuilder::new(); + if let Some(path) = args.rom { + builder = builder.with_rom(&path); + } thread::spawn(move || { - let mut emulator = builder.build().unwrap(); + let mut emulator = match builder.build() { + Ok(e) => e, + Err(err) => { + eprintln!("Error initializing emulator: {err}"); + process::exit(1); + } + }; emulator.run(); }); diff --git a/src/shrooms_vb_core.rs b/src/shrooms_vb_core.rs index 90acc21..19ce04b 100644 --- a/src/shrooms_vb_core.rs +++ b/src/shrooms_vb_core.rs @@ -119,6 +119,10 @@ impl CoreVB { CoreVB { sim } } + pub fn reset(&mut self) { + unsafe { vb_reset(self.sim) }; + } + pub fn load_rom(&mut self, rom: Vec) -> Result<()> { self.unload_rom();