//! Rust versions of libretro data structures.
use super::*;
use std::collections::HashMap;
/// Static information about the [`Core`] implementation.
#[derive(Debug, Default)]
pub struct SystemInfo {
/// Descriptive name of library. Should not
/// contain any version numbers, etc.
pub library_name: CString,
/// Descriptive version of the core.
pub library_version: CString,
/// A string listing probably content extensions the core will be able to
/// load, separated with pipe. I.e. "bin|rom|iso".
/// Typically used for a GUI to filter out extensions.
pub valid_extensions: CString,
/// libretro cores that need to have direct access to their content
/// files, including cores which use the path of the content files to
/// determine the paths of other files, should set `need_fullpath` to true.
///
/// Cores should strive for setting `need_fullpath` to [`false`],
/// as it allows the frontend to perform patching, etc.
///
/// If `need_fullpath` is [`true`] and [`Core::on_load_game`] is called:
/// - [`retro_game_info::path`] is guaranteed to have a valid path
/// - [`retro_game_info::data`] and [`retro_game_info::size`] are invalid
///
/// If `need_fullpath` is [`false`] and [`Core::on_load_game`] is called:
/// - [`retro_game_info::path`] may be NULL
/// - [`retro_game_info::data`] and [`retro_game_info::size`] are guaranteed
/// to be valid
///
/// See also:
/// - [`environment::get_system_directory`]
/// - [`environment::get_save_directory`]
pub need_fullpath: bool,
/// If [`true`], the frontend is not allowed to extract any archives before
/// loading the real content.
/// Necessary for certain libretro implementations that load games
/// from zipped archives.
pub block_extract: bool,
}
bitflags::bitflags! {
/// Bitflags indicating the type of input device
pub struct RetroDevice: u8 {
/// Input disabled
const NONE = (1 << RETRO_DEVICE_NONE);
/// The JOYPAD is called RetroPad. It is essentially a Super Nintendo
/// controller, but with additional L2/R2/L3/R3 buttons, similar to a
/// PS1 DualShock.
const JOYPAD = (1 << RETRO_DEVICE_JOYPAD);
/// The mouse is a simple mouse, similar to Super Nintendo's mouse.
/// X and Y coordinates are reported relatively to last poll (poll callback).
/// It is up to the libretro implementation to keep track of where the mouse
/// pointer is supposed to be on the screen.
/// The frontend must make sure not to interfere with its own hardware
/// mouse pointer.
const MOUSE = (1 << RETRO_DEVICE_MOUSE);
/// KEYBOARD device lets one poll for raw key pressed.
/// It is poll based, so input callback will return with the current
/// pressed state.
/// For event/text based keyboard input, see
/// [`RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK`].
const KEYBOARD = (1 << RETRO_DEVICE_KEYBOARD);
/// LIGHTGUN device is similar to Guncon-2 for PlayStation 2.
/// It reports X/Y coordinates in screen space (similar to the pointer)
/// in the range `[-0x8000, 0x7fff]` in both axes, with zero being center and
/// `-0x8000` being out of bounds.
/// As well as reporting on/off screen state. It features a trigger,
/// start/select buttons, auxiliary action buttons and a
/// directional pad. A forced off-screen shot can be requested for
/// auto-reloading function in some games.
const LIGHTGUN = (1 << RETRO_DEVICE_LIGHTGUN);
/// The ANALOG device is an extension to JOYPAD (RetroPad).
/// Similar to DualShock2 it adds two analog sticks and all buttons can
/// be analog. This is treated as a separate device type as it returns
/// axis values in the full analog range of `[-0x7fff, 0x7fff]`,
/// although some devices may return `-0x8000`.
/// Positive X axis is right. Positive Y axis is down.
/// Buttons are returned in the range `[0, 0x7fff]`.
/// Only use ANALOG type when polling for analog values.
const ANALOG = (1 << RETRO_DEVICE_ANALOG);
/// Abstracts the concept of a pointing mechanism, e.g. touch.
/// This allows libretro to query in absolute coordinates where on the
/// screen a mouse (or something similar) is being placed.
/// For a touch centric device, coordinates reported are the coordinates
/// of the press.
///
/// Coordinates in X and Y are reported as:
/// `[-0x7fff, 0x7fff]`: `-0x7fff` corresponds to the far left/top of the screen,
/// and `0x7fff` corresponds to the far right/bottom of the screen.
/// The "screen" is here defined as area that is passed to the frontend and
/// later displayed on the monitor.
///
/// The frontend is free to scale/resize this screen as it sees fit, however,
/// `(X, Y) = (-0x7fff, -0x7fff)` will correspond to the top-left pixel of the
/// game image, etc.
///
/// To check if the pointer coordinates are valid (e.g. a touch display
/// actually being touched), PRESSED returns 1 or 0.
///
/// If using a mouse on a desktop, PRESSED will usually correspond to the
/// left mouse button, but this is a frontend decision.
/// PRESSED will only return 1 if the pointer is inside the game screen.
///
/// For multi-touch, the index variable can be used to successively query
/// more presses.
/// If `index = 0` returns `true` for `_PRESSED`, coordinates can be extracted
/// with `_X, _Y` for `index = 0`. One can then query `_PRESSED, _X, _Y` with
/// `index = 1`, and so on.
/// Eventually `_PRESSED` will return `false` for an index. No further presses
/// are registered at this point.
const POINTER = (1 << RETRO_DEVICE_POINTER);
}
}
#[test]
fn retro_device_struct_size() {
assert_eq!(
std::mem::size_of::<RetroDevice>(),
((RETRO_DEVICE_MASK + 1) >> RETRO_DEVICE_TYPE_SHIFT) as usize
);
}
bitflags::bitflags! {
/// Signifies quirks of the [`Core`]’s serialization feature (if any).
pub struct SerializationQuirks: u32 {
/// Serialized state is incomplete in some way. Set if serialization is
/// usable in typical end-user cases but should not be relied upon to
/// implement frame-sensitive frontend features such as netplay or
/// rerecording.
const INCOMPLETE = RETRO_SERIALIZATION_QUIRK_INCOMPLETE;
/// The core must spend some time initializing before serialization is
/// supported. [`Core::on_serialize`] will initially fail; [`Core::on_unserialize`]
/// and [`Core::get_serialize_size`] may or may not work correctly either.
const MUST_INITIALIZE = RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE;
/// Serialization size may change within a session.
const CORE_VARIABLE_SIZE = RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE;
/// Set by the frontend to acknowledge that it supports variable-sized
/// states.
const FRONT_VARIABLE_SIZE = RETRO_SERIALIZATION_QUIRK_FRONT_VARIABLE_SIZE;
/// Serialized state can only be loaded during the same session.
const SINGLE_SESSION = RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION;
/// Serialized state cannot be loaded on an architecture with a different
/// endianness from the one it was saved on.
const ENDIAN_DEPENDENT = RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT;
/// Serialized state cannot be loaded on a different platform from the one it
/// was saved on for reasons other than endianness, such as word size
/// dependence
const PLATFORM_DEPENDENT = RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT;
}
}
bitflags::bitflags! {
pub struct CpuFeatures: u64 {
const SSE = RETRO_SIMD_SSE as u64;
const SSE2 = RETRO_SIMD_SSE2 as u64;
const VMX = RETRO_SIMD_VMX as u64;
const VMX128 = RETRO_SIMD_VMX128 as u64;
const AVX = RETRO_SIMD_AVX as u64;
const NEON = RETRO_SIMD_NEON as u64;
const SSE3 = RETRO_SIMD_SSE3 as u64;
const SSSE3 = RETRO_SIMD_SSSE3 as u64;
const MMX = RETRO_SIMD_MMX as u64;
const MMXEXT = RETRO_SIMD_MMXEXT as u64;
const SSE4 = RETRO_SIMD_SSE4 as u64;
const SSE42 = RETRO_SIMD_SSE42 as u64;
const AVX2 = RETRO_SIMD_AVX2 as u64;
const VFPU = RETRO_SIMD_VFPU as u64;
const PS = RETRO_SIMD_PS as u64;
const AES = RETRO_SIMD_AES as u64;
const VFPV3 = RETRO_SIMD_VFPV3 as u64;
const VFPV4 = RETRO_SIMD_VFPV4 as u64;
const POPCNT = RETRO_SIMD_POPCNT as u64;
const MOVBE = RETRO_SIMD_MOVBE as u64;
const CMOV = RETRO_SIMD_CMOV as u64;
const ASIMD = RETRO_SIMD_ASIMD as u64;
}
}
/// Used in [`environment::set_message_ext`] to signal some ongoing progress.
pub enum MessageProgress {
/// The message is unmetered or the progress cannot be determined.
Indeterminate,
/// The progress as a percentage (0 - 100).
Percentage(u8),
}
impl MessageProgress {
pub fn indeterminate() -> Self {
MessageProgress::Indeterminate
}
pub fn percentage(value: u8) -> Option<Self> {
if value <= 100 {
Some(MessageProgress::Percentage(value))
} else {
None
}
}
pub fn as_i8(&self) -> i8 {
match *self {
MessageProgress::Percentage(value) => value as i8,
MessageProgress::Indeterminate => -1,
}
}
}
/// Screen rotation in degrees
pub enum Rotation {
None,
Clockwise90,
Clockwise180,
Clockwise270,
CounterClockwise90,
CounterClockwise180,
CounterClockwise270,
}
impl Rotation {
pub fn get_env_value(&self) -> u32 {
match self {
Rotation::None => 0,
Rotation::Clockwise90 => 3,
Rotation::Clockwise180 => 2,
Rotation::Clockwise270 => 1,
Rotation::CounterClockwise90 => 1,
Rotation::CounterClockwise180 => 2,
Rotation::CounterClockwise270 => 3,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum PixelFormat {
XRGB1555 = retro_pixel_format::RETRO_PIXEL_FORMAT_0RGB1555 as isize,
XRGB8888 = retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888 as isize,
RGB565 = retro_pixel_format::RETRO_PIXEL_FORMAT_RGB565 as isize,
UNKNOWN = retro_pixel_format::RETRO_PIXEL_FORMAT_UNKNOWN as isize,
}
impl PixelFormat {
#[inline]
pub fn bit_per_pixel(self) -> usize {
match self {
Self::XRGB1555 => 2,
Self::XRGB8888 => 4,
Self::RGB565 => 2,
Self::UNKNOWN => 0,
}
}
}
impl From<retro_pixel_format> for PixelFormat {
fn from(other: retro_pixel_format) -> Self {
match other {
retro_pixel_format::RETRO_PIXEL_FORMAT_0RGB1555 => Self::XRGB1555,
retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888 => Self::XRGB8888,
retro_pixel_format::RETRO_PIXEL_FORMAT_RGB565 => Self::RGB565,
_ => Self::UNKNOWN,
}
}
}
impl From<PixelFormat> for retro_pixel_format {
fn from(other: PixelFormat) -> Self {
match other {
PixelFormat::XRGB1555 => Self::RETRO_PIXEL_FORMAT_0RGB1555,
PixelFormat::XRGB8888 => Self::RETRO_PIXEL_FORMAT_XRGB8888,
PixelFormat::RGB565 => Self::RETRO_PIXEL_FORMAT_RGB565,
PixelFormat::UNKNOWN => Self::RETRO_PIXEL_FORMAT_UNKNOWN,
}
}
}
#[derive(Debug)]
pub struct PerfCounter {
#[allow(unused)]
/// Borrowed by the `retro_perf_counter`.
pub(crate) ident: CString,
pub(crate) counter: retro_perf_counter,
}
#[derive(Debug, Default)]
pub struct PerfCounters {
pub interface: Option<retro_perf_callback>,
pub counters: HashMap<&'static str, PerfCounter>,
}
#[derive(Debug, Default)]
pub struct Position {
pub lat: f64,
pub lon: f64,
pub horiz_accuracy: f64,
pub vert_accuracy: f64,
}
/// Data structures used by experimental libretro environment function calls
#[proc::unstable(feature = "env-commands")]
pub mod unstable {
use super::PixelFormat;
use core::marker::PhantomData;
use rust_libretro_sys::*;
bitflags::bitflags! {
/// Tells the core if the frontend wants audio or video.
pub struct AudioVideoEnable: u32 {
/// When this bit is **not** set:
/// * The frontend wants the core: to not generate any video,
/// including presenting frames via hardware acceleration.
/// * The frontend's video frame callback will do nothing.
/// * After running the frame, the video output of the next frame should be
/// no different than if video was enabled, and saving and loading state
/// should have no issues.
const ENABLE_VIDEO = 0b0001;
/// When this bit is **not** set:
/// * The frontend wants the core to not generate any audio.
/// * The frontend's audio callbacks will do nothing.
/// * After running the frame, the audio output of the next frame should be
/// no different than if audio was enabled, and saving and loading state
/// should have no issues.
const ENABLE_AUDIO = 0b0010;
/// When this bit is set:
/// * Guaranteed to be created by the same binary that will load them.
/// * Will not be written to or read from the disk.
/// * Suggest that the core assumes loading state will succeed.
/// * Suggest that the core updates its memory buffers in-place if possible.
/// * Suggest that the core skips clearing memory.
/// * Suggest that the core skips resetting the system.
/// * Suggest that the core may skip validation steps.
const USE_FAST_SAVESTATES = 0b0100;
/// When this bit is set:
/// * Used for a secondary core when running ahead.
/// * Indicates that the frontend will never need audio from the core.
/// * Suggests that the core may stop synthesizing audio, but this should not
/// compromise emulation accuracy.
/// * Audio output for the next frame does not matter, and the frontend will
/// never need an accurate audio state in the future.
/// * State will never be saved when using Hard Disable Audio.
const HARD_DISABLE_AUDIO = 0b1000;
}
}
bitflags::bitflags! {
/// Joypad button mask
pub struct JoypadState: u16 {
const B = 0b0000_0000_0000_0001;
const Y = 0b0000_0000_0000_0010;
const SELECT = 0b0000_0000_0000_0100;
const START = 0b0000_0000_0000_1000;
const UP = 0b0000_0000_0001_0000;
const DOWN = 0b0000_0000_0010_0000;
const LEFT = 0b0000_0000_0100_0000;
const RIGHT = 0b0000_0000_1000_0000;
const A = 0b0000_0001_0000_0000;
const X = 0b0000_0010_0000_0000;
const L = 0b0000_0100_0000_0000;
const R = 0b0000_1000_0000_0000;
const L2 = 0b0001_0000_0000_0000;
const R2 = 0b0010_0000_0000_0000;
const L3 = 0b0100_0000_0000_0000;
const R3 = 0b1000_0000_0000_0000;
}
}
#[derive(Debug, Default)]
pub struct VfsInterfaceInfo {
pub(crate) supported_version: u32,
pub(crate) interface: Option<retro_vfs_interface>,
}
bitflags::bitflags! {
pub struct VfsFileOpenFlags: u32 {
const READ = RETRO_VFS_FILE_ACCESS_READ;
const WRITE = RETRO_VFS_FILE_ACCESS_WRITE;
const READ_WRITE = RETRO_VFS_FILE_ACCESS_READ_WRITE;
const UPDATE_EXISTING = RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
}
}
bitflags::bitflags! {
pub struct VfsFileOpenHints: u32 {
const NONE = RETRO_VFS_FILE_ACCESS_HINT_NONE;
const FREQUENT_ACCESS = RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
}
}
#[repr(i32)]
pub enum VfsSeekPosition {
Start = RETRO_VFS_SEEK_POSITION_START as i32,
Current = RETRO_VFS_SEEK_POSITION_CURRENT as i32,
End = RETRO_VFS_SEEK_POSITION_END as i32,
}
bitflags::bitflags! {
pub struct VfsStat: i32 {
const STAT_IS_VALID = RETRO_VFS_STAT_IS_VALID as i32;
const STAT_IS_DIRECTORY = RETRO_VFS_STAT_IS_DIRECTORY as i32;
const STAT_IS_CHARACTER_SPECIAL = RETRO_VFS_STAT_IS_CHARACTER_SPECIAL as i32;
}
}
bitflags::bitflags! {
pub struct MemoryAccess: u32 {
const WRITE = RETRO_MEMORY_ACCESS_WRITE;
const READ = RETRO_MEMORY_ACCESS_READ;
}
}
bitflags::bitflags! {
pub struct MemoryType: u32 {
const UNCACHED = 0;
const CACHED = RETRO_MEMORY_TYPE_CACHED;
}
}
// TODO: Can we get rid of the raw pointer and PhantomData in an ergonomic way?
pub struct Framebuffer<'a> {
pub data: *mut u8,
pub data_len: usize,
pub phantom: PhantomData<&'a mut [u8]>,
pub width: u32,
pub height: u32,
pub pitch: usize,
pub format: PixelFormat,
pub access_flags: MemoryAccess,
pub memory_flags: MemoryType,
}
impl<'a> Framebuffer<'a> {
pub fn borrow_slice(&self) -> &'a [u8] {
unsafe { std::slice::from_raw_parts(self.data, self.data_len) }
}
pub unsafe fn as_slice<'b>(&self) -> &'b [u8] {
unsafe { std::slice::from_raw_parts(self.data, self.data_len) }
}
pub fn borrow_slice_mut(&self) -> &'a mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.data, self.data_len) }
}
pub unsafe fn as_slice_mut<'b>(&self) -> &'b mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.data, self.data_len) }
}
}
pub trait HwRenderContextNegotiationInterface: std::fmt::Debug {
fn as_any(&self) -> &dyn std::any::Any;
}
impl HwRenderContextNegotiationInterface for retro_hw_render_context_negotiation_interface {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[cfg(feature = "vulkan")]
impl HwRenderContextNegotiationInterface for retro_hw_render_context_negotiation_interface_vulkan {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[test]
#[cfg(feature = "vulkan")]
fn retro_hw_render_context_negotiation_interface_vulkan_is_superset() {
assert!(
std::mem::size_of::<retro_hw_render_context_negotiation_interface_vulkan>()
>= std::mem::size_of::<retro_hw_render_context_negotiation_interface>(),
);
}
}
pub use unstable::*;