extern crate crossbeam_channel; extern crate ferretro_components; #[cfg(feature = "ffmpeg_comp")] extern crate ffmpeg_next as ffmpeg; extern crate sdl2; use std::path::PathBuf; use sdl2::video::FullscreenType; use structopt::StructOpt; use ferretro_components::base::ControlFlow; use ferretro_components::prelude::*; use ferretro_components::provided::{ sdl2::*, stdlib::*, }; #[cfg(feature = "ffmpeg_comp")] use ferretro_components::provided::ffmpeg::FfmpegComponent; #[derive(StructOpt)] struct Opt { /// Core module to use. #[structopt(short, long, parse(from_os_str))] core: PathBuf, /// ROM to load using the core. #[structopt(parse(from_os_str))] rom: PathBuf, /// Save state to load at startup. #[structopt(long, parse(from_os_str))] state: Option, /// System directory, often containing BIOS files #[structopt(short, long, parse(from_os_str))] system: Option, /// Recorded video to write. #[cfg(feature = "ffmpeg_comp")] #[structopt(long, parse(from_os_str))] record_video: Option, /// Disable OpenGL context creation. #[structopt(long)] no_opengl: bool, /// Fullscreen (borderless). #[structopt(short, long)] fullscreen: bool, /// Print core-provided system information to stderr. #[structopt(long)] sys_info: bool, /// Trace all API calls to stderr. #[structopt(long)] trace_api: bool, } pub fn main() -> Result<(), Box> { let opt: Opt = Opt::from_args(); let mut emu = RetroComponentBase::new(&opt.core); let mut sdl_context = sdl2::init()?; let mut stderr_log = StderrLogComponent::default(); stderr_log.prefix = "{log} ".to_string(); emu.register_component(stderr_log)?; let mut stderr_msg = StderrMessageComponent::default(); stderr_msg.prefix = "{message} ".to_string(); emu.register_component(stderr_msg)?; if opt.trace_api { emu.register_component(StderrCallTraceComponent { prefix: "{trace} ".to_string() })?; } // must register before opengl so it can have priority in queries about what N64 plugin to use // (only supports software-rendered 2D frames currently) #[cfg(feature = "ffmpeg_comp")] if let Some(video_path) = opt.record_video { ffmpeg::log::set_level(ffmpeg::log::Level::Info); ffmpeg::init()?; let ffmpeg_comp = FfmpegComponent::new(emu.libretro_core(), video_path); emu.register_component(ffmpeg_comp)?; } let ft = if opt.fullscreen { FullscreenType::Desktop } else { FullscreenType::Off }; let sdl2_ogl_res = if opt.no_opengl { Err("OpenGL disabled".into()) } else { SimpleSdl2OpenglComponent::new(&mut sdl_context, ft) }; if let Ok(sdl2_ogl) = sdl2_ogl_res { emu.register_component(sdl2_ogl)?; } else { match SimpleSdl2CanvasComponent::new(&mut sdl_context, ft) { Ok(sdl2_canvas) => emu.register_component(sdl2_canvas)?, Err(e) => eprintln!("Couldn't initialize SDL2 video component: {:?}", e), } } emu.register_component(Sdl2RateControlledAudioComponent::new(&mut sdl_context)?)?; emu.register_component(SimpleSdl2KeyboardComponent::new(&mut sdl_context)?)?; emu.register_component(SimpleSdl2GamepadComponent::new(&mut sdl_context))?; emu.register_component(SleepFramerateLimitComponent::default())?; 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.register_component(LocalFileSaveComponent::default())?; if opt.sys_info { emu.register_component(StderrSysInfoLogComponent::default())?; } emu.register_component(EnvironmentVariableComponent::default())?; let mut var_store = VariableStoreComponent::default(); // TODO: load a config file specified on the CLI var_store.insert("mgba_skip_bios", "ON"); var_store.insert("gpsp_drc", "enabled"); var_store.insert("gpsp_frame_mixing", "enabled"); var_store.insert("gpsp_save_method", "libretro"); emu.register_component(var_store)?; emu.init()?; emu.load_game(&opt.rom)?; if let Some(state) = opt.state { emu.unserialize_path(state)?; } let mut frame = 0u64; while let ControlFlow::Continue = emu.run() { frame = frame.wrapping_add(1); } eprintln!("Ran for {} frames.", frame); Ok(()) }