rust_libretro/
types.rs

1//! Rust versions of libretro data structures.
2use super::*;
3use std::collections::HashMap;
4
5/// Static information about the [`Core`] implementation.
6#[derive(Debug, Default)]
7pub struct SystemInfo {
8    /// Descriptive name of library. Should not
9    /// contain any version numbers, etc.
10    pub library_name: CString,
11
12    /// Descriptive version of the core.
13    pub library_version: CString,
14
15    /// A string listing probably content extensions the core will be able to
16    /// load, separated with pipe. I.e. "bin|rom|iso".
17    /// Typically used for a GUI to filter out extensions.
18    pub valid_extensions: CString,
19
20    /// libretro cores that need to have direct access to their content
21    /// files, including cores which use the path of the content files to
22    /// determine the paths of other files, should set `need_fullpath` to true.
23    ///
24    /// Cores should strive for setting `need_fullpath` to [`false`],
25    /// as it allows the frontend to perform patching, etc.
26    ///
27    /// If `need_fullpath` is [`true`] and [`Core::on_load_game`] is called:
28    ///    - [`retro_game_info::path`] is guaranteed to have a valid path
29    ///    - [`retro_game_info::data`] and [`retro_game_info::size`] are invalid
30    ///
31    /// If `need_fullpath` is [`false`] and [`Core::on_load_game`] is called:
32    ///    - [`retro_game_info::path`] may be NULL
33    ///    - [`retro_game_info::data`] and [`retro_game_info::size`] are guaranteed
34    ///      to be valid
35    ///
36    /// See also:
37    ///    - [`environment::get_system_directory`]
38    ///    - [`environment::get_save_directory`]
39    pub need_fullpath: bool,
40
41    /// If [`true`], the frontend is not allowed to extract any archives before
42    /// loading the real content.
43    /// Necessary for certain libretro implementations that load games
44    /// from zipped archives.
45    pub block_extract: bool,
46}
47
48bitflags::bitflags! {
49    /// Bitflags indicating the type of input device
50    pub struct RetroDevice: u8 {
51        /// Input disabled
52        const NONE = (1 << RETRO_DEVICE_NONE);
53
54        /// The JOYPAD is called RetroPad. It is essentially a Super Nintendo
55        /// controller, but with additional L2/R2/L3/R3 buttons, similar to a
56        /// PS1 DualShock.
57        const JOYPAD = (1 << RETRO_DEVICE_JOYPAD);
58
59        /// The mouse is a simple mouse, similar to Super Nintendo's mouse.
60        /// X and Y coordinates are reported relatively to last poll (poll callback).
61        /// It is up to the libretro implementation to keep track of where the mouse
62        /// pointer is supposed to be on the screen.
63        /// The frontend must make sure not to interfere with its own hardware
64        /// mouse pointer.
65        const MOUSE = (1 << RETRO_DEVICE_MOUSE);
66
67        /// KEYBOARD device lets one poll for raw key pressed.
68        /// It is poll based, so input callback will return with the current
69        /// pressed state.
70        /// For event/text based keyboard input, see
71        /// [`RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK`].
72        const KEYBOARD = (1 << RETRO_DEVICE_KEYBOARD);
73
74        /// LIGHTGUN device is similar to Guncon-2 for PlayStation 2.
75        /// It reports X/Y coordinates in screen space (similar to the pointer)
76        /// in the range `[-0x8000, 0x7fff]` in both axes, with zero being center and
77        /// `-0x8000` being out of bounds.
78        /// As well as reporting on/off screen state. It features a trigger,
79        /// start/select buttons, auxiliary action buttons and a
80        /// directional pad. A forced off-screen shot can be requested for
81        /// auto-reloading function in some games.
82        const LIGHTGUN = (1 << RETRO_DEVICE_LIGHTGUN);
83
84        /// The ANALOG device is an extension to JOYPAD (RetroPad).
85        /// Similar to DualShock2 it adds two analog sticks and all buttons can
86        /// be analog. This is treated as a separate device type as it returns
87        /// axis values in the full analog range of `[-0x7fff, 0x7fff]`,
88        /// although some devices may return `-0x8000`.
89        /// Positive X axis is right. Positive Y axis is down.
90        /// Buttons are returned in the range `[0, 0x7fff]`.
91        /// Only use ANALOG type when polling for analog values.
92        const ANALOG = (1 << RETRO_DEVICE_ANALOG);
93
94        /// Abstracts the concept of a pointing mechanism, e.g. touch.
95        /// This allows libretro to query in absolute coordinates where on the
96        /// screen a mouse (or something similar) is being placed.
97        /// For a touch centric device, coordinates reported are the coordinates
98        /// of the press.
99        ///
100        /// Coordinates in X and Y are reported as:
101        /// `[-0x7fff, 0x7fff]`: `-0x7fff` corresponds to the far left/top of the screen,
102        /// and `0x7fff` corresponds to the far right/bottom of the screen.
103        /// The "screen" is here defined as area that is passed to the frontend and
104        /// later displayed on the monitor.
105        ///
106        /// The frontend is free to scale/resize this screen as it sees fit, however,
107        /// `(X, Y) = (-0x7fff, -0x7fff)` will correspond to the top-left pixel of the
108        /// game image, etc.
109        ///
110        /// To check if the pointer coordinates are valid (e.g. a touch display
111        /// actually being touched), PRESSED returns 1 or 0.
112        ///
113        /// If using a mouse on a desktop, PRESSED will usually correspond to the
114        /// left mouse button, but this is a frontend decision.
115        /// PRESSED will only return 1 if the pointer is inside the game screen.
116        ///
117        /// For multi-touch, the index variable can be used to successively query
118        /// more presses.
119        /// If `index = 0` returns `true` for `_PRESSED`, coordinates can be extracted
120        /// with `_X, _Y` for `index = 0`. One can then query `_PRESSED, _X, _Y` with
121        /// `index = 1`, and so on.
122        /// Eventually `_PRESSED` will return `false` for an index. No further presses
123        /// are registered at this point.
124        const POINTER = (1 << RETRO_DEVICE_POINTER);
125    }
126}
127#[test]
128fn retro_device_struct_size() {
129    assert_eq!(
130        std::mem::size_of::<RetroDevice>(),
131        ((RETRO_DEVICE_MASK + 1) >> RETRO_DEVICE_TYPE_SHIFT) as usize
132    );
133}
134
135bitflags::bitflags! {
136    /// Signifies quirks of the [`Core`]’s serialization feature (if any).
137    pub struct SerializationQuirks: u32 {
138        /// Serialized state is incomplete in some way. Set if serialization is
139        /// usable in typical end-user cases but should not be relied upon to
140        /// implement frame-sensitive frontend features such as netplay or
141        /// rerecording.
142        const INCOMPLETE = RETRO_SERIALIZATION_QUIRK_INCOMPLETE;
143
144        /// The core must spend some time initializing before serialization is
145        /// supported. [`Core::on_serialize`] will initially fail; [`Core::on_unserialize`]
146        /// and [`Core::get_serialize_size`] may or may not work correctly either.
147        const MUST_INITIALIZE = RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE;
148
149        /// Serialization size may change within a session.
150        const CORE_VARIABLE_SIZE = RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE;
151
152        /// Set by the frontend to acknowledge that it supports variable-sized
153        /// states.
154        const FRONT_VARIABLE_SIZE = RETRO_SERIALIZATION_QUIRK_FRONT_VARIABLE_SIZE;
155
156        /// Serialized state can only be loaded during the same session.
157        const SINGLE_SESSION = RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION;
158
159        /// Serialized state cannot be loaded on an architecture with a different
160        /// endianness from the one it was saved on.
161        const ENDIAN_DEPENDENT = RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT;
162
163        /// Serialized state cannot be loaded on a different platform from the one it
164        /// was saved on for reasons other than endianness, such as word size
165        /// dependence
166        const PLATFORM_DEPENDENT = RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT;
167    }
168}
169
170bitflags::bitflags! {
171    pub struct CpuFeatures: u64 {
172        const SSE = RETRO_SIMD_SSE as u64;
173        const SSE2 = RETRO_SIMD_SSE2 as u64;
174        const VMX = RETRO_SIMD_VMX as u64;
175        const VMX128 = RETRO_SIMD_VMX128 as u64;
176        const AVX = RETRO_SIMD_AVX as u64;
177        const NEON = RETRO_SIMD_NEON as u64;
178        const SSE3 = RETRO_SIMD_SSE3 as u64;
179        const SSSE3 = RETRO_SIMD_SSSE3 as u64;
180        const MMX = RETRO_SIMD_MMX as u64;
181        const MMXEXT = RETRO_SIMD_MMXEXT as u64;
182        const SSE4 = RETRO_SIMD_SSE4 as u64;
183        const SSE42 = RETRO_SIMD_SSE42 as u64;
184        const AVX2 = RETRO_SIMD_AVX2 as u64;
185        const VFPU = RETRO_SIMD_VFPU as u64;
186        const PS = RETRO_SIMD_PS as u64;
187        const AES = RETRO_SIMD_AES as u64;
188        const VFPV3 = RETRO_SIMD_VFPV3 as u64;
189        const VFPV4 = RETRO_SIMD_VFPV4 as u64;
190        const POPCNT = RETRO_SIMD_POPCNT as u64;
191        const MOVBE = RETRO_SIMD_MOVBE as u64;
192        const CMOV = RETRO_SIMD_CMOV as u64;
193        const ASIMD = RETRO_SIMD_ASIMD as u64;
194    }
195}
196
197/// Used in [`environment::set_message_ext`] to signal some ongoing progress.
198pub enum MessageProgress {
199    /// The message is unmetered or the progress cannot be determined.
200    Indeterminate,
201
202    /// The progress as a percentage (0 - 100).
203    Percentage(u8),
204}
205
206impl MessageProgress {
207    pub fn indeterminate() -> Self {
208        MessageProgress::Indeterminate
209    }
210
211    pub fn percentage(value: u8) -> Option<Self> {
212        if value <= 100 {
213            Some(MessageProgress::Percentage(value))
214        } else {
215            None
216        }
217    }
218
219    pub fn as_i8(&self) -> i8 {
220        match *self {
221            MessageProgress::Percentage(value) => value as i8,
222            MessageProgress::Indeterminate => -1,
223        }
224    }
225}
226
227/// Screen rotation in degrees
228pub enum Rotation {
229    None,
230
231    Clockwise90,
232    Clockwise180,
233    Clockwise270,
234
235    CounterClockwise90,
236    CounterClockwise180,
237    CounterClockwise270,
238}
239
240impl Rotation {
241    pub fn get_env_value(&self) -> u32 {
242        match self {
243            Rotation::None => 0,
244
245            Rotation::Clockwise90 => 3,
246            Rotation::Clockwise180 => 2,
247            Rotation::Clockwise270 => 1,
248
249            Rotation::CounterClockwise90 => 1,
250            Rotation::CounterClockwise180 => 2,
251            Rotation::CounterClockwise270 => 3,
252        }
253    }
254}
255
256#[derive(Debug, Copy, Clone)]
257pub enum PixelFormat {
258    XRGB1555 = retro_pixel_format::RETRO_PIXEL_FORMAT_0RGB1555 as isize,
259    XRGB8888 = retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888 as isize,
260    RGB565 = retro_pixel_format::RETRO_PIXEL_FORMAT_RGB565 as isize,
261    UNKNOWN = retro_pixel_format::RETRO_PIXEL_FORMAT_UNKNOWN as isize,
262}
263
264impl PixelFormat {
265    #[inline]
266    pub fn bit_per_pixel(self) -> usize {
267        match self {
268            Self::XRGB1555 => 2,
269            Self::XRGB8888 => 4,
270            Self::RGB565 => 2,
271            Self::UNKNOWN => 0,
272        }
273    }
274}
275
276impl From<retro_pixel_format> for PixelFormat {
277    fn from(other: retro_pixel_format) -> Self {
278        match other {
279            retro_pixel_format::RETRO_PIXEL_FORMAT_0RGB1555 => Self::XRGB1555,
280            retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888 => Self::XRGB8888,
281            retro_pixel_format::RETRO_PIXEL_FORMAT_RGB565 => Self::RGB565,
282            _ => Self::UNKNOWN,
283        }
284    }
285}
286
287impl From<PixelFormat> for retro_pixel_format {
288    fn from(other: PixelFormat) -> Self {
289        match other {
290            PixelFormat::XRGB1555 => Self::RETRO_PIXEL_FORMAT_0RGB1555,
291            PixelFormat::XRGB8888 => Self::RETRO_PIXEL_FORMAT_XRGB8888,
292            PixelFormat::RGB565 => Self::RETRO_PIXEL_FORMAT_RGB565,
293            PixelFormat::UNKNOWN => Self::RETRO_PIXEL_FORMAT_UNKNOWN,
294        }
295    }
296}
297
298#[derive(Debug)]
299pub struct PerfCounter {
300    #[allow(unused)]
301    /// Borrowed by the `retro_perf_counter`.
302    pub(crate) ident: CString,
303    pub(crate) counter: retro_perf_counter,
304}
305
306#[derive(Debug, Default)]
307pub struct PerfCounters {
308    pub interface: Option<retro_perf_callback>,
309    pub counters: HashMap<&'static str, PerfCounter>,
310}
311
312#[derive(Debug, Default)]
313pub struct Position {
314    pub lat: f64,
315    pub lon: f64,
316    pub horiz_accuracy: f64,
317    pub vert_accuracy: f64,
318}
319
320/// Data structures used by experimental libretro environment function calls
321#[proc::unstable(feature = "env-commands")]
322pub mod unstable {
323    use super::PixelFormat;
324    use core::marker::PhantomData;
325    use rust_libretro_sys::*;
326
327    bitflags::bitflags! {
328        /// Tells the core if the frontend wants audio or video.
329        pub struct AudioVideoEnable: u32 {
330            /// When this bit is **not** set:
331            /// * The frontend wants the core: to not generate any video,
332            ///   including presenting frames via hardware acceleration.
333            /// * The frontend's video frame callback will do nothing.
334            /// * After running the frame, the video output of the next frame should be
335            ///   no different than if video was enabled, and saving and loading state
336            ///   should have no issues.
337            const ENABLE_VIDEO = 0b0001;
338
339            /// When this bit is **not** set:
340            /// * The frontend wants the core to not generate any audio.
341            /// * The frontend's audio callbacks will do nothing.
342            /// * After running the frame, the audio output of the next frame should be
343            ///   no different than if audio was enabled, and saving and loading state
344            ///   should have no issues.
345            const ENABLE_AUDIO = 0b0010;
346
347            /// When this bit is set:
348            /// * Guaranteed to be created by the same binary that will load them.
349            /// * Will not be written to or read from the disk.
350            /// * Suggest that the core assumes loading state will succeed.
351            /// * Suggest that the core updates its memory buffers in-place if possible.
352            /// * Suggest that the core skips clearing memory.
353            /// * Suggest that the core skips resetting the system.
354            /// * Suggest that the core may skip validation steps.
355            const USE_FAST_SAVESTATES = 0b0100;
356
357            /// When this bit is set:
358            /// * Used for a secondary core when running ahead.
359            /// * Indicates that the frontend will never need audio from the core.
360            /// * Suggests that the core may stop synthesizing audio, but this should not
361            ///   compromise emulation accuracy.
362            /// * Audio output for the next frame does not matter, and the frontend will
363            ///   never need an accurate audio state in the future.
364            /// * State will never be saved when using Hard Disable Audio.
365            const HARD_DISABLE_AUDIO = 0b1000;
366        }
367    }
368
369    bitflags::bitflags! {
370        /// Joypad button mask
371        pub struct JoypadState: u16 {
372            const B      = 0b0000_0000_0000_0001;
373            const Y      = 0b0000_0000_0000_0010;
374            const SELECT = 0b0000_0000_0000_0100;
375            const START  = 0b0000_0000_0000_1000;
376
377            const UP     = 0b0000_0000_0001_0000;
378            const DOWN   = 0b0000_0000_0010_0000;
379            const LEFT   = 0b0000_0000_0100_0000;
380            const RIGHT  = 0b0000_0000_1000_0000;
381
382            const A      = 0b0000_0001_0000_0000;
383            const X      = 0b0000_0010_0000_0000;
384            const L      = 0b0000_0100_0000_0000;
385            const R      = 0b0000_1000_0000_0000;
386
387            const L2     = 0b0001_0000_0000_0000;
388            const R2     = 0b0010_0000_0000_0000;
389            const L3     = 0b0100_0000_0000_0000;
390            const R3     = 0b1000_0000_0000_0000;
391        }
392    }
393
394    #[derive(Debug, Default)]
395    pub struct VfsInterfaceInfo {
396        pub(crate) supported_version: u32,
397        pub(crate) interface: Option<retro_vfs_interface>,
398    }
399
400    bitflags::bitflags! {
401        pub struct VfsFileOpenFlags: u32 {
402            const READ = RETRO_VFS_FILE_ACCESS_READ;
403            const WRITE = RETRO_VFS_FILE_ACCESS_WRITE;
404            const READ_WRITE = RETRO_VFS_FILE_ACCESS_READ_WRITE;
405            const UPDATE_EXISTING = RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
406        }
407    }
408
409    bitflags::bitflags! {
410        pub struct VfsFileOpenHints: u32 {
411            const NONE = RETRO_VFS_FILE_ACCESS_HINT_NONE;
412            const FREQUENT_ACCESS = RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
413        }
414    }
415
416    #[repr(i32)]
417    pub enum VfsSeekPosition {
418        Start = RETRO_VFS_SEEK_POSITION_START as i32,
419        Current = RETRO_VFS_SEEK_POSITION_CURRENT as i32,
420        End = RETRO_VFS_SEEK_POSITION_END as i32,
421    }
422
423    bitflags::bitflags! {
424        pub struct VfsStat: i32 {
425            const STAT_IS_VALID = RETRO_VFS_STAT_IS_VALID as i32;
426            const STAT_IS_DIRECTORY = RETRO_VFS_STAT_IS_DIRECTORY as i32;
427            const STAT_IS_CHARACTER_SPECIAL = RETRO_VFS_STAT_IS_CHARACTER_SPECIAL as i32;
428        }
429    }
430
431    bitflags::bitflags! {
432        pub struct MemoryAccess: u32 {
433            const WRITE = RETRO_MEMORY_ACCESS_WRITE;
434            const READ = RETRO_MEMORY_ACCESS_READ;
435        }
436    }
437
438    bitflags::bitflags! {
439        pub struct MemoryType: u32 {
440            const UNCACHED = 0;
441            const CACHED = RETRO_MEMORY_TYPE_CACHED;
442        }
443    }
444
445    // TODO: Can we get rid of the raw pointer and PhantomData in an ergonomic way?
446    pub struct Framebuffer<'a> {
447        pub data: *mut u8,
448        pub data_len: usize,
449        pub phantom: PhantomData<&'a mut [u8]>,
450
451        pub width: u32,
452        pub height: u32,
453        pub pitch: usize,
454        pub format: PixelFormat,
455        pub access_flags: MemoryAccess,
456        pub memory_flags: MemoryType,
457    }
458
459    impl<'a> Framebuffer<'a> {
460        pub fn borrow_slice(&self) -> &'a [u8] {
461            unsafe { std::slice::from_raw_parts(self.data, self.data_len) }
462        }
463
464        pub unsafe fn as_slice<'b>(&self) -> &'b [u8] {
465            unsafe { std::slice::from_raw_parts(self.data, self.data_len) }
466        }
467
468        pub fn borrow_slice_mut(&self) -> &'a mut [u8] {
469            unsafe { std::slice::from_raw_parts_mut(self.data, self.data_len) }
470        }
471
472        pub unsafe fn as_slice_mut<'b>(&self) -> &'b mut [u8] {
473            unsafe { std::slice::from_raw_parts_mut(self.data, self.data_len) }
474        }
475    }
476
477    pub trait HwRenderContextNegotiationInterface: std::fmt::Debug {
478        fn as_any(&self) -> &dyn std::any::Any;
479    }
480
481    impl HwRenderContextNegotiationInterface for retro_hw_render_context_negotiation_interface {
482        fn as_any(&self) -> &dyn std::any::Any {
483            self
484        }
485    }
486
487    #[cfg(feature = "vulkan")]
488    impl HwRenderContextNegotiationInterface for retro_hw_render_context_negotiation_interface_vulkan {
489        fn as_any(&self) -> &dyn std::any::Any {
490            self
491        }
492    }
493
494    #[test]
495    #[cfg(feature = "vulkan")]
496    fn retro_hw_render_context_negotiation_interface_vulkan_is_superset() {
497        assert!(
498            std::mem::size_of::<retro_hw_render_context_negotiation_interface_vulkan>()
499                >= std::mem::size_of::<retro_hw_render_context_negotiation_interface>(),
500        );
501    }
502}
503pub use unstable::*;