extern crate ferretro_components; extern crate sdl2; use std::cell::RefCell; use std::ffi::CStr; use std::path::PathBuf; use std::pin::Pin; use std::rc::Rc; use sdl2::render::{WindowCanvas, Texture, TextureCreator}; use sdl2::image::ImageRWops; use sdl2::surface::Surface; use sdl2::video::WindowContext; use structopt::StructOpt; use ferretro_components::base::{ControlFlow, RetroComponentBase}; use ferretro_components::prelude::LibretroWrapperAccess; use ferretro_components::provided::{ sdl2::{Sdl2SurfaceComponent, SimpleSdl2AudioComponent}, stdlib::{SleepFramerateLimitComponent, PathBufComponent}, }; type Result = std::result::Result>; #[derive(StructOpt)] struct Opt { /// Core module to use. #[structopt(short, long, parse(from_os_str))] core: PathBuf, /// ROM to load using the core. #[structopt(short, long, parse(from_os_str))] rom: PathBuf, /// System directory, often containing BIOS files #[structopt(short, long, parse(from_os_str))] system: Option, } struct Zretro { emu: Pin>, canvas: WindowCanvas, font: Surface<'static>, sdl2surf_comp: Rc>, } const FONT_PNG: &'static [u8] = include_bytes!("lifont.png"); impl Zretro { fn new(opt: Opt) -> Result { let mut emu = RetroComponentBase::new(&opt.core); let geometry = emu.libretro_core().get_system_av_info().geometry; let sys_info = emu.libretro_core().get_system_info(); let title = format!( "zretro - {}", unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() ); let mut sdl_context = sdl2::init()?; let window = sdl_context .video()? .window(title.as_str(), geometry.base_width, geometry.base_height) .borderless() .build()?; let mut canvas = window.into_canvas().build()?; let sdl2surf_comp = Rc::new(RefCell::new( Sdl2SurfaceComponent::new(emu.libretro_core())? )); emu.register_component(sdl2surf_comp.clone()); let sdl2_audio = SimpleSdl2AudioComponent::new(&mut sdl_context, emu.libretro_core()); emu.register_component(sdl2_audio); let sleep_fps = SleepFramerateLimitComponent::new(emu.libretro_core()); emu.register_component(sleep_fps); emu.register_component(PathBufComponent { sys_path: opt.system.clone(), libretro_path: Some(opt.core.to_path_buf()), core_assets_path: None, save_path: Some(std::env::temp_dir()), }); emu.init().unwrap(); emu.load_game(&opt.rom).unwrap(); let rwops = sdl2::rwops::RWops::from_bytes(FONT_PNG)?; let font_shortlived = rwops.load_png()?; // there's a nonsemantic inheritance of lifetime from ImageRWops::load_png. TODO: pr let font = unsafe { let ptr = font_shortlived.raw(); std::mem::forget(font_shortlived); Surface::from_ll(ptr) }; Ok(Zretro { emu, canvas, font, sdl2surf_comp, }) } fn run(&mut self) -> Result<()> { let tc = self.canvas.texture_creator(); let font_tx = tc.create_texture_from_surface(&self.font)?; let mut font_rect = self.font.rect(); font_rect.set_height(64); while let ControlFlow::Continue = self.emu.run() { let ref_mut = RefCell::try_borrow_mut(&self.sdl2surf_comp)?; let mut surface = ref_mut.surface(); let texture = tc.create_texture_from_surface(surface)?; self.canvas.clear(); self.canvas.copy(&texture, None, None)?; self.canvas.copy(&font_tx, font_rect, font_rect)?; self.canvas.present(); } Ok(()) } } fn main() -> Result<()> { let opt: Opt = Opt::from_args(); let mut frontend = Zretro::new(opt)?; frontend.run()?; Ok(()) }