ferretro/ferretro_components/src/base/mod.rs

499 lines
16 KiB
Rust

use crate::prelude::*;
use ferretro_base::retro::ffi::*;
use std::os::raw::c_uint;
use std::path::{PathBuf, Path};
use std::pin::Pin;
use std::io::Read;
pub struct RetroComponentBase {
retro: LibretroWrapper,
libretro_path: PathBuf,
// TODO: control when things get added to this with lifetime constraints defined by implementers
components: Vec<Box<dyn RetroComponent>>,
// replaying env calls for late-added components
cached_rom_path: Option<PathBuf>,
cached_pixel_format: Option<PixelFormat>,
cached_input_descriptors: Option<Vec<InputDescriptor2>>,
cached_hw_render_callback: Option<HwRenderCallback>,
cached_variables: Option<Vec<Variable2>>,
cached_support_no_game: Option<bool>,
cached_system_av_info: Option<SystemAvInfo>,
cached_subsystem_info: Option<Vec<SubsystemInfo2>>,
cached_controller_info: Option<Vec<ControllerDescription2>>,
cached_memory_map: Option<MemoryMap>,
cached_geometry: Option<GameGeometry>,
}
// TODO: replace with std::ops::ControlFlow when it becomes stable
pub enum ControlFlow {
Continue,
Break,
}
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[rustfmt::skip]
#[allow(unused_variables)]
pub trait RetroComponent: RetroCallbacks {
fn pre_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue }
fn post_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue }
fn pre_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> Result<()> { Ok(()) }
fn post_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> Result<()> { Ok(()) }
}
impl RetroComponentBase {
// TODO: constructor & wrapper that uses a statically linked libretro?
pub fn new(core_path: impl AsRef<Path>) -> Pin<Box<Self>> {
let lib = libloading::Library::new(core_path.as_ref()).unwrap();
let raw_retro = ferretro_base::retro::loading::LibretroApi::from_library(lib).unwrap();
let retro = LibretroWrapper::from(raw_retro);
let emu = RetroComponentBase {
retro,
libretro_path: core_path.as_ref().to_path_buf(),
components: Vec::new(),
cached_rom_path: None,
cached_pixel_format: None,
cached_input_descriptors: None,
cached_hw_render_callback: None,
cached_variables: None,
cached_support_no_game: None,
cached_system_av_info: None,
cached_subsystem_info: None,
cached_controller_info: None,
cached_memory_map: None,
cached_geometry: None
};
let mut pin_emu = Box::pin(emu);
ferretro_base::retro::wrapper::set_handler(pin_emu.as_mut());
pin_emu.retro.init();
pin_emu
}
pub fn register_component<T>(&mut self, comp: T) -> Option<()> // TODO: Result
where T: RetroComponent
{
// TODO: match comp.schedule { BeforeInit, BeforeLoad, BeforeFirstRun, Anytime }
let mut comp = Box::new(comp);
if let Some(cached) = &self.cached_pixel_format {
if let Some(false) = comp.set_pixel_format(*cached) {
// TODO: error, and propagate this pattern downward
}
}
if let Some(cached) = &self.cached_input_descriptors {
comp.set_input_descriptors(cached);
}
if let Some(cached) = &self.cached_hw_render_callback {
comp.set_hw_render(cached);
}
if let Some(cached) = &self.cached_variables {
comp.set_variables(cached);
}
if let Some(cached) = &self.cached_support_no_game {
comp.set_support_no_game(*cached);
}
if let Some(cached) = &self.cached_system_av_info {
comp.set_system_av_info(cached);
}
if let Some(cached) = &self.cached_subsystem_info {
comp.set_subsystem_info(cached);
}
if let Some(cached) = &self.cached_controller_info {
comp.set_controller_info(cached);
}
if let Some(cached) = &self.cached_memory_map {
comp.set_memory_maps(cached);
}
if let Some(cached) = &self.cached_geometry {
comp.set_geometry(cached);
}
if let Some(cached) = &self.cached_rom_path {
comp.post_load_game(&mut self.retro, &cached);
}
self.components.push(comp);
Some(())
}
pub fn load_game(&mut self, rom: impl AsRef<Path>) -> Result<()> {
let path = rom.as_ref();
self.cached_rom_path = Some(path.to_path_buf());
for comp in &mut self.components {
comp.pre_load_game(&mut self.retro, path)?;
}
let mut data = None;
let mut v = Vec::new();
if !self.retro.get_system_info().need_fullpath {
if let Ok(mut f) = std::fs::File::open(path) {
if f.read_to_end(&mut v).is_ok() {
data = Some(v.as_ref());
}
}
}
self.retro.load_game(Some(path), data, None)?;
for comp in &mut self.components {
comp.post_load_game(&mut self.retro, path)?;
}
Ok(())
}
pub fn run(&mut self) -> ControlFlow {
for comp in &mut self.components {
if let ControlFlow::Break = comp.pre_run(&mut self.retro) {
return ControlFlow::Break;
}
}
self.retro.run();
for comp in &mut self.components {
if let ControlFlow::Break = comp.post_run(&mut self.retro) {
return ControlFlow::Break;
}
}
ControlFlow::Continue
}
pub fn unserialize_path(&mut self, state: impl AsRef<Path>) -> Result<()> {
let path = state.as_ref();
let mut v = Vec::new();
if let Ok(mut f) = std::fs::File::open(path) {
if f.read_to_end(&mut v).is_ok(){
return self.unserialize_buf(v);
}
}
Err("Couldn't read file to unserialize".into())
}
pub fn unserialize_buf(&mut self, data: impl AsRef<[u8]>) -> Result<()> {
self.retro.unserialize(data.as_ref())
}
}
impl LibretroWrapperAccess for RetroComponentBase {
fn libretro_core(&mut self) -> &mut LibretroWrapper {
&mut self.retro
}
}
impl RetroCallbacks for RetroComponentBase {
fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {
for comp in &mut self.components {
comp.video_refresh(data, width, height, pitch);
}
}
fn video_refresh_dupe(&mut self, width: c_uint, height: c_uint, pitch: c_uint) {
for comp in &mut self.components {
comp.video_refresh_dupe(width, height, pitch);
}
}
fn audio_sample(&mut self, left: i16, right: i16) {
for comp in &mut self.components {
comp.audio_sample(left, right);
}
}
fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize {
self.components.iter_mut()
.map(|comp| comp.audio_sample_batch(stereo_pcm))
.max()
.unwrap_or_default()
}
fn input_poll(&mut self) {
for comp in &mut self.components {
comp.input_poll();
}
}
fn input_state(&mut self, port: u32, device: InputDeviceId, index: InputIndex) -> i16 {
self.components.iter_mut()
.map(|comp| comp.input_state(port, device, index))
.filter(|x| *x != 0)
// TODO: is this really the semantic we want?
.last()
.unwrap_or_default()
}
fn set_rotation(&mut self, rotation: EnvRotation) -> Option<bool> {
self.components.iter_mut()
.map(|comp| comp.set_rotation(rotation))
.flatten()
.fold(false, |x, y| x || y) // not "any" because we don't short-circuit
.into()
}
fn get_overscan(&mut self) -> Option<bool> {
self.components.iter_mut()
.map(|comp| comp.get_overscan())
.fold(None, |x, y| match (x, y) {
(Some(a), Some(b)) => Some(a || b),
(Some(a), None) | (None, Some(a)) => Some(a),
(None, None) => None,
})
}
fn set_message(&mut self, message: &Message) -> Option<bool> {
self.components.iter_mut()
.map(|comp| comp.set_message(message))
.flatten()
.fold(false, |x, y| x || y) // not "any" because we don't short-circuit
.into()
}
fn shutdown(&mut self) -> Option<bool> {
self.components.iter_mut()
.map(|comp| comp.shutdown())
.flatten()
.all(|x| x)
.into()
}
fn set_performance_level(&mut self, level: c_uint) -> Option<bool> {
self.components.iter_mut()
.map(|comp| comp.set_performance_level(level))
.flatten()
.all(|x| x)
.into()
}
fn get_system_directory(&mut self) -> Option<PathBuf> {
self.components.iter_mut()
.map(|comp| comp.get_system_directory())
.flatten()
.next()
}
fn set_pixel_format(&mut self, format: PixelFormat) -> Option<bool> {
self.cached_pixel_format = Some(format);
self.components.iter_mut()
.map(|comp| comp.set_pixel_format(format))
.flatten()
.all(|x| x)
.into()
}
fn set_input_descriptors(&mut self, input_descriptors: &Vec<InputDescriptor2>) -> Option<bool> {
self.cached_input_descriptors = Some(input_descriptors.to_vec());
self.components.iter_mut()
.map(|comp| comp.set_input_descriptors(input_descriptors))
.flatten()
.fold(false, |x, y| x || y)
.into()
}
fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option<bool> {
self.cached_hw_render_callback = Some(hw_render_callback.to_owned());
self.components.iter_mut()
.map(|comp| comp.set_hw_render(hw_render_callback))
.flatten()
.all(|x| x)
.into()
}
fn get_variable(&mut self, key: &str) -> Option<String> {
self.components.iter_mut()
.map(|comp| comp.get_variable(key))
.flatten()
.next()
}
fn set_variables(&mut self, variables: &Vec<Variable2>) -> Option<bool> {
self.cached_variables = Some(variables.to_vec());
self.components.iter_mut()
.map(|comp| comp.set_variables(variables))
.flatten()
.fold(false, |x, y| x || y)
.into()
}
fn get_variable_update(&mut self) -> Option<bool> {
self.components.iter_mut()
.map(|comp| comp.get_variable_update())
.flatten()
.reduce(|x, y| x || y)
}
fn set_support_no_game(&mut self, supports_no_game: bool) -> Option<bool> {
self.cached_support_no_game = Some(supports_no_game);
self.components.iter_mut()
.map(|comp| comp.set_support_no_game(supports_no_game))
.flatten()
.all(|x| x)
.into()
}
// allow it to be overridden, but we *do* have the answer at this level since we loaded it.
fn get_libretro_path(&mut self) -> Option<PathBuf> {
self.components.iter_mut()
.map(|comp| comp.get_libretro_path())
.flatten()
.next()
.unwrap_or_else(|| self.libretro_path.clone())
.into()
}
fn get_input_device_capabilities(&mut self) -> Option<u64> {
self.components.iter_mut()
.map(|comp| comp.get_input_device_capabilities())
.flatten()
.reduce(|x, y| x & y)
}
fn get_core_assets_directory(&mut self) -> Option<PathBuf> {
self.components.iter_mut()
.map(|comp| comp.get_core_assets_directory())
.flatten()
.next()
}
fn get_save_directory(&mut self) -> Option<PathBuf> {
self.components.iter_mut()
.map(|comp| comp.get_save_directory())
.flatten()
.next()
}
fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option<bool> {
self.cached_system_av_info = Some(system_av_info.to_owned());
self.cached_geometry = Some(system_av_info.geometry.clone());
self.components.iter_mut()
.map(|comp| comp.set_system_av_info(system_av_info))
.flatten()
.fold(false, |x, y| x || y) // not "any" because we don't short-circuit
.into()
}
fn set_subsystem_info(&mut self, subsystem_info: &Vec<SubsystemInfo2>) -> Option<bool> {
self.cached_subsystem_info = Some(subsystem_info.to_vec());
self.components.iter_mut()
.map(|comp| comp.set_subsystem_info(subsystem_info))
.flatten()
.all(|x| x)
.into()
}
fn set_controller_info(&mut self, controller_info: &Vec<ControllerDescription2>) -> Option<bool> {
self.cached_controller_info = Some(controller_info.to_vec());
self.components.iter_mut()
.map(|comp| comp.set_controller_info(controller_info))
.flatten()
.all(|x| x)
.into()
}
fn set_memory_maps(&mut self, memory_map: &MemoryMap) -> Option<bool> {
self.cached_memory_map = Some(memory_map.to_owned());
self.components.iter_mut()
.map(|comp| comp.set_memory_maps(memory_map))
.flatten()
.all(|x| x)
.into()
}
fn set_geometry(&mut self, game_geometry: &GameGeometry) -> Option<bool> {
self.cached_geometry = Some(game_geometry.to_owned());
self.components.iter_mut()
.map(|comp| comp.set_geometry(game_geometry))
.flatten()
.fold(false, |x, y| x || y) // not "any" because we don't short-circuit
.into()
}
fn get_username(&mut self) -> Option<String> {
self.components.iter_mut()
.map(|comp| comp.get_username())
.flatten()
.next()
}
fn get_language(&mut self) -> Option<Language> {
self.components.iter_mut()
.map(|comp| comp.get_language())
.flatten()
.next()
}
fn log_print(&mut self, level: LogLevel, msg: &str) {
for comp in &mut self.components {
comp.log_print(level, msg);
}
}
fn set_rumble_state(&mut self, port: c_uint, effect: RumbleEffect, strength: u16) -> bool {
self.components.iter_mut()
.map(|comp| comp.set_rumble_state(port, effect, strength))
.fold(false, |x, y| x || y) // not "any" because we don't short-circuit
}
fn perf_get_time_usec_cb(&mut self) -> Time {
self.components.first_mut()
.map(|comp| comp.perf_get_time_usec_cb())
.unwrap_or_default()
}
fn perf_get_counter_cb(&mut self) -> PerfTick {
self.components.first_mut()
.map(|comp| comp.perf_get_counter_cb())
.unwrap_or_default()
}
fn perf_get_cpu_features_cb(&mut self) -> u64 {
self.components.first_mut()
.map(|comp| comp.perf_get_cpu_features_cb())
.unwrap_or_default()
}
fn perf_log_cb(&mut self) {
if let Some(comp) = self.components.first_mut() {
comp.perf_log_cb()
}
}
fn perf_register_cb(&mut self, counter: &mut PerfCounter) {
if let Some(comp) = self.components.first_mut() {
comp.perf_register_cb(counter)
}
}
fn perf_start_cb(&mut self, counter: &mut PerfCounter) {
if let Some(comp) = self.components.first_mut() {
comp.perf_start_cb(counter)
}
}
fn perf_stop_cb(&mut self, counter: &mut PerfCounter) {
if let Some(comp) = self.components.first_mut() {
comp.perf_stop_cb(counter)
}
}
fn set_sensor_state(&mut self, port: c_uint, action: SensorAction, rate: c_uint) -> bool {
self.components.iter_mut()
.map(|comp| comp.set_sensor_state(port, action, rate))
.fold(false, |x, y| x || y) // not "any" because we don't short-circuit
}
fn get_sensor_input(&mut self, port: c_uint, id: c_uint) -> f32 {
self.components.iter_mut()
.map(|comp| comp.get_sensor_input(port, id))
.filter(|x| *x != 0.0)
.last()
.unwrap_or_default()
}
}
impl Drop for RetroComponentBase {
fn drop(&mut self) {
ferretro_base::retro::wrapper::unset_handler();
}
}