ferretro/ferretro_components/examples/multifunction_emulator.rs

154 lines
4.7 KiB
Rust

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 = "cpal_comp")]
use ferretro_components::provided::cpal::CpalAudioComponent;
#[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<PathBuf>,
/// System directory, often containing BIOS files
#[structopt(short, long, parse(from_os_str))]
system: Option<PathBuf>,
/// Recorded video to write.
#[cfg(feature = "ffmpeg_comp")]
#[structopt(long, parse(from_os_str))]
record_video: Option<PathBuf>,
/// 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<dyn std::error::Error + Send + Sync>> {
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),
}
}
#[cfg(not(feature = "cpal_comp"))]
{
emu.register_component(SimpleSdl2AudioThreadComponent::new(&mut sdl_context)?)?;
}
#[cfg(feature = "cpal_comp")]
{
emu.register_component(CpalAudioComponent::default())?;
}
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(())
}