1use enumflags2::{BitFlags, bitflags};
7use std::ffi::{CStr, c_char, c_void};
8use std::marker::PhantomData;
9use std::ptr::NonNull;
10use std::slice;
11
12use crate::raw::{
13 RETRO_MEMDESC_ALIGN_2, RETRO_MEMDESC_ALIGN_4, RETRO_MEMDESC_ALIGN_8, RETRO_MEMDESC_BIGENDIAN,
14 RETRO_MEMDESC_CONST, RETRO_MEMDESC_MINSIZE_2, RETRO_MEMDESC_MINSIZE_4, RETRO_MEMDESC_MINSIZE_8,
15 RETRO_MEMDESC_SAVE_RAM, RETRO_MEMDESC_SYSTEM_RAM, RETRO_MEMDESC_VIDEO_RAM,
16 RETRO_MEMORY_ACCESS_READ, RETRO_MEMORY_ACCESS_WRITE, RETRO_MEMORY_MASK, RETRO_MEMORY_ROM,
17 RETRO_MEMORY_RTC, RETRO_MEMORY_SAVE_RAM, RETRO_MEMORY_SYSTEM_RAM, RETRO_MEMORY_TYPE_CACHED,
18 RETRO_MEMORY_VIDEO_RAM, RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE,
19 RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT, RETRO_SERIALIZATION_QUIRK_FRONT_VARIABLE_SIZE,
20 RETRO_SERIALIZATION_QUIRK_INCOMPLETE, RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE,
21 RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT, RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION,
22 retro_framebuffer, retro_game_info_ext, retro_pixel_format,
23};
24
25#[bitflags]
26#[repr(u32)]
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28pub enum FramebufferMemoryAccess {
29 Write = RETRO_MEMORY_ACCESS_WRITE,
30 Read = RETRO_MEMORY_ACCESS_READ,
31}
32
33pub type FramebufferMemoryAccessFlags = BitFlags<FramebufferMemoryAccess>;
34
35#[bitflags]
36#[repr(u32)]
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
38pub enum FramebufferMemoryType {
39 Cached = RETRO_MEMORY_TYPE_CACHED,
40}
41
42pub type FramebufferMemoryTypes = BitFlags<FramebufferMemoryType>;
43
44#[bitflags]
45#[repr(u64)]
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47pub enum MemoryDescriptorFlag {
48 Constant = RETRO_MEMDESC_CONST,
49 BigEndian = RETRO_MEMDESC_BIGENDIAN,
50 SystemRam = RETRO_MEMDESC_SYSTEM_RAM,
51 SaveRam = RETRO_MEMDESC_SAVE_RAM,
52 VideoRam = RETRO_MEMDESC_VIDEO_RAM,
53}
54
55pub type MemoryDescriptorFlags = BitFlags<MemoryDescriptorFlag>;
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58pub enum MemoryDescriptorAlignment {
59 TwoBytes,
60 FourBytes,
61 EightBytes,
62}
63
64impl MemoryDescriptorAlignment {
65 pub const fn as_raw_flag(self) -> u64 {
66 match self {
67 Self::TwoBytes => RETRO_MEMDESC_ALIGN_2,
68 Self::FourBytes => RETRO_MEMDESC_ALIGN_4,
69 Self::EightBytes => RETRO_MEMDESC_ALIGN_8,
70 }
71 }
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
75pub enum MemoryDescriptorMinAccessSize {
76 TwoBytes,
77 FourBytes,
78 EightBytes,
79}
80
81impl MemoryDescriptorMinAccessSize {
82 pub const fn as_raw_flag(self) -> u64 {
83 match self {
84 Self::TwoBytes => RETRO_MEMDESC_MINSIZE_2,
85 Self::FourBytes => RETRO_MEMDESC_MINSIZE_4,
86 Self::EightBytes => RETRO_MEMDESC_MINSIZE_8,
87 }
88 }
89}
90
91#[bitflags]
92#[repr(u64)]
93#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
94pub enum SerializationQuirk {
95 Incomplete = RETRO_SERIALIZATION_QUIRK_INCOMPLETE,
96 MustInitialize = RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE,
97 CoreVariableSize = RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE,
98 FrontendVariableSize = RETRO_SERIALIZATION_QUIRK_FRONT_VARIABLE_SIZE,
99 SingleSession = RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION,
100 EndianDependent = RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT,
101 PlatformDependent = RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT,
102}
103
104pub type SerializationQuirks = BitFlags<SerializationQuirk>;
105
106#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107pub struct ExtendedGameInfo<'a> {
108 pub full_path: Option<&'a CStr>,
109 pub archive_path: Option<&'a CStr>,
110 pub archive_file: Option<&'a CStr>,
111 pub dir: Option<&'a CStr>,
112 pub name: Option<&'a CStr>,
113 pub extension: Option<&'a CStr>,
114 pub meta: Option<&'a CStr>,
115 pub data: Option<&'a [u8]>,
116 pub file_in_archive: bool,
117 pub persistent_data: bool,
118}
119
120impl<'a> ExtendedGameInfo<'a> {
121 pub(crate) unsafe fn from_raw(raw: &'a retro_game_info_ext) -> Self {
122 Self {
123 full_path: unsafe { cstr_from_ptr(raw.full_path) },
124 archive_path: unsafe { cstr_from_ptr(raw.archive_path) },
125 archive_file: unsafe { cstr_from_ptr(raw.archive_file) },
126 dir: unsafe { cstr_from_ptr(raw.dir) },
127 name: unsafe { cstr_from_ptr(raw.name) },
128 extension: unsafe { cstr_from_ptr(raw.ext) },
129 meta: unsafe { cstr_from_ptr(raw.meta) },
130 data: if raw.data.is_null() {
131 None
132 } else {
133 Some(unsafe { slice::from_raw_parts(raw.data.cast::<u8>(), raw.size) })
136 },
137 file_in_archive: raw.file_in_archive,
138 persistent_data: raw.persistent_data,
139 }
140 }
141}
142
143unsafe fn cstr_from_ptr<'a>(ptr: *const c_char) -> Option<&'a CStr> {
144 if ptr.is_null() {
145 None
146 } else {
147 Some(unsafe { CStr::from_ptr(ptr) })
149 }
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
153pub struct SoftwareFramebufferRequest {
154 pub width: u32,
155 pub height: u32,
156 pub access: FramebufferMemoryAccessFlags,
157}
158
159impl SoftwareFramebufferRequest {
160 pub fn new(width: u32, height: u32) -> Self {
161 Self {
162 width,
163 height,
164 access: FramebufferMemoryAccessFlags::from(FramebufferMemoryAccess::Write),
165 }
166 }
167
168 pub fn with_access(mut self, access: FramebufferMemoryAccessFlags) -> Self {
169 self.access = access;
170 self
171 }
172
173 pub(crate) fn into_raw(self) -> retro_framebuffer {
174 retro_framebuffer {
175 width: self.width,
176 height: self.height,
177 access_flags: self.access.bits(),
178 ..retro_framebuffer::default()
179 }
180 }
181}
182
183#[derive(Debug)]
184pub struct SoftwareFramebuffer {
185 data: NonNull<c_void>,
186 width: u32,
187 height: u32,
188 pitch: usize,
189 format: retro_pixel_format,
190 access: FramebufferMemoryAccessFlags,
191 memory: FramebufferMemoryTypes,
192}
193
194impl SoftwareFramebuffer {
195 pub(crate) fn from_raw(framebuffer: retro_framebuffer) -> Option<Self> {
196 Some(Self {
197 data: NonNull::new(framebuffer.data)?,
198 width: framebuffer.width,
199 height: framebuffer.height,
200 pitch: framebuffer.pitch,
201 format: framebuffer.format,
202 access: FramebufferMemoryAccessFlags::from_bits_truncate(framebuffer.access_flags),
203 memory: FramebufferMemoryTypes::from_bits_truncate(framebuffer.memory_flags),
204 })
205 }
206
207 pub const fn width(&self) -> u32 {
208 self.width
209 }
210
211 pub const fn height(&self) -> u32 {
212 self.height
213 }
214
215 pub const fn pitch(&self) -> usize {
216 self.pitch
217 }
218
219 pub const fn format(&self) -> retro_pixel_format {
220 self.format
221 }
222
223 pub const fn access(&self) -> FramebufferMemoryAccessFlags {
224 self.access
225 }
226
227 pub const fn memory(&self) -> FramebufferMemoryTypes {
228 self.memory
229 }
230
231 pub fn bytes(&self) -> Option<&[u8]> {
232 if !self.access.contains(FramebufferMemoryAccess::Read) {
233 return None;
234 }
235 let len = self.byte_len()?;
236 Some(unsafe { slice::from_raw_parts(self.data.as_ptr().cast::<u8>(), len) })
239 }
240
241 pub fn bytes_mut(&mut self) -> Option<&mut [u8]> {
242 if !self.access.contains(FramebufferMemoryAccess::Write) {
243 return None;
244 }
245 let len = self.byte_len()?;
246 Some(unsafe { slice::from_raw_parts_mut(self.data.as_ptr().cast::<u8>(), len) })
249 }
250
251 pub(crate) fn video_refresh_args(&self) -> (*const c_void, u32, u32, usize) {
252 (
253 self.data.as_ptr().cast_const(),
254 self.width,
255 self.height,
256 self.pitch,
257 )
258 }
259
260 fn byte_len(&self) -> Option<usize> {
261 self.pitch.checked_mul(self.height as usize)
262 }
263}
264
265#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
266pub struct MemoryMapOffset(usize);
267
268impl MemoryMapOffset {
269 pub const fn new(offset: usize) -> Self {
270 Self(offset)
271 }
272
273 pub const fn as_usize(self) -> usize {
274 self.0
275 }
276}
277
278impl From<usize> for MemoryMapOffset {
279 fn from(offset: usize) -> Self {
280 Self::new(offset)
281 }
282}
283
284#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
285pub struct EmulatedAddress(usize);
286
287impl EmulatedAddress {
288 pub const fn new(address: usize) -> Self {
289 Self(address)
290 }
291
292 pub const fn as_usize(self) -> usize {
293 self.0
294 }
295}
296
297impl From<usize> for EmulatedAddress {
298 fn from(address: usize) -> Self {
299 Self::new(address)
300 }
301}
302
303#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
304pub struct MemoryMapMask(usize);
305
306impl MemoryMapMask {
307 pub const fn new(mask: usize) -> Self {
308 Self(mask)
309 }
310
311 pub const fn zero() -> Self {
312 Self(0)
313 }
314
315 pub const fn as_usize(self) -> usize {
316 self.0
317 }
318}
319
320impl From<usize> for MemoryMapMask {
321 fn from(mask: usize) -> Self {
322 Self::new(mask)
323 }
324}
325
326#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
327pub struct MemoryMapLen(usize);
328
329impl MemoryMapLen {
330 pub const fn new(len: usize) -> Self {
331 Self(len)
332 }
333
334 pub const fn as_usize(self) -> usize {
335 self.0
336 }
337}
338
339impl From<usize> for MemoryMapLen {
340 fn from(len: usize) -> Self {
341 Self::new(len)
342 }
343}
344
345#[derive(Debug)]
346pub struct MemoryMapDescriptor<'a> {
347 pub flags: MemoryDescriptorFlags,
348 pub alignment: Option<MemoryDescriptorAlignment>,
349 pub min_access_size: Option<MemoryDescriptorMinAccessSize>,
350 pub ptr: Option<NonNull<u8>>,
351 pub offset: MemoryMapOffset,
352 pub start: EmulatedAddress,
353 pub select: MemoryMapMask,
354 pub disconnect: MemoryMapMask,
355 pub len: MemoryMapLen,
356 pub addrspace: Option<String>,
357 _lifetime: PhantomData<&'a mut [u8]>,
358}
359
360impl<'a> MemoryMapDescriptor<'a> {
361 pub fn new_inaccessible(
362 addrspace: impl Into<Option<String>>,
363 start: impl Into<EmulatedAddress>,
364 select: impl Into<MemoryMapMask>,
365 ) -> Self {
366 Self {
367 flags: MemoryDescriptorFlags::empty(),
368 alignment: None,
369 min_access_size: None,
370 ptr: None,
371 offset: MemoryMapOffset::new(0),
372 start: start.into(),
373 select: select.into(),
374 disconnect: MemoryMapMask::zero(),
375 len: MemoryMapLen::new(0),
376 addrspace: addrspace.into(),
377 _lifetime: PhantomData,
378 }
379 }
380
381 pub fn from_slice(
382 addrspace: impl Into<Option<String>>,
383 start: impl Into<EmulatedAddress>,
384 memory: &'a mut [u8],
385 ) -> Self {
386 Self {
387 flags: MemoryDescriptorFlags::empty(),
388 alignment: None,
389 min_access_size: None,
390 ptr: NonNull::new(memory.as_mut_ptr()),
391 offset: MemoryMapOffset::new(0),
392 start: start.into(),
393 select: MemoryMapMask::zero(),
394 disconnect: MemoryMapMask::zero(),
395 len: MemoryMapLen::new(memory.len()),
396 addrspace: addrspace.into(),
397 _lifetime: PhantomData,
398 }
399 }
400
401 pub fn with_flags(mut self, flags: MemoryDescriptorFlags) -> Self {
402 self.flags = flags;
403 self
404 }
405
406 pub fn with_alignment(mut self, alignment: MemoryDescriptorAlignment) -> Self {
407 self.alignment = Some(alignment);
408 self
409 }
410
411 pub fn with_min_access_size(mut self, size: MemoryDescriptorMinAccessSize) -> Self {
412 self.min_access_size = Some(size);
413 self
414 }
415
416 pub(crate) fn raw_flags(&self) -> u64 {
417 self.flags.bits()
418 | self
419 .alignment
420 .map(MemoryDescriptorAlignment::as_raw_flag)
421 .unwrap_or(0)
422 | self
423 .min_access_size
424 .map(MemoryDescriptorMinAccessSize::as_raw_flag)
425 .unwrap_or(0)
426 }
427
428 pub fn with_offset(mut self, offset: impl Into<MemoryMapOffset>) -> Self {
429 self.offset = offset.into();
430 self
431 }
432
433 pub fn with_select(mut self, select: impl Into<MemoryMapMask>) -> Self {
434 self.select = select.into();
435 self
436 }
437
438 pub fn with_disconnect(mut self, disconnect: impl Into<MemoryMapMask>) -> Self {
439 self.disconnect = disconnect.into();
440 self
441 }
442
443 pub fn with_len(mut self, len: impl Into<MemoryMapLen>) -> Self {
444 self.len = len.into();
445 self
446 }
447}
448
449#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
450pub enum SavestateContext {
451 #[default]
452 Normal,
453 RunaheadSameInstance,
454 RunaheadSameBinary,
455 RollbackNetplay,
456 Unknown(i32),
457}
458
459impl SavestateContext {
460 pub const fn from_raw(context: i32) -> Self {
461 match context {
462 0 => Self::Normal,
463 1 => Self::RunaheadSameInstance,
464 2 => Self::RunaheadSameBinary,
465 3 => Self::RollbackNetplay,
466 other => Self::Unknown(other),
467 }
468 }
469
470 pub const fn as_raw(self) -> i32 {
471 match self {
472 Self::Normal => 0,
473 Self::RunaheadSameInstance => 1,
474 Self::RunaheadSameBinary => 2,
475 Self::RollbackNetplay => 3,
476 Self::Unknown(context) => context,
477 }
478 }
479}
480
481#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
482pub enum MemoryRegion {
483 SaveRam,
484 Rtc,
485 SystemRam,
486 VideoRam,
487 Rom,
488 Unknown(u32),
489}
490
491impl MemoryRegion {
492 pub const fn from_raw(id: u32) -> Self {
493 match id & RETRO_MEMORY_MASK {
494 RETRO_MEMORY_SAVE_RAM => Self::SaveRam,
495 RETRO_MEMORY_RTC => Self::Rtc,
496 RETRO_MEMORY_SYSTEM_RAM => Self::SystemRam,
497 RETRO_MEMORY_VIDEO_RAM => Self::VideoRam,
498 RETRO_MEMORY_ROM => Self::Rom,
499 _ => Self::Unknown(id),
500 }
501 }
502
503 pub const fn as_raw(self) -> u32 {
504 match self {
505 Self::SaveRam => RETRO_MEMORY_SAVE_RAM,
506 Self::Rtc => RETRO_MEMORY_RTC,
507 Self::SystemRam => RETRO_MEMORY_SYSTEM_RAM,
508 Self::VideoRam => RETRO_MEMORY_VIDEO_RAM,
509 Self::Rom => RETRO_MEMORY_ROM,
510 Self::Unknown(id) => id,
511 }
512 }
513}
514
515#[derive(Debug)]
516pub enum CoreMemory<'a> {
517 ReadOnly(&'a [u8]),
518 ReadWrite(&'a mut [u8]),
519}
520
521impl<'a> CoreMemory<'a> {
522 pub fn read_only(data: &'a [u8]) -> Self {
523 Self::ReadOnly(data)
524 }
525
526 pub fn read_write(data: &'a mut [u8]) -> Self {
527 Self::ReadWrite(data)
528 }
529
530 pub fn len(&self) -> usize {
531 match self {
532 Self::ReadOnly(data) => data.len(),
533 Self::ReadWrite(data) => data.len(),
534 }
535 }
536
537 pub fn is_empty(&self) -> bool {
538 self.len() == 0
539 }
540
541 pub fn as_slice(&self) -> &[u8] {
542 match self {
543 Self::ReadOnly(data) => data,
544 Self::ReadWrite(data) => data,
545 }
546 }
547
548 pub(crate) fn as_mut_ptr(&mut self) -> *mut c_void {
549 if self.is_empty() {
550 return std::ptr::null_mut();
551 }
552 match self {
553 Self::ReadOnly(data) => data.as_ptr().cast_mut().cast::<c_void>(),
554 Self::ReadWrite(data) => data.as_mut_ptr().cast::<c_void>(),
555 }
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use super::{
562 CoreMemory, FramebufferMemoryAccess, FramebufferMemoryAccessFlags, FramebufferMemoryType,
563 FramebufferMemoryTypes, MemoryDescriptorAlignment, MemoryDescriptorFlag,
564 MemoryDescriptorFlags, MemoryDescriptorMinAccessSize, MemoryMapDescriptor, MemoryMapLen,
565 MemoryMapMask, MemoryMapOffset, MemoryRegion, SavestateContext, SerializationQuirk,
566 SerializationQuirks,
567 };
568
569 #[test]
570 fn known_memory_regions_round_trip_to_libretro_ids() {
571 let regions = [
572 MemoryRegion::SaveRam,
573 MemoryRegion::Rtc,
574 MemoryRegion::SystemRam,
575 MemoryRegion::VideoRam,
576 MemoryRegion::Rom,
577 ];
578
579 for region in regions {
580 assert_eq!(MemoryRegion::from_raw(region.as_raw()), region);
581 }
582 }
583
584 #[test]
585 fn unknown_memory_region_preserves_original_id() {
586 assert_eq!(MemoryRegion::from_raw(99), MemoryRegion::Unknown(99));
587 assert_eq!(MemoryRegion::Unknown(99).as_raw(), 99);
588 }
589
590 #[test]
591 fn memory_region_masks_libretro_memory_type_bits() {
592 let id_with_extra_bits = crate::raw::RETRO_MEMORY_SAVE_RAM | 0x100;
593
594 assert_eq!(
595 MemoryRegion::from_raw(id_with_extra_bits),
596 MemoryRegion::SaveRam
597 );
598 }
599
600 #[test]
601 fn core_memory_wraps_readonly_and_readwrite_slices() {
602 let readonly = [1, 2, 3];
603 let readonly_memory = CoreMemory::read_only(&readonly);
604 assert_eq!(readonly_memory.len(), 3);
605 assert_eq!(readonly_memory.as_slice(), &[1, 2, 3]);
606
607 let mut readwrite = [4, 5];
608 let mut readwrite_memory = CoreMemory::read_write(&mut readwrite);
609 assert_eq!(readwrite_memory.len(), 2);
610 assert_eq!(readwrite_memory.as_slice(), &[4, 5]);
611 assert!(!readwrite_memory.as_mut_ptr().is_null());
612
613 let mut empty = CoreMemory::read_only(&[]);
614 assert!(empty.as_mut_ptr().is_null());
615 }
616
617 #[test]
618 fn savestate_contexts_round_trip_to_libretro_ids() {
619 let contexts = [
620 SavestateContext::Normal,
621 SavestateContext::RunaheadSameInstance,
622 SavestateContext::RunaheadSameBinary,
623 SavestateContext::RollbackNetplay,
624 ];
625
626 for context in contexts {
627 assert_eq!(SavestateContext::from_raw(context.as_raw()), context);
628 }
629 }
630
631 #[test]
632 fn unknown_savestate_context_preserves_original_id() {
633 assert_eq!(
634 SavestateContext::from_raw(i32::MAX),
635 SavestateContext::Unknown(i32::MAX)
636 );
637 assert_eq!(SavestateContext::Unknown(99).as_raw(), 99);
638 }
639
640 #[test]
641 fn serialization_quirks_encode_libretro_flags() {
642 let quirks = SerializationQuirks::from(SerializationQuirk::MustInitialize)
643 | SerializationQuirk::PlatformDependent;
644
645 assert!(quirks.contains(SerializationQuirk::MustInitialize));
646 assert!(quirks.contains(SerializationQuirk::PlatformDependent));
647 assert_eq!(
648 quirks.bits(),
649 crate::raw::RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE
650 | crate::raw::RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT
651 );
652 }
653
654 #[test]
655 fn memory_descriptor_flags_encode_libretro_bits() {
656 let flags = MemoryDescriptorFlags::from(MemoryDescriptorFlag::Constant)
657 | MemoryDescriptorFlag::BigEndian
658 | MemoryDescriptorFlag::SaveRam;
659
660 assert_eq!(
661 flags.bits(),
662 crate::raw::RETRO_MEMDESC_CONST
663 | crate::raw::RETRO_MEMDESC_BIGENDIAN
664 | crate::raw::RETRO_MEMDESC_SAVE_RAM
665 );
666 }
667
668 #[test]
669 fn memory_descriptor_alignment_and_min_size_are_exclusive_values() {
670 assert_eq!(
671 MemoryDescriptorAlignment::TwoBytes.as_raw_flag(),
672 crate::raw::RETRO_MEMDESC_ALIGN_2
673 );
674 assert_eq!(
675 MemoryDescriptorAlignment::FourBytes.as_raw_flag(),
676 crate::raw::RETRO_MEMDESC_ALIGN_4
677 );
678 assert_eq!(
679 MemoryDescriptorAlignment::EightBytes.as_raw_flag(),
680 crate::raw::RETRO_MEMDESC_ALIGN_8
681 );
682 assert_eq!(
683 MemoryDescriptorMinAccessSize::TwoBytes.as_raw_flag(),
684 crate::raw::RETRO_MEMDESC_MINSIZE_2
685 );
686 assert_eq!(
687 MemoryDescriptorMinAccessSize::FourBytes.as_raw_flag(),
688 crate::raw::RETRO_MEMDESC_MINSIZE_4
689 );
690 assert_eq!(
691 MemoryDescriptorMinAccessSize::EightBytes.as_raw_flag(),
692 crate::raw::RETRO_MEMDESC_MINSIZE_8
693 );
694 }
695
696 #[test]
697 fn memory_map_descriptor_from_slice_tracks_typed_fields() {
698 let mut ram = [0u8; 8];
699 let descriptor =
700 MemoryMapDescriptor::from_slice(Some("WRAM".to_string()), 0x7e0000usize, &mut ram)
701 .with_flags(MemoryDescriptorFlags::from(MemoryDescriptorFlag::SystemRam))
702 .with_alignment(MemoryDescriptorAlignment::FourBytes)
703 .with_min_access_size(MemoryDescriptorMinAccessSize::TwoBytes)
704 .with_offset(MemoryMapOffset::new(2))
705 .with_select(MemoryMapMask::new(0xff0000))
706 .with_disconnect(MemoryMapMask::new(0x80))
707 .with_len(MemoryMapLen::new(4));
708
709 assert!(descriptor.ptr.is_some());
710 assert_eq!(descriptor.offset, MemoryMapOffset::new(2));
711 assert_eq!(descriptor.select, MemoryMapMask::new(0xff0000));
712 assert_eq!(descriptor.disconnect, MemoryMapMask::new(0x80));
713 assert_eq!(descriptor.len, MemoryMapLen::new(4));
714 assert_eq!(
715 descriptor.raw_flags(),
716 crate::raw::RETRO_MEMDESC_SYSTEM_RAM
717 | crate::raw::RETRO_MEMDESC_ALIGN_4
718 | crate::raw::RETRO_MEMDESC_MINSIZE_2
719 );
720 }
721
722 #[test]
723 fn framebuffer_memory_flags_encode_libretro_bits() {
724 let access = FramebufferMemoryAccessFlags::from(FramebufferMemoryAccess::Read)
725 | FramebufferMemoryAccess::Write;
726 let types = FramebufferMemoryTypes::from(FramebufferMemoryType::Cached);
727
728 assert_eq!(
729 access.bits(),
730 crate::raw::RETRO_MEMORY_ACCESS_READ | crate::raw::RETRO_MEMORY_ACCESS_WRITE
731 );
732 assert_eq!(types.bits(), crate::raw::RETRO_MEMORY_TYPE_CACHED);
733 }
734}