use std::cell::RefCell; use std::rc::Rc; use std::path::Path; use std::time; #[macro_use] extern crate clap; #[macro_use] extern crate rustboyadvance_ng; use rustboyadvance_ng::core::keypad; use rustboyadvance_ng::prelude::*; use rustboyadvance_ng::util::FpsCounter; use bit::BitIndex; use minifb; use minifb::{Key, Window, WindowOptions}; struct MiniFb { window: minifb::Window, } impl VideoInterface for MiniFb { fn render(&mut self, buffer: &[u32]) { self.window.update_with_buffer(buffer).unwrap(); } } impl InputInterface for MiniFb { fn poll(&mut self) -> u16 { let mut keyinput = keypad::KEYINPUT_ALL_RELEASED; keyinput.set_bit(keypad::Keys::Up as usize, !self.window.is_key_down(Key::Up)); keyinput.set_bit( keypad::Keys::Down as usize, !self.window.is_key_down(Key::Down), ); keyinput.set_bit( keypad::Keys::Left as usize, !self.window.is_key_down(Key::Left), ); keyinput.set_bit( keypad::Keys::Right as usize, !self.window.is_key_down(Key::Right), ); keyinput.set_bit( keypad::Keys::ButtonB as usize, !self.window.is_key_down(Key::Z), ); keyinput.set_bit( keypad::Keys::ButtonA as usize, !self.window.is_key_down(Key::X), ); keyinput.set_bit( keypad::Keys::Start as usize, !self.window.is_key_down(Key::Enter), ); keyinput.set_bit( keypad::Keys::Select as usize, !self.window.is_key_down(Key::Space), ); keyinput.set_bit( keypad::Keys::ButtonL as usize, !self.window.is_key_down(Key::A), ); keyinput.set_bit( keypad::Keys::ButtonR as usize, !self.window.is_key_down(Key::S), ); keyinput } } impl AudioInterface for MiniFb { fn get_sample_rate(&self) -> i32 { 0 } } fn main() { let yaml = load_yaml!("cli.yml"); let matches = clap::App::from_yaml(yaml).get_matches(); let skip_bios = matches.occurrences_of("skip_bios") != 0; let no_framerate_limit = matches.occurrences_of("no_framerate_limit") != 0; let bios_path = Path::new(matches.value_of("bios").unwrap_or_default()); let rom_path = Path::new(matches.value_of("game_rom").unwrap()); let rom_name = rom_path.file_name().unwrap().to_str().unwrap(); let bios_bin = read_bin_file(bios_path).unwrap(); let cart = GamepakBuilder::new().file(rom_path).build().unwrap(); let minifb = Rc::new(RefCell::new(MiniFb { window: Window::new( "rustboyadvance-ng", 240, 160, WindowOptions { borderless: true, scale: minifb::Scale::X4, ..Default::default() }, ) .unwrap(), })); let mut fps_counter = FpsCounter::default(); let mut gba = GameBoyAdvance::new( bios_bin.into_boxed_slice(), cart, minifb.clone(), minifb.clone(), minifb.clone(), ); if skip_bios { gba.skip_bios(); } let frame_time = time::Duration::new(0, 1_000_000_000u32 / 60); loop { let start_time = time::Instant::now(); gba.frame(); if let Some(fps) = fps_counter.tick() { let title = format!("{} ({} fps)", rom_name, fps); // video.borrow_mut().set_window_title(&title); minifb.borrow_mut().window.set_title(&title); } if !no_framerate_limit { let time_passed = start_time.elapsed(); let delay = frame_time.checked_sub(time_passed); match delay { None => {} Some(delay) => { ::std::thread::sleep(delay); } }; } } }