use core::convert::{TryFrom, TryInto}; use core::mem::size_of; use std::ffi::CStr; use std::fmt::{Debug, Formatter}; use std::os::raw::{c_char, c_uint}; use std::slice::from_raw_parts; use super::constants::*; use super::ffi::*; fn delimited_cstr(cstr: *const c_char, delim: char) -> Vec { if cstr.is_null() { Vec::new() } else { unsafe { CStr::from_ptr(cstr) } .to_string_lossy() .split(delim) .map(str::to_string) .collect() } } #[derive(Clone, Copy)] pub enum VideoFrame<'a> { XRGB1555 { data: &'a [u16], width: c_uint, height: c_uint, pitch_u16: usize }, RGB565 { data: &'a [u16], width: c_uint, height: c_uint, pitch_u16: usize }, XRGB8888 { data: &'a [u32], width: c_uint, height: c_uint, pitch_u32: usize }, Duplicate { width: c_uint, height: c_uint, pitch_u8: usize, }, HardwareRender { width: c_uint, height: c_uint, }, } impl<'a> VideoFrame<'a> { pub fn data_pitch_as_bytes(&self) -> Option<(&'a [u8], usize)> { match self { VideoFrame::RGB565 { data, pitch_u16, .. } | VideoFrame::XRGB1555 { data, pitch_u16, .. } => { let ptr = data.as_ptr() as *const u8; let len = data.len() * size_of::(); Some((unsafe { from_raw_parts(ptr, len) }, pitch_u16 * size_of::())) } VideoFrame::XRGB8888 { data, pitch_u32, .. } => { let ptr = data.as_ptr() as *const u8; let len = data.len() * size_of::(); Some((unsafe { from_raw_parts(ptr, len) }, pitch_u32 * size_of::())) } _ => None, } } pub fn dimensions(&self) -> (c_uint, c_uint) { match self { VideoFrame::XRGB1555 { width, height, .. } | VideoFrame::RGB565 { width, height, .. } | VideoFrame::XRGB8888 { width, height, .. } | VideoFrame::Duplicate { width, height, .. } | VideoFrame::HardwareRender { width, height, .. } => (*width, *height), } } pub fn pixel_format(&self) -> Option { match self { VideoFrame::XRGB1555 { .. } => Some(PixelFormat::ARGB1555), VideoFrame::RGB565 { .. } => Some(PixelFormat::RGB565), VideoFrame::XRGB8888 { .. } => Some(PixelFormat::ARGB8888), _ => None, } } } impl<'a> Debug for VideoFrame<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { VideoFrame::XRGB1555 { data, width, height, pitch_u16 } => { write!( f, "XRGB1555 {{ data: &[u16; {}], width: {}, height: {}, pitch_u16: {} }}", data.len(), width, height, pitch_u16 ) } VideoFrame::RGB565 { data, width, height, pitch_u16 } => { write!( f, "RGB565 {{ data: &[u16; {}], width: {}, height: {}, pitch_u16: {} }}", data.len(), width, height, pitch_u16 ) } VideoFrame::XRGB8888 { data, width, height, pitch_u32 } => { write!( f, "XRGB8888 {{ data: &[u32; {}], width: {}, height: {}, pitch_u32: {} }}", data.len(), width, height, pitch_u32 ) } VideoFrame::Duplicate { width, height, pitch_u8 } => { write!( f, "Duplicate {{ width: {}, height: {}, pitch_u8: {} }}", width, height, pitch_u8 ) } VideoFrame::HardwareRender { width, height } => { write!(f, "HardwareRender {{ width: {}, height: {} }}", width, height) } } } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum InputDeviceId { None(c_uint), Joypad(JoypadButton), Mouse(MouseButton), Keyboard(c_uint), LightGun(LightGunButton), Analog(AnalogAxis), Pointer(PointerStat), } impl TryFrom<(D, c_uint)> for InputDeviceId where D: TryInto, >::Error: std::error::Error + Send + Sync + 'static, { type Error = Box; fn try_from(pair: (D, c_uint)) -> Result { let (device, id) = pair; Ok(match device.try_into()? { DeviceType::None => InputDeviceId::None(id), DeviceType::Joypad => InputDeviceId::Joypad(JoypadButton::try_from(id)?), DeviceType::Mouse => InputDeviceId::Mouse(MouseButton::try_from(id)?), DeviceType::Keyboard => InputDeviceId::Keyboard(id), DeviceType::LightGun => InputDeviceId::LightGun(LightGunButton::try_from(id)?), DeviceType::Analog => InputDeviceId::Analog(AnalogAxis::try_from(id)?), DeviceType::Pointer => InputDeviceId::Pointer(PointerStat::try_from(id)?), }) } } pub struct SystemInfo2 { pub library_name: String, pub library_version: String, pub valid_extensions: Vec, pub need_fullpath: bool, pub block_extract: bool, } impl From<&SystemInfo> for SystemInfo2 { fn from(info: &SystemInfo) -> Self { SystemInfo2 { library_name: unsafe { CStr::from_ptr(info.library_name) }.to_string_lossy().to_string(), library_version: unsafe { CStr::from_ptr(info.library_version) }.to_string_lossy().to_string(), valid_extensions: delimited_cstr(info.valid_extensions, '|'), need_fullpath: info.need_fullpath, block_extract: info.block_extract, } } } #[derive(Clone, Debug)] pub struct Variable2 { pub key: String, pub description: String, pub options: Vec, } impl From<&Variable> for Variable2 { fn from(var: &Variable) -> Self { let key = unsafe { CStr::from_ptr(var.key) } .to_string_lossy() .to_string(); let value = unsafe { CStr::from_ptr(var.value) } .to_string_lossy() .to_string(); let split: Vec<&str> = value.splitn(2, "; ").collect(); let description = split.get(0).unwrap_or(&"").to_string(); let options = split .get(1) .unwrap_or(&"") .split('|') .map(str::to_string) .collect(); Variable2 { key, description, options, } } } #[derive(Clone, Debug)] pub struct MemoryDescriptor2 { } #[derive(Clone, Debug)] pub struct MemoryMap2 { pub descriptors: Vec } #[derive(Clone, Debug)] pub struct ControllerDescription2 { pub name: String, pub base_type: DeviceType, pub subclass: Option, } impl ControllerDescription2 { pub fn device_id(&self) -> c_uint { match self.subclass { None => c_uint::from(self.base_type), Some(sc) => self.base_type.device_subclass(sc), } } } impl TryFrom<&ControllerDescription> for ControllerDescription2 { type Error = (); fn try_from(t: &ControllerDescription) -> Result { let base_type = DeviceType::try_from(t.id & DEVICE_MASK).map_err(|_| ())?; let subclass = match t.id >> DEVICE_TYPE_SHIFT { 0 => None, n => Some(n - 1), }; let name = unsafe { CStr::from_ptr(t.desc) } .to_str() .map_err(|_| ())? .to_string(); Ok(ControllerDescription2 { base_type, subclass, name, }) } } #[derive(Clone, Debug)] pub struct InputDescriptor2 { pub port: c_uint, pub device_id: InputDeviceId, pub index: InputIndex, pub description: String, } impl TryFrom<&InputDescriptor> for InputDescriptor2 { type Error = (); fn try_from(t: &InputDescriptor) -> Result { if t.id >> DEVICE_TYPE_SHIFT != 0 { eprintln!("Device subclass encountered in retro_input_descriptor, ignoring"); } let description = unsafe { CStr::from_ptr(t.description) } .to_str() .map_err(|_| ())? .to_string(); Ok(InputDescriptor2 { port: t.port, device_id: InputDeviceId::try_from((t.device & DEVICE_MASK, t.id)).map_err(|_| ())?, index: InputIndex::try_from(t.index).map_err(|_| ())?, description, }) } } #[derive(Clone, Debug)] pub struct SubsystemInfo2 { pub description: String, pub identifier: String, pub roms: Vec, pub id: c_uint, } impl From<&SubsystemInfo> for SubsystemInfo2 { fn from(si: &SubsystemInfo) -> Self { let rom_slice = unsafe { from_raw_parts(si.roms, si.num_roms as usize) }; SubsystemInfo2 { description: unsafe { CStr::from_ptr(si.desc) } .to_string_lossy() .to_string(), identifier: unsafe { CStr::from_ptr(si.ident) } .to_string_lossy() .to_string(), roms: rom_slice.iter().map(Into::into).collect(), id: si.id, } } } #[derive(Clone, Debug)] pub struct SubsystemRomInfo2 { pub description: String, pub valid_extensions: Vec, pub need_fullpath: bool, pub block_extract: bool, pub required: bool, pub memory: Vec, } impl From<&SubsystemRomInfo> for SubsystemRomInfo2 { fn from(sri: &SubsystemRomInfo) -> Self { let mem_slice = unsafe { from_raw_parts(sri.memory, sri.num_memory as usize) }; SubsystemRomInfo2 { description: unsafe { CStr::from_ptr(sri.desc) } .to_string_lossy() .to_string(), valid_extensions: delimited_cstr(sri.valid_extensions, '|'), need_fullpath: sri.need_fullpath, block_extract: sri.block_extract, required: sri.required, memory: mem_slice.iter().map(Into::into).collect(), } } } #[derive(Clone, Debug)] pub struct SubsystemMemoryInfo2 { pub extension: String, pub kind: c_uint, } impl From<&SubsystemMemoryInfo> for SubsystemMemoryInfo2 { fn from(smi: &SubsystemMemoryInfo) -> Self { SubsystemMemoryInfo2 { extension: unsafe { CStr::from_ptr(smi.extension) } .to_string_lossy() .to_string(), kind: smi.kind, } } }