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::*;