libretro_proxy/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#![allow(non_camel_case_types)]

use libloading::Symbol;
use core::ffi::{c_uint, c_void, c_char};

pub type retro_environment_t = rust_libretro_sys::retro_environment_t;
pub type retro_video_refresh_t = rust_libretro_sys::retro_video_refresh_t;
pub type retro_audio_sample_t = rust_libretro_sys::retro_audio_sample_t;
pub type retro_audio_sample_batch_t = rust_libretro_sys::retro_audio_sample_batch_t;
pub type retro_input_poll_t = rust_libretro_sys::retro_input_poll_t;
pub type retro_input_state_t = rust_libretro_sys::retro_input_state_t;
pub type retro_system_info = rust_libretro_sys::retro_system_info;
pub type retro_system_av_info = rust_libretro_sys::retro_system_av_info;
pub type retro_game_info = rust_libretro_sys::retro_game_info;

pub type Library = libloading::Library;

pub struct Core {
    pub lib: Library,
}

macro_rules! forward_to_lib {
    ($name:ident, fn($($arg:ident: $type:ty),*) $(-> $ret:ty)?) => {
        pub extern "C" fn $name(&self, $($arg: $type),*) $(-> $ret)? {
            unsafe {
                let lib = &self.lib;
                let func: Symbol<unsafe extern "C" fn($($type),*) $(-> $ret)?> = lib
                    .get(concat!(stringify!($name), "\0").as_bytes())
                    .expect(&format!("Failed to find function '{}' in proxied core", stringify!($name)));
                func($($arg),*)
            }
        }
    };
}

impl Core {
    pub fn load(path: &str) -> Option<Core> {
        unsafe {
            Library::new(&path).ok().map(|lib| Core {lib})
        }
    }

    forward_to_lib!(retro_set_environment, fn(cb: retro_environment_t));
    forward_to_lib!(retro_set_video_refresh, fn(cb: retro_video_refresh_t));
    forward_to_lib!(retro_set_audio_sample, fn(cb: retro_audio_sample_t));
    forward_to_lib!(retro_set_audio_sample_batch, fn(cb: retro_audio_sample_batch_t));
    forward_to_lib!(retro_set_input_poll, fn(cb: retro_input_poll_t));
    forward_to_lib!(retro_set_input_state, fn(cb: retro_input_state_t));
    forward_to_lib!(retro_init, fn());
    forward_to_lib!(retro_deinit, fn());
    forward_to_lib!(retro_api_version, fn() -> c_uint);
    forward_to_lib!(retro_get_system_info, fn(info: *mut retro_system_info));
    forward_to_lib!(retro_get_system_av_info, fn(info: *mut retro_system_av_info));
    forward_to_lib!(retro_set_controller_port_device, fn(port: c_uint, device: c_uint));
    forward_to_lib!(retro_reset, fn());
    forward_to_lib!(retro_run, fn());
    forward_to_lib!(retro_serialize_size, fn() -> usize);
    forward_to_lib!(retro_serialize, fn(data: *mut c_void, size: usize) -> bool);
    forward_to_lib!(retro_unserialize, fn(data: *const c_void, size: usize) -> bool);
    forward_to_lib!(retro_cheat_reset, fn());
    forward_to_lib!(retro_cheat_set, fn(index: c_uint, enabled: bool, code: *const c_char));
    forward_to_lib!(retro_load_game, fn(game: *const retro_game_info) -> bool);
    forward_to_lib!(retro_load_game_special, fn(game_type: c_uint, info: *const retro_game_info, num_info: usize) -> bool);
    forward_to_lib!(retro_unload_game, fn());
    forward_to_lib!(retro_get_region, fn() -> c_uint);
    forward_to_lib!(retro_get_memory_data, fn(id: c_uint) -> *mut c_void);
    forward_to_lib!(retro_get_memory_size, fn(id: c_uint) -> usize);
}

pub trait Proxy {
    fn core(&self) -> &Core;

    fn retro_set_environment(&self, cb: retro_environment_t) { self.core().retro_set_environment(cb) }
    fn retro_set_video_refresh(&self, cb: retro_video_refresh_t) { self.core().retro_set_video_refresh(cb) }
    fn retro_set_audio_sample(&self, cb: retro_audio_sample_t) { self.core().retro_set_audio_sample(cb) }
    fn retro_set_audio_sample_batch(&self, cb: retro_audio_sample_batch_t) { self.core().retro_set_audio_sample_batch(cb) }
    fn retro_set_input_poll(&self, cb: retro_input_poll_t) { self.core().retro_set_input_poll(cb) }
    fn retro_set_input_state(&self, cb: retro_input_state_t) { self.core().retro_set_input_state(cb) }
    fn retro_init(&self) { self.core().retro_init() }
    fn retro_deinit(&self) { self.core().retro_deinit() }
    fn retro_api_version(&self) -> c_uint { self.core().retro_api_version() }
    fn retro_get_system_info(&self, info: *mut retro_system_info) { self.core().retro_get_system_info(info) }
    fn retro_get_system_av_info(&self, info: *mut retro_system_av_info) { self.core().retro_get_system_av_info(info) }
    fn retro_set_controller_port_device(&self, port: c_uint, device: c_uint) { self.core().retro_set_controller_port_device(port, device) }
    fn retro_reset(&self) { self.core().retro_reset() }
    fn retro_run(&self) { self.core().retro_run() }
    fn retro_serialize_size(&self) -> usize { self.core().retro_serialize_size() }
    fn retro_serialize(&self, data: *mut c_void, size: usize) -> bool { self.core().retro_serialize(data, size) }
    fn retro_unserialize(&self, data: *const c_void, size: usize) -> bool { self.core().retro_unserialize(data, size) }
    fn retro_cheat_reset(&self) { self.core().retro_cheat_reset() }
    fn retro_cheat_set(&self, index: c_uint, enabled: bool, code: *const c_char) { self.core().retro_cheat_set(index, enabled, code) }
    fn retro_load_game(&self, game: *const retro_game_info) -> bool { self.core().retro_load_game(game) }
    fn retro_load_game_special(&self, game_type: c_uint, info: *const retro_game_info, num_info: usize) -> bool { self.core().retro_load_game_special(game_type, info, num_info) }
    fn retro_unload_game(&self) { self.core().retro_unload_game() }
    fn retro_get_region(&self) -> c_uint { self.core().retro_get_region() }
    fn retro_get_memory_data(&self, id: c_uint) -> *mut c_void { self.core().retro_get_memory_data(id) }
    fn retro_get_memory_size(&self, id: c_uint) -> usize { self.core().retro_get_memory_size(id) }
}

#[macro_export]
macro_rules! proxy_to {
    ($proxy:expr) => {
        #[no_mangle] pub extern "C" fn retro_set_environment(cb: libretro_proxy::retro_environment_t) { $proxy.retro_set_environment(cb) }
        #[no_mangle] pub extern "C" fn retro_set_video_refresh(cb: libretro_proxy::retro_video_refresh_t) { $proxy.retro_set_video_refresh(cb) }
        #[no_mangle] pub extern "C" fn retro_set_audio_sample(cb: libretro_proxy::retro_audio_sample_t) { $proxy.retro_set_audio_sample(cb) }
        #[no_mangle] pub extern "C" fn retro_set_audio_sample_batch(cb: libretro_proxy::retro_audio_sample_batch_t) { $proxy.retro_set_audio_sample_batch(cb) }
        #[no_mangle] pub extern "C" fn retro_set_input_poll(cb: libretro_proxy::retro_input_poll_t) { $proxy.retro_set_input_poll(cb) }
        #[no_mangle] pub extern "C" fn retro_set_input_state(cb: libretro_proxy::retro_input_state_t) { $proxy.retro_set_input_state(cb) }
        #[no_mangle] pub extern "C" fn retro_init() { $proxy.retro_init() }
        #[no_mangle] pub extern "C" fn retro_deinit() { $proxy.retro_deinit() }
        #[no_mangle] pub extern "C" fn retro_api_version() -> ::core::ffi::c_uint { $proxy.retro_api_version() }
        #[no_mangle] pub extern "C" fn retro_get_system_info(info: *mut libretro_proxy::retro_system_info) { $proxy.retro_get_system_info(info) }
        #[no_mangle] pub extern "C" fn retro_get_system_av_info(info: *mut libretro_proxy::retro_system_av_info) { $proxy.retro_get_system_av_info(info) }
        #[no_mangle] pub extern "C" fn retro_set_controller_port_device(port: ::core::ffi::c_uint, device: ::core::ffi::c_uint) { $proxy.retro_set_controller_port_device(port, device) }
        #[no_mangle] pub extern "C" fn retro_reset() { $proxy.retro_reset() }
        #[no_mangle] pub extern "C" fn retro_run() { $proxy.retro_run() }
        #[no_mangle] pub extern "C" fn retro_serialize_size() -> usize { $proxy.retro_serialize_size() }
        #[no_mangle] pub extern "C" fn retro_serialize(data: *mut ::core::ffi::c_void, size: usize) -> bool { $proxy.retro_serialize(data, size) }
        #[no_mangle] pub extern "C" fn retro_unserialize(data: *const ::core::ffi::c_void, size: usize) -> bool { $proxy.retro_unserialize(data, size) }
        #[no_mangle] pub extern "C" fn retro_cheat_reset() { $proxy.retro_cheat_reset() }
        #[no_mangle] pub extern "C" fn retro_cheat_set(index: ::core::ffi::c_uint, enabled: bool, code: *const ::core::ffi::c_char) { $proxy.retro_cheat_set(index, enabled, code) }
        #[no_mangle] pub extern "C" fn retro_load_game(game: *const libretro_proxy::retro_game_info) -> bool { $proxy.retro_load_game(game) }
        #[no_mangle] pub extern "C" fn retro_load_game_special(game_type: ::core::ffi::c_uint, info: *const libretro_proxy::retro_game_info, num_info: usize) -> bool { $proxy.retro_load_game_special(game_type, info, num_info) }
        #[no_mangle] pub extern "C" fn retro_unload_game() { $proxy.retro_unload_game() }
        #[no_mangle] pub extern "C" fn retro_get_region() -> ::core::ffi::c_uint { $proxy.retro_get_region() }
        #[no_mangle] pub extern "C" fn retro_get_memory_data(id: ::core::ffi::c_uint) -> *mut ::core::ffi::c_void { $proxy.retro_get_memory_data(id) }
        #[no_mangle] pub extern "C" fn retro_get_memory_size(id: ::core::ffi::c_uint) -> usize { $proxy.retro_get_memory_size(id) }
    };
}