ferretro/src/libretro_loading.rs

247 lines
11 KiB
Rust
Raw Normal View History

2019-11-04 09:14:16 +01:00
use std::os::raw::{c_char, c_uint, c_void};
use std::path::Path;
2019-11-04 09:14:16 +01:00
use failure::Fallible;
use crate::libretro_types::*;
use libloading;
use std::ffi::OsStr;
2019-11-04 09:14:16 +01:00
pub struct LibretroApi<'a> {
pub retro_set_environment:
libloading::Symbol<'a, unsafe extern "C" fn(arg1: retro_environment_t)>,
pub retro_set_video_refresh:
libloading::Symbol<'a, unsafe extern "C" fn(arg1: retro_video_refresh_t)>,
pub retro_set_audio_sample:
libloading::Symbol<'a, unsafe extern "C" fn(arg1: retro_audio_sample_t)>,
pub retro_set_audio_sample_batch:
libloading::Symbol<'a, unsafe extern "C" fn(arg1: retro_audio_sample_batch_t)>,
pub retro_set_input_poll:
libloading::Symbol<'a, unsafe extern "C" fn(arg1: retro_input_poll_t)>,
pub retro_set_input_state:
libloading::Symbol<'a, unsafe extern "C" fn(arg1: retro_input_state_t)>,
2019-11-04 09:14:16 +01:00
pub retro_init: libloading::Symbol<'a, unsafe extern "C" fn()>,
pub retro_deinit: libloading::Symbol<'a, unsafe extern "C" fn()>,
pub retro_api_version: libloading::Symbol<'a, unsafe extern "C" fn() -> c_uint>,
pub retro_get_system_info:
libloading::Symbol<'a, unsafe extern "C" fn(info: *mut retro_system_info)>,
pub retro_get_system_av_info:
libloading::Symbol<'a, unsafe extern "C" fn(info: *mut retro_system_av_info)>,
pub retro_set_controller_port_device:
libloading::Symbol<'a, unsafe extern "C" fn(port: c_uint, device: c_uint)>,
2019-11-04 09:14:16 +01:00
pub retro_reset: libloading::Symbol<'a, unsafe extern "C" fn()>,
pub retro_run: libloading::Symbol<'a, unsafe extern "C" fn()>,
pub retro_serialize_size: libloading::Symbol<'a, unsafe extern "C" fn() -> usize>,
pub retro_serialize:
libloading::Symbol<'a, unsafe extern "C" fn(data: *mut c_void, size: usize) -> bool>,
pub retro_unserialize:
libloading::Symbol<'a, unsafe extern "C" fn(data: *const c_void, size: usize) -> bool>,
2019-11-04 09:14:16 +01:00
pub retro_cheat_reset: libloading::Symbol<'a, unsafe extern "C" fn()>,
pub retro_cheat_set: libloading::Symbol<
'a,
unsafe extern "C" fn(index: c_uint, enabled: bool, code: *const c_char),
>,
pub retro_load_game:
libloading::Symbol<'a, unsafe extern "C" fn(game: *const retro_game_info) -> bool>,
pub retro_load_game_special: libloading::Symbol<
'a,
unsafe extern "C" fn(
game_type: c_uint,
info: *const retro_game_info,
num_info: usize,
) -> bool,
>,
2019-11-04 09:14:16 +01:00
pub retro_unload_game: libloading::Symbol<'a, unsafe extern "C" fn()>,
pub retro_get_region: libloading::Symbol<'a, unsafe extern "C" fn() -> c_uint>,
pub retro_get_memory_data:
libloading::Symbol<'a, unsafe extern "C" fn(id: c_uint) -> *mut c_void>,
2019-11-04 09:14:16 +01:00
pub retro_get_memory_size: libloading::Symbol<'a, unsafe extern "C" fn(id: c_uint) -> usize>,
}
impl<'a> LibretroApi<'a> {
pub fn from_library(lib: &'a libloading::Library) -> Fallible<Self> {
unsafe {
Ok(LibretroApi {
retro_set_environment: lib.get(b"retro_set_environment")?,
retro_set_video_refresh: lib.get(b"retro_set_video_refresh")?,
retro_set_audio_sample: lib.get(b"retro_set_audio_sample")?,
retro_set_audio_sample_batch: lib.get(b"retro_set_audio_sample_batch")?,
retro_set_input_poll: lib.get(b"retro_set_input_poll")?,
retro_set_input_state: lib.get(b"retro_set_input_state")?,
retro_init: lib.get(b"retro_init")?,
retro_deinit: lib.get(b"retro_deinit")?,
retro_api_version: lib.get(b"retro_api_version")?,
retro_get_system_info: lib.get(b"retro_get_system_info")?,
retro_get_system_av_info: lib.get(b"retro_get_system_av_info")?,
retro_set_controller_port_device: lib.get(b"retro_set_controller_port_device")?,
retro_reset: lib.get(b"retro_reset")?,
retro_run: lib.get(b"retro_run")?,
retro_serialize_size: lib.get(b"retro_serialize_size")?,
retro_serialize: lib.get(b"retro_serialize")?,
retro_unserialize: lib.get(b"retro_unserialize")?,
retro_cheat_reset: lib.get(b"retro_cheat_reset")?,
retro_cheat_set: lib.get(b"retro_cheat_set")?,
retro_load_game: lib.get(b"retro_load_game")?,
retro_load_game_special: lib.get(b"retro_load_game_special")?,
retro_unload_game: lib.get(b"retro_unload_game")?,
retro_get_region: lib.get(b"retro_get_region")?,
retro_get_memory_data: lib.get(b"retro_get_memory_data")?,
retro_get_memory_size: lib.get(b"retro_get_memory_size")?,
})
}
}
/// retro_set_environment() must be called before retro_init().
pub fn set_environment(&self, cb: retro_environment_t) {
unsafe { (&self.retro_set_environment)(cb) }
}
pub fn set_video_refresh(&self, cb: retro_video_refresh_t) {
unsafe { (&self.retro_set_video_refresh)(env) }
}
pub fn set_audio_sample(&self, cb: retro_audio_sample_t) {
unsafe { (&self.retro_set_audio_sample)(env) }
}
pub fn set_audio_sample_batch(&self, cb: retro_audio_sample_batch_t) {
unsafe { (&self.retro_set_audio_sample_batch)(env) }
}
pub fn set_input_poll(&self, cb: retro_input_poll_t) {
unsafe { (&self.retro_set_input_poll)(env) }
}
pub fn set_input_state(&self, cb: retro_input_state_t) {
unsafe { (&self.retro_set_input_state)(env) }
2019-11-04 09:14:16 +01:00
}
/// retro_set_environment() must be called before retro_init().
pub fn init(&self) {
// TODO assert!(called retro_set_environment);
unsafe { (&self.retro_init)() }
}
pub fn deinit(&self) {
unsafe { (&self.retro_deinit)() }
2019-11-04 09:14:16 +01:00
}
/// Must return RETRO_API_VERSION. Used to validate ABI compatibility when the API is revised.
pub fn api_version(&self) -> u32 {
unsafe { (&self.retro_api_version)() as u32 }
}
/// Gets statically known system info. Can be called at any time, even before retro_init().
2019-11-04 09:14:16 +01:00
pub fn get_system_info(&self) -> retro_system_info {
unsafe {
let mut info = ::std::mem::zeroed::<retro_system_info>();
(&self.retro_get_system_info)(&mut info);
info
}
}
/** Gets information about system audio/video timings and geometry.
* Can be called only after retro_load_game() has successfully completed.
* NOTE: The implementation of this function might not initialize every
* variable if needed.
* E.g. geom.aspect_ratio might not be initialized if core doesn't
* desire a particular aspect ratio. */
pub fn get_system_av_info(&self) -> retro_system_av_info {
unsafe {
let mut av_info = ::std::mem::zeroed::<retro_system_av_info>();
(&self.retro_get_system_av_info)(&mut av_info);
av_info
}
}
/** Sets device to be used for player 'port'.
* By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all
* available ports.
* Setting a particular device type is not a guarantee that libretro cores
* will only poll input based on that particular device type. It is only a
* hint to the libretro core when a core cannot automatically detect the
* appropriate input device type on its own. It is also relevant when a
* core can change its behavior depending on device type.
*
* As part of the core's implementation of retro_set_controller_port_device,
* the core should call RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS to notify the
* frontend if the descriptions for any controls have changed as a
* result of changing the device type.
*/
pub fn set_controller_port_device(&self, port: u32, device: u32) {
unsafe { (&self.retro_set_controller_port_device)(port, device) }
}
/// Resets the current game.
pub fn reset(&self) {
unsafe { (&self.retro_reset)() }
}
/** Runs the game for one video frame.
* During retro_run(), input_poll callback must be called at least once.
*
* If a frame is not rendered for reasons where a game "dropped" a frame,
* this still counts as a frame, and retro_run() should explicitly dupe
* a frame if GET_CAN_DUPE returns true.
* In this case, the video callback can take a NULL argument for data.
*/
pub fn run(&self) {
unsafe { (&self.retro_run)() }
}
/// Serializes internal state.
pub fn serialize(&self) -> Fallible<Vec<u8>> {
let size = unsafe { (&self.retro_serialize_size)() };
let mut vec = Vec::with_capacity(size);
vec.resize(size, 0);
if unsafe { (&self.retro_serialize)(vec.as_mut_ptr() as *mut c_void, size) } {
Ok(vec)
} else {
Err(failure::err_msg("Serialize failed"))
}
}
pub fn unserialize(&self, data: &[u8]) -> Fallible<()> {
if unsafe { (&self.retro_unserialize)(data.as_ptr() as *const c_void, data.len()) } {
Ok(())
} else {
Err(failure::err_msg("Unserialize failed"))
}
}
pub fn cheat_reset(&self) {
unsafe { (&self.retro_cheat_reset)() }
}
pub fn cheat_set(&self, index: u32, enabled: bool, code: &str) {
unsafe { (&self.retro_cheat_set)(index, enabled, code.as_bytes().as_ptr() as *const c_char) }
}
/// Loads a game.
pub fn load_game(
&self,
path: Option<&Path>,
data: Option<&[u8]>,
meta: Option<&str>,
) -> Fallible<()> {
let mut game = retro_game_info {
path: std::ptr::null(),
data: std::ptr::null(),
size: 0,
meta: std::ptr::null(),
};
if let Some(p) = path.and_then(Path::to_str) {
game.path = p.as_bytes().as_ptr() as *const c_char;
}
if let Some(d) = data {
game.data = d.as_ptr() as *const c_void;
game.size = d.len();
}
if let Some(m) = meta {
game.meta = m.as_bytes().as_ptr() as *const c_char;
}
if unsafe { (&self.retro_load_game)(&game) } {
Ok(())
} else {
Err(failure::err_msg("Failed to load game"))
}
}
/// Unloads the currently loaded game. Called before deinit().
pub fn unload_game(&self) {
unsafe { (&self.retro_unload_game)() }
}
pub fn get_region(&self) -> u32 {
unsafe { (&self.retro_get_region)() as u32 }
}
/// Gets region of memory
pub fn get_memory(&self, id: u32) -> &mut [u8] {
unsafe {
let data = (&self.retro_get_memory_data)(id);
let size = (&self.retro_get_memory_size)(id);
std::slice::from_raw_parts_mut(data as *mut u8, size)
}
}
2019-11-04 09:14:16 +01:00
}