libretro_backend/
lib.rs

1#[doc(hidden)]
2pub extern crate libc;
3#[doc(hidden)]
4pub extern crate libretro_sys;
5
6use std::mem;
7use std::ptr;
8use std::slice;
9use std::ffi::{CStr, CString};
10use std::cmp::max;
11
12pub use libretro_sys::{PixelFormat, Region};
13
14pub struct CoreInfo {
15    library_name: CString,
16    library_version: CString,
17    supported_romfile_extensions: CString,
18    require_path_when_loading_roms: bool,
19    allow_frontend_to_extract_archives: bool
20}
21
22impl CoreInfo {
23    pub fn new( name: &str, version: &str ) -> CoreInfo {
24        CoreInfo {
25            library_name: CString::new( name ).unwrap(),
26            library_version: CString::new( version ).unwrap(),
27            supported_romfile_extensions: CString::new( "" ).unwrap(),
28            require_path_when_loading_roms: false,
29            allow_frontend_to_extract_archives: true
30        }
31    }
32
33    pub fn supports_roms_with_extension( mut self, mut extension: &str ) -> Self {
34        if extension.starts_with( "." ) {
35            extension = &extension[ 1.. ];
36        }
37
38        let mut string = CString::new( "" ).unwrap();
39        mem::swap( &mut string, &mut self.supported_romfile_extensions );
40
41        let mut vec = string.into_bytes();
42        if vec.is_empty() == false {
43            vec.push( '|' as u8 );
44        }
45
46        vec.extend_from_slice( extension.as_bytes() );
47
48        match extension {
49            "gz" | "xz"  |
50            "zip" | "rar" | "7z" | "tar" | "tgz" | "txz" | "bz2" |
51            "tar.gz" | "tar.bz2"| "tar.xz" => {
52                self.allow_frontend_to_extract_archives = false;
53            },
54            _ => {}
55        }
56
57        self.supported_romfile_extensions = CString::new( vec ).unwrap();
58        self
59    }
60
61    pub fn requires_path_when_loading_roms( mut self ) -> Self {
62        self.require_path_when_loading_roms = true;
63        self
64    }
65}
66
67pub struct AudioVideoInfo {
68    width: u32,
69    height: u32,
70    max_width: u32,
71    max_height: u32,
72    frames_per_second: f64,
73    audio_sample_rate: f64,
74    aspect_ratio: Option< f32 >,
75    pixel_format: PixelFormat,
76    game_region: Option< Region >
77}
78
79impl AudioVideoInfo {
80    pub fn new() -> AudioVideoInfo {
81        AudioVideoInfo {
82            width: 0,
83            height: 0,
84            max_width: 0,
85            max_height: 0,
86            frames_per_second: 0.0,
87            aspect_ratio: None,
88            pixel_format: PixelFormat::RGB565,
89            audio_sample_rate: 0.0,
90            game_region: None
91        }
92    }
93
94    pub fn video( mut self, width: u32, height: u32, frames_per_second: f64, pixel_format: PixelFormat ) -> Self {
95        self.width = width;
96        self.height = height;
97        self.max_width = max( self.max_width, width );
98        self.max_height = max( self.max_height, height );
99        self.frames_per_second = frames_per_second;
100        self.pixel_format = pixel_format;
101        self
102    }
103
104    pub fn max_video_size( mut self, max_width: u32, max_height: u32 ) -> Self {
105        self.max_width = max( self.max_width, max_width );
106        self.max_height = max( self.max_height, max_height );
107        self
108    }
109
110    pub fn aspect_ratio( mut self, aspect_ratio: f32 ) -> Self {
111        self.aspect_ratio = Some( aspect_ratio );
112        self
113    }
114
115    pub fn audio( mut self, sample_rate: f64 ) -> Self {
116        self.audio_sample_rate = sample_rate;
117        self
118    }
119
120    pub fn region( mut self, game_region: Region ) -> Self {
121        self.game_region = Some( game_region );
122        self
123    }
124
125    fn infer_game_region( &self ) -> Region {
126        self.game_region.unwrap_or_else( || {
127            if self.frames_per_second > 59.0 {
128                Region::NTSC
129            } else {
130                Region::PAL
131            }
132        })
133    }
134}
135
136pub struct GameData {
137    path: Option< String >,
138
139    // The 'static lifetime here is a lie, but it's safe anyway
140    // since the user doesn't get direct access to this reference,
141    // and he has to give us a GameData object back in on_unload_game,
142    // and since we're the only source of those he has to give us
143    // the one that he got in on_load_game.
144    data: Option< &'static [u8] >
145}
146
147impl GameData {
148    pub fn path( &self ) -> Option< &str > {
149        self.path.as_ref().map( |path| &path[..] )
150    }
151
152    pub fn data( &self ) -> Option< &[u8] > {
153        self.data.map( |data| data as &[u8] )
154    }
155
156    pub fn is_empty( &self ) -> bool {
157        self.path().is_none() && self.data().is_none()
158    }
159}
160
161pub enum LoadGameResult {
162    Success( AudioVideoInfo ),
163    Failed( GameData )
164}
165
166#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
167pub enum JoypadButton {
168    A,
169    B,
170    X,
171    Y,
172    Select,
173    Start,
174    Up,
175    Down,
176    Left,
177    Right,
178    L1,
179    L2,
180    L3,
181    R1,
182    R2,
183    R3
184}
185
186pub trait Core: Default {
187    fn info() -> CoreInfo;
188    fn on_load_game( &mut self, game_data: GameData ) -> LoadGameResult;
189    fn on_unload_game( &mut self ) -> GameData;
190    fn on_run( &mut self, handle: &mut RuntimeHandle );
191    fn on_reset( &mut self );
192    fn save_memory( &mut self ) -> Option< &mut [u8] > {
193        None
194    }
195    fn rtc_memory( &mut self ) -> Option< &mut [u8] > {
196        None
197    }
198    fn system_memory( &mut self ) -> Option< &mut [u8] > {
199        None
200    }
201    fn video_memory( &mut self ) -> Option< &mut [u8] > {
202        None
203    }
204}
205
206static mut ENVIRONMENT_CALLBACK: Option< libretro_sys::EnvironmentFn > = None;
207
208#[doc(hidden)]
209pub struct Retro< B: Core > {
210    video_refresh_callback: Option< libretro_sys::VideoRefreshFn >,
211    audio_sample_callback: Option< libretro_sys::AudioSampleFn >,
212    audio_sample_batch_callback: Option< libretro_sys::AudioSampleBatchFn >,
213    input_poll_callback: Option< libretro_sys::InputPollFn >,
214    input_state_callback: Option< libretro_sys::InputStateFn >,
215
216    core: B,
217
218    is_game_loaded: bool,
219    av_info: AudioVideoInfo,
220    total_audio_samples_uploaded: usize
221}
222
223macro_rules! set_callback {
224    ($output: expr, $input: expr) => (
225        unsafe {
226            if $input == mem::transmute( 0 as usize ) {
227                $output = None;
228            } else {
229                $output = Some( $input );
230            }
231        }
232    )
233}
234
235impl< B: Core > Retro< B > {
236    fn new( core: B ) -> Self {
237        Retro {
238            video_refresh_callback: None,
239            audio_sample_callback: None,
240            audio_sample_batch_callback: None,
241            input_poll_callback: None,
242            input_state_callback: None,
243
244            core: core,
245
246            is_game_loaded: false,
247            av_info: AudioVideoInfo::new(),
248            total_audio_samples_uploaded: 0
249        }
250    }
251
252    #[must_use]
253    unsafe fn call_environment< T >( &mut self, command: libc::c_uint, pointer: &T ) -> Result< (), () > {
254        let ok = ENVIRONMENT_CALLBACK.unwrap()( command, mem::transmute( pointer ) );
255        if ok {
256            Ok(())
257        } else {
258            Err(())
259        }
260    }
261
262    pub fn on_get_system_info( info: *mut libretro_sys::SystemInfo ) {
263        assert_ne!( info, ptr::null_mut() );
264        let info = unsafe { &mut *info };
265
266        // Pointers in SystemInfo have to be statically allocated,
267        // which is why we do this.
268        static mut INFO: Option< *const CoreInfo > = None;
269        let core_info = unsafe {
270            if INFO.is_none() {
271                INFO = Some( Box::into_raw( Box::new( B::info() ) ) );
272            }
273            INFO.map( |core_info| &*core_info ).unwrap()
274        };
275
276        info.library_name = core_info.library_name.as_ptr();
277        info.library_version = core_info.library_version.as_ptr();
278        info.valid_extensions = core_info.supported_romfile_extensions.as_ptr();
279        info.need_fullpath = core_info.require_path_when_loading_roms;
280        info.block_extract = core_info.allow_frontend_to_extract_archives == false;
281    }
282
283    pub fn on_set_environment( callback: libretro_sys::EnvironmentFn ) {
284        set_callback!( ENVIRONMENT_CALLBACK, callback );
285    }
286
287    pub fn on_set_video_refresh( &mut self, callback: libretro_sys::VideoRefreshFn ) {
288        set_callback!( self.video_refresh_callback, callback );
289    }
290
291    pub fn on_set_audio_sample( &mut self, callback: libretro_sys::AudioSampleFn ) {
292        set_callback!( self.audio_sample_callback, callback );
293    }
294
295    pub fn on_set_audio_sample_batch( &mut self, callback: libretro_sys::AudioSampleBatchFn ) {
296        set_callback!( self.audio_sample_batch_callback, callback );
297    }
298
299    pub fn on_set_input_poll( &mut self, callback: libretro_sys::InputPollFn ) {
300        set_callback!( self.input_poll_callback, callback );
301    }
302
303    pub fn on_set_input_state( &mut self, callback: libretro_sys::InputStateFn ) {
304        set_callback!( self.input_state_callback, callback );
305    }
306
307    pub fn on_get_system_av_info( &mut self, info: *mut libretro_sys::SystemAvInfo ) {
308        assert_ne!( info, ptr::null_mut() );
309        let info = unsafe { &mut *info };
310
311        info.geometry.base_width = self.av_info.width as libc::c_uint;
312        info.geometry.base_height = self.av_info.height as libc::c_uint;
313        info.geometry.max_width = self.av_info.max_width as libc::c_uint;
314        info.geometry.max_height = self.av_info.max_height as libc::c_uint;
315        info.geometry.aspect_ratio = self.av_info.aspect_ratio.unwrap_or( 0.0 );
316        info.timing.fps = self.av_info.frames_per_second;
317        info.timing.sample_rate = self.av_info.audio_sample_rate;
318    }
319
320    pub fn on_set_controller_port_device( &mut self, _port: libc::c_uint, _device: libc::c_uint ) {
321    }
322
323    pub fn on_reset( &mut self ) {
324        self.core.on_reset();
325    }
326
327    pub fn on_load_game( &mut self, game_info: *const libretro_sys::GameInfo ) -> bool {
328        assert_eq!( self.is_game_loaded, false );
329
330        let game_info = if game_info == ptr::null() {
331            None
332        } else {
333            Some( unsafe { &*game_info } )
334        };
335
336        let game_data = match game_info {
337            Some( game_info ) => {
338                let path = if game_info.path == ptr::null() {
339                    None
340                } else {
341                    unsafe {
342                        CStr::from_ptr( game_info.path ).to_str().ok().map( |path| path.to_owned() )
343                    }
344                };
345
346                let data = if game_info.data == ptr::null() && game_info.size == 0 {
347                    None
348                } else {
349                    unsafe {
350                        Some( slice::from_raw_parts( game_info.data as *const u8, game_info.size ) )
351                    }
352                };
353
354                GameData {
355                    path: path,
356                    data: data
357                }
358            },
359            None => {
360                GameData {
361                    path: None,
362                    data: None
363                }
364            }
365        };
366
367        let result = self.core.on_load_game( game_data );
368        match result {
369            LoadGameResult::Success( av_info ) => {
370                self.av_info = av_info;
371                unsafe {
372                    let pixel_format = self.av_info.pixel_format;
373                    self.call_environment( libretro_sys::ENVIRONMENT_SET_PIXEL_FORMAT, &pixel_format ).unwrap();
374                }
375
376                self.is_game_loaded = true;
377                true
378            },
379            LoadGameResult::Failed( _ ) => false
380        }
381    }
382
383    pub fn on_load_game_special( &mut self, _game_type: libc::c_uint, _info: *const libretro_sys::GameInfo, _num_info: libc::size_t ) -> bool {
384        false
385    }
386
387    pub fn on_run( &mut self ) {
388        let mut handle = RuntimeHandle {
389            video_refresh_callback: self.video_refresh_callback.unwrap(),
390            input_state_callback: self.input_state_callback.unwrap(),
391            audio_sample_batch_callback: self.audio_sample_batch_callback.unwrap(),
392            upload_video_frame_already_called: false,
393            audio_samples_uploaded: 0,
394
395            video_width: self.av_info.width,
396            video_height: self.av_info.height,
397            video_frame_bytes_per_pixel: match self.av_info.pixel_format {
398                PixelFormat::ARGB1555 | PixelFormat::RGB565 => 2,
399                PixelFormat::ARGB8888 => 4
400            }
401        };
402
403        unsafe {
404            self.input_poll_callback.unwrap()();
405        }
406
407        self.core.on_run( &mut handle );
408
409        self.total_audio_samples_uploaded += handle.audio_samples_uploaded;
410        let required_audio_sample_count_per_frame = (self.av_info.audio_sample_rate / self.av_info.frames_per_second) * 2.0;
411        assert!(
412            self.total_audio_samples_uploaded as f64 >= required_audio_sample_count_per_frame,
413            format!( "You need to upload at least {} audio samples each frame!", required_audio_sample_count_per_frame )
414        );
415
416        self.total_audio_samples_uploaded -= required_audio_sample_count_per_frame as usize;
417    }
418
419    pub fn on_serialize_size( &mut self ) -> libc::size_t {
420        0
421    }
422
423    pub fn on_serialize( &mut self, _data: *mut libc::c_void, _size: libc::size_t ) -> bool {
424        false
425    }
426
427    pub fn on_unserialize( &mut self, _data: *const libc::c_void, _size: libc::size_t ) -> bool {
428        false
429    }
430
431    pub fn on_cheat_reset( &mut self ) {
432    }
433
434    pub fn on_cheat_set( &mut self, _index: libc::c_uint, _is_enabled: bool, _code: *const libc::c_char ) {
435    }
436
437    pub fn on_unload_game( &mut self ) {
438        if self.is_game_loaded == false {
439            return;
440        }
441
442        let _ = self.core.on_unload_game();
443    }
444
445    pub fn on_get_region( &mut self ) -> libc::c_uint {
446        self.av_info.infer_game_region().to_uint()
447    }
448
449    fn memory_data( &mut self, id: libc::c_uint ) -> Option< &mut [u8] > {
450        match id {
451            libretro_sys::MEMORY_SAVE_RAM => self.core.save_memory(),
452            libretro_sys::MEMORY_RTC => self.core.rtc_memory(),
453            libretro_sys::MEMORY_SYSTEM_RAM => self.core.system_memory(),
454            libretro_sys::MEMORY_VIDEO_RAM => self.core.video_memory(),
455            _ => unreachable!(),
456        }
457    }
458
459    pub fn on_get_memory_data( &mut self, id: libc::c_uint ) -> *mut libc::c_void {
460        self.memory_data( id )
461            .map( |d| d as *mut _ as *mut libc::c_void )
462            .unwrap_or( ptr::null_mut() )
463    }
464
465    pub fn on_get_memory_size( &mut self, id: libc::c_uint ) -> libc::size_t {
466        self.memory_data( id )
467            .map( |d| d.len() as libc::size_t )
468            .unwrap_or( 0 )
469    }
470}
471
472pub struct RuntimeHandle {
473    video_refresh_callback: libretro_sys::VideoRefreshFn,
474    input_state_callback: libretro_sys::InputStateFn,
475    audio_sample_batch_callback: libretro_sys::AudioSampleBatchFn,
476    upload_video_frame_already_called: bool,
477    audio_samples_uploaded: usize,
478
479    video_width: u32,
480    video_height: u32,
481    video_frame_bytes_per_pixel: u32
482}
483
484impl RuntimeHandle {
485    pub fn upload_video_frame( &mut self, data: &[u8] ) {
486        assert!( self.upload_video_frame_already_called == false, "You can only call upload_video_frame() once per frame!" );
487
488        self.upload_video_frame_already_called = true;
489        let bytes = data.as_ptr() as *const libc::c_void;
490        let width = self.video_width as libc::c_uint;
491        let height = self.video_height as libc::c_uint;
492        let bytes_per_pixel = (self.video_width * self.video_frame_bytes_per_pixel) as usize;
493        unsafe {
494            (self.video_refresh_callback)( bytes, width, height, bytes_per_pixel );
495        }
496    }
497
498    pub fn upload_audio_frame( &mut self, data: &[i16] ) {
499        assert!( data.len() % 2 == 0, "Audio data must be in stereo!" );
500
501        self.audio_samples_uploaded += data.len();
502        unsafe {
503            (self.audio_sample_batch_callback)( data.as_ptr(), data.len() / 2 );
504        }
505    }
506
507    pub fn is_joypad_button_pressed( &mut self, port: u32, button: JoypadButton ) -> bool {
508        let device_id = match button {
509            JoypadButton::A => libretro_sys::DEVICE_ID_JOYPAD_A,
510            JoypadButton::B => libretro_sys::DEVICE_ID_JOYPAD_B,
511            JoypadButton::X => libretro_sys::DEVICE_ID_JOYPAD_X,
512            JoypadButton::Y => libretro_sys::DEVICE_ID_JOYPAD_Y,
513            JoypadButton::Start => libretro_sys::DEVICE_ID_JOYPAD_START,
514            JoypadButton::Select => libretro_sys::DEVICE_ID_JOYPAD_SELECT,
515            JoypadButton::Left => libretro_sys::DEVICE_ID_JOYPAD_LEFT,
516            JoypadButton::Right => libretro_sys::DEVICE_ID_JOYPAD_RIGHT,
517            JoypadButton::Up => libretro_sys::DEVICE_ID_JOYPAD_UP,
518            JoypadButton::Down => libretro_sys::DEVICE_ID_JOYPAD_DOWN,
519            JoypadButton::L1 => libretro_sys::DEVICE_ID_JOYPAD_L,
520            JoypadButton::L2 => libretro_sys::DEVICE_ID_JOYPAD_L2,
521            JoypadButton::L3 => libretro_sys::DEVICE_ID_JOYPAD_L3,
522            JoypadButton::R1 => libretro_sys::DEVICE_ID_JOYPAD_R,
523            JoypadButton::R2 => libretro_sys::DEVICE_ID_JOYPAD_R2,
524            JoypadButton::R3 => libretro_sys::DEVICE_ID_JOYPAD_R3
525        };
526
527        unsafe {
528            let value = (self.input_state_callback)( port, libretro_sys::DEVICE_JOYPAD, 0, device_id );
529            return value == 1;
530        }
531    }
532}
533
534#[doc(hidden)]
535pub fn construct< T: 'static + Core >() -> Retro< T > {
536    Retro::new( T::default() )
537}
538
539#[macro_export]
540macro_rules! libretro_core {
541    ($core: path) => (
542        #[doc(hidden)]
543        static mut LIBRETRO_INSTANCE: *mut $crate::Retro< $core > = 0 as *mut $crate::Retro< $core >;
544
545        #[doc(hidden)]
546        #[no_mangle]
547        pub extern "C" fn retro_api_version() -> $crate::libc::c_uint {
548            return $crate::libretro_sys::API_VERSION;
549        }
550
551        #[doc(hidden)]
552        #[no_mangle]
553        pub unsafe extern "C" fn retro_init() {
554            assert_eq!( LIBRETRO_INSTANCE, 0 as *mut _ );
555            let retro = $crate::construct::< $core >();
556            LIBRETRO_INSTANCE = Box::into_raw( Box::new( retro ) );
557        }
558
559        #[doc(hidden)]
560        #[no_mangle]
561        pub unsafe extern "C" fn retro_deinit() {
562            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
563            let instance = Box::from_raw( LIBRETRO_INSTANCE );
564            LIBRETRO_INSTANCE = 0 as *mut _;
565            ::std::mem::drop( instance );
566        }
567
568        #[doc(hidden)]
569        #[no_mangle]
570        pub unsafe extern "C" fn retro_set_environment( callback: $crate::libretro_sys::EnvironmentFn ) {
571            $crate::Retro::< $core >::on_set_environment( callback )
572        }
573
574        #[doc(hidden)]
575        #[no_mangle]
576        pub unsafe extern "C" fn retro_set_video_refresh( callback: $crate::libretro_sys::VideoRefreshFn ) {
577            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
578            (&mut *LIBRETRO_INSTANCE).on_set_video_refresh( callback )
579        }
580
581        #[doc(hidden)]
582        #[no_mangle]
583        pub unsafe extern "C" fn retro_set_audio_sample( callback: $crate::libretro_sys::AudioSampleFn ) {
584            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
585            (&mut *LIBRETRO_INSTANCE).on_set_audio_sample( callback )
586        }
587
588        #[doc(hidden)]
589        #[no_mangle]
590        pub unsafe extern "C" fn retro_set_audio_sample_batch( callback: $crate::libretro_sys::AudioSampleBatchFn ) {
591            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
592            (&mut *LIBRETRO_INSTANCE).on_set_audio_sample_batch( callback )
593        }
594
595        #[doc(hidden)]
596        #[no_mangle]
597        pub unsafe extern "C" fn retro_set_input_poll( callback: $crate::libretro_sys::InputPollFn ) {
598            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
599            (&mut *LIBRETRO_INSTANCE).on_set_input_poll( callback )
600        }
601
602        #[doc(hidden)]
603        #[no_mangle]
604        pub unsafe extern "C" fn retro_set_input_state( callback: $crate::libretro_sys::InputStateFn ) {
605            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
606            (&mut *LIBRETRO_INSTANCE).on_set_input_state( callback )
607        }
608
609        #[doc(hidden)]
610        #[no_mangle]
611        pub extern "C" fn retro_get_system_info( info: *mut $crate::libretro_sys::SystemInfo ) {
612            $crate::Retro::< $core >::on_get_system_info( info )
613        }
614
615        #[doc(hidden)]
616        #[no_mangle]
617        pub unsafe extern "C" fn retro_get_system_av_info( info: *mut $crate::libretro_sys::SystemAvInfo ) {
618            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
619            (&mut *LIBRETRO_INSTANCE).on_get_system_av_info( info )
620        }
621
622        #[doc(hidden)]
623        #[no_mangle]
624        pub unsafe extern "C" fn retro_set_controller_port_device( port: $crate::libc::c_uint, device: $crate::libc::c_uint ) {
625            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
626            (&mut *LIBRETRO_INSTANCE).on_set_controller_port_device( port, device )
627        }
628
629        #[doc(hidden)]
630        #[no_mangle]
631        pub unsafe extern "C" fn retro_reset() {
632            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
633            (&mut *LIBRETRO_INSTANCE).on_reset()
634        }
635
636        #[doc(hidden)]
637        #[no_mangle]
638        pub unsafe extern "C" fn retro_run() {
639            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
640            (&mut *LIBRETRO_INSTANCE).on_run()
641        }
642
643        #[doc(hidden)]
644        #[no_mangle]
645        pub unsafe extern "C" fn retro_serialize_size() -> $crate::libc::size_t {
646            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
647            (&mut *LIBRETRO_INSTANCE).on_serialize_size()
648        }
649
650        #[doc(hidden)]
651        #[no_mangle]
652        pub unsafe extern "C" fn retro_serialize( data: *mut $crate::libc::c_void, size: $crate::libc::size_t ) -> bool {
653            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
654            (&mut *LIBRETRO_INSTANCE).on_serialize( data, size )
655        }
656
657        #[doc(hidden)]
658        #[no_mangle]
659        pub unsafe extern "C" fn retro_unserialize( data: *const $crate::libc::c_void, size: $crate::libc::size_t ) -> bool {
660            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
661            (&mut *LIBRETRO_INSTANCE).on_unserialize( data, size )
662        }
663
664        #[doc(hidden)]
665        #[no_mangle]
666        pub unsafe extern "C" fn retro_cheat_reset() {
667            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
668            (&mut *LIBRETRO_INSTANCE).on_cheat_reset()
669        }
670
671        #[doc(hidden)]
672        #[no_mangle]
673        pub unsafe extern "C" fn retro_cheat_set( index: $crate::libc::c_uint, is_enabled: bool, code: *const $crate::libc::c_char ) {
674            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
675            (&mut *LIBRETRO_INSTANCE).on_cheat_set( index, is_enabled, code )
676        }
677
678        #[doc(hidden)]
679        #[no_mangle]
680        pub unsafe extern "C" fn retro_load_game( game: *const $crate::libretro_sys::GameInfo ) -> bool {
681            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
682            (&mut *LIBRETRO_INSTANCE).on_load_game( game )
683        }
684
685        #[doc(hidden)]
686        #[no_mangle]
687        pub unsafe extern "C" fn retro_load_game_special( game_type: $crate::libc::c_uint, info: *const $crate::libretro_sys::GameInfo, num_info: $crate::libc::size_t ) -> bool {
688            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
689            (&mut *LIBRETRO_INSTANCE).on_load_game_special( game_type, info, num_info )
690        }
691
692        #[doc(hidden)]
693        #[no_mangle]
694        pub unsafe extern "C" fn retro_unload_game() {
695            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
696            (&mut *LIBRETRO_INSTANCE).on_unload_game()
697        }
698
699        #[doc(hidden)]
700        #[no_mangle]
701        pub unsafe extern "C" fn retro_get_region() -> $crate::libc::c_uint {
702            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
703            (&mut *LIBRETRO_INSTANCE).on_get_region()
704        }
705
706        #[doc(hidden)]
707        #[no_mangle]
708        pub unsafe extern "C" fn retro_get_memory_data( id: $crate::libc::c_uint ) -> *mut $crate::libc::c_void {
709            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
710            (&mut *LIBRETRO_INSTANCE).on_get_memory_data( id )
711        }
712
713        #[doc(hidden)]
714        #[no_mangle]
715        pub unsafe extern "C" fn retro_get_memory_size( id: $crate::libc::c_uint ) -> $crate::libc::size_t {
716            assert_ne!( LIBRETRO_INSTANCE, 0 as *mut _ );
717            (&mut *LIBRETRO_INSTANCE).on_get_memory_size( id )
718        }
719    )
720}