use std::{ fs, path::{Path, PathBuf}, sync::mpsc::{self, TryRecvError}, }; use anyhow::Result; use crate::{audio::Audio, renderer::GameRenderer, shrooms_vb_core::CoreVB}; pub struct EmulatorBuilder { rom: Option, commands: mpsc::Receiver, } impl EmulatorBuilder { pub fn new() -> (Self, EmulatorClient) { let (queue, commands) = mpsc::channel(); let builder = Self { rom: None, commands, }; let client = EmulatorClient { queue }; (builder, client) } pub fn with_rom(self, path: &Path) -> Self { Self { rom: Some(path.into()), ..self } } pub fn build(self) -> Result { let mut emulator = Emulator::new(self.commands)?; if let Some(path) = self.rom { emulator.load_rom(&path)?; } Ok(emulator) } } pub struct Emulator { sim: CoreVB, audio: Audio, commands: mpsc::Receiver, renderer: Option, } impl Emulator { fn new(commands: mpsc::Receiver) -> Result { Ok(Self { sim: CoreVB::new(), audio: Audio::init()?, commands, renderer: None, }) } pub fn load_rom(&mut self, path: &Path) -> Result<()> { let bytes = fs::read(path)?; self.sim.load_rom(bytes)?; Ok(()) } pub fn run(&mut self) { let mut eye_contents = vec![0u8; 384 * 224 * 2]; let mut audio_samples = vec![]; loop { self.sim.emulate_frame(); if let Some(renderer) = &mut self.renderer { if self.sim.read_pixels(&mut eye_contents) { renderer.render(&eye_contents); } } self.sim.read_samples(&mut audio_samples); if !audio_samples.is_empty() { self.audio.update(&audio_samples); audio_samples.clear(); } loop { match self.commands.try_recv() { Ok(command) => self.handle_command(command), Err(TryRecvError::Empty) => { break; } Err(TryRecvError::Disconnected) => { return; } } } } } fn handle_command(&mut self, command: EmulatorCommand) { match command { EmulatorCommand::SetRenderer(renderer) => { self.renderer = Some(renderer); } EmulatorCommand::PressStart => { self.sim.set_keys(0x1003); } EmulatorCommand::ReleaseStart => { self.sim.set_keys(0x0003); } } } } #[derive(Debug)] pub enum EmulatorCommand { SetRenderer(GameRenderer), PressStart, ReleaseStart, } pub struct EmulatorClient { queue: mpsc::Sender, } impl EmulatorClient { pub fn send_command(&self, command: EmulatorCommand) { if let Err(err) = self.queue.send(command) { eprintln!( "could not send command {:?} as emulator is shut down", err.0 ); } } }