mirror of
https://github.com/libretro/rust-libretro.git
synced 2026-11-07 12:37:51 -10:00
Begin support for user specified environment variables
This commit is contained in:
1 parent
322d8df788
commit
70d5afdbc6
3 files changed
+159
-93
No files matched your search
+25
-5
@@ -35,8 +35,9 @@
|
||||
#[phase(plugin, link)]
|
||||
extern crate core;
|
||||
extern crate alloc;
|
||||
extern crate collections;
|
||||
extern crate libc;
|
||||
extern crate rlibc;
|
||||
|
||||
use core::intrinsics::transmute;
|
||||
use core::prelude::*;
|
||||
use rust_wrapper::*;
|
||||
@@ -132,9 +133,25 @@ const AV_SAMPLE_RATE: f64 = 48000.0;
|
||||
// higher image quality.
|
||||
const COLOR_DEPTH_32: bool = false;
|
||||
|
||||
// You may specify environment variables as an array of EnvVar
|
||||
// structs. eg.:
|
||||
const ENV_VARS: &'static [EnvVar] = [
|
||||
EnvVar { key: "cheats",
|
||||
desc: "Enable cheats",
|
||||
// first element of values is the default
|
||||
values: ["no", "yes"], },
|
||||
];
|
||||
// These will be appended to the list of automatically provided environment
|
||||
// variables. Leave the list empty if you do not want custom environment
|
||||
// variables. eg.:
|
||||
// const ENV_VARS: &'static[EnvVar] = [];
|
||||
|
||||
|
||||
|
||||
// You must implement several functions that will be automatically called by
|
||||
// rust-libretro.
|
||||
// First is core_run(). You can poll input here with
|
||||
//
|
||||
// Every core must implement core_run(). You can poll input here with
|
||||
// InputState::poll(playernum) and update the core state accordingly. All state
|
||||
// change must be deterministic across all platforms, so be careful with
|
||||
// threading and floating point math. See
|
||||
@@ -319,7 +336,10 @@ pub static RAWIMAGE: &'static [u8] = include_bin!("rgb565.raw");
|
||||
fn image_loader()
|
||||
{
|
||||
unsafe {
|
||||
rlibc::memcpy(transmute(frame_buf), transmute(&RAWIMAGE[0]), (AV_SCREEN_WIDTH * AV_SCREEN_HEIGHT * 2) as uint);
|
||||
core::intrinsics::copy_nonoverlapping_memory::<u8>(transmute(frame_buf),
|
||||
transmute(&RAWIMAGE[0]),
|
||||
(AV_SCREEN_WIDTH *
|
||||
AV_SCREEN_HEIGHT * 2) as uint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,8 +374,8 @@ unsafe fn blit_sprite(mut x: i32, mut y: i32)
|
||||
let buf_slice = mem_as_mut_slice(frame_buf as *mut u16, AV_SCREEN_WIDTH as uint * AV_SCREEN_HEIGHT as uint);
|
||||
let spr_slice = mem_as_slice(RAWSPRITE.as_ptr() as *const u16, 96 as uint * 19 as uint);
|
||||
|
||||
for ix in range(startx, w) {
|
||||
for iy in range (starty, h) {
|
||||
for iy in range(starty, h) {
|
||||
for ix in range (startx, w) {
|
||||
let spr_pix = spr_slice.unsafe_get(ix as uint + iy as uint * 96);
|
||||
if *spr_pix != 0 {
|
||||
*buf_slice.unsafe_mut(x as uint - startx as uint + ix as uint + (y as uint - starty as uint + iy as uint) * AV_SCREEN_WIDTH as uint) = *spr_pix;
|
||||
|
||||
@@ -19,6 +19,7 @@ extern fn panic_fmt(args: &core::fmt::Arguments,
|
||||
file: &str,
|
||||
line: uint) -> !
|
||||
{
|
||||
// TODO handle panics before retro_log_cb is ready
|
||||
struct PanicWriter
|
||||
{
|
||||
buffer: [u8, ..1024],
|
||||
|
||||
+133
-88
@@ -1,14 +1,15 @@
|
||||
extern crate core;
|
||||
extern crate libc;
|
||||
extern crate collections;
|
||||
|
||||
use libc::c_uint;
|
||||
use libc::size_t;
|
||||
use libc::types::common::c95::c_void;
|
||||
use libc::types::os::arch::c95::c_char;
|
||||
|
||||
use core::prelude::*;
|
||||
use core::intrinsics::transmute;
|
||||
use core::atomic::{AtomicBool, SeqCst, INIT_ATOMIC_BOOL};
|
||||
use collections::*;
|
||||
|
||||
use rust_wrapper::libretro::*;
|
||||
pub use rust_wrapper::input::{InputState, ButtonState, ControllerButton,
|
||||
@@ -41,6 +42,11 @@ macro_rules! VALID_EXTENSIONS(
|
||||
);
|
||||
)
|
||||
|
||||
pub struct EnvVar {
|
||||
pub key: &'static str,
|
||||
pub desc: &'static str,
|
||||
pub values: &'static [&'static str]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -84,58 +90,117 @@ pub unsafe extern "C" fn retro_set_input_state(cb: retro_input_state_t)
|
||||
static NO_CONTENT_FLAG: u8 = true as u8;
|
||||
static REQUIRED_CONTENT_FLAG: u8 = false as u8;
|
||||
|
||||
static mut retro_variables: [retro_variable, ..2] =
|
||||
[retro_variable {key: 0u as *const c_char, value: 0u as *const c_char}, ..2];
|
||||
|
||||
pub enum CoreLogicRate {
|
||||
LogicRate60 = 60,
|
||||
LogicRate120 = 120,
|
||||
LogicRate720 = 720,
|
||||
}
|
||||
|
||||
static FRAME_RATE_KEY: &'static str = "refresh_rate\0";
|
||||
static FRAME_RATE_KEY: &'static str = "frame_rate\0";
|
||||
static LOW_FRAME_RATE_VALUES: &'static str =
|
||||
"Display Refresh Rate; 60|30\0";
|
||||
"Frame rate; 60|30\0";
|
||||
static MEDIUM_FRAME_RATE_VALUES: &'static str =
|
||||
"Display Refresh Rate; 60|120|30\0";
|
||||
"Frame rate; 60|120|30\0";
|
||||
static HIGH_FRAME_RATE_VALUES: &'static str =
|
||||
"Display Refresh Rate; 60|72|80|90|102.9|120|144|180|240|24|30|48|51.4|\0";
|
||||
"Frame rate; 60|72|80|90|102.9|120|144|180|240|24|30|48|51.4|\0";
|
||||
|
||||
static mut retro_environment_cb: Option<retro_environment_t> = None;
|
||||
static mut retro_log_cb: Option<retro_log_printf_t> = None;
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn retro_set_environment(cb: retro_environment_t)
|
||||
pub extern "C" fn retro_set_environment(cb: retro_environment_t)
|
||||
{
|
||||
use super::{NO_CONTENT, CORE_LOGIC_RATE};
|
||||
|
||||
retro_environment_cb = Some(cb);
|
||||
|
||||
let no_content: *mut c_void =
|
||||
if NO_CONTENT {
|
||||
transmute(&NO_CONTENT_FLAG)
|
||||
} else {
|
||||
transmute(&REQUIRED_CONTENT_FLAG)
|
||||
};
|
||||
retro_environment_cb.unwrap()(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME,
|
||||
no_content);
|
||||
use super::{NO_CONTENT, ENV_VARS, CORE_LOGIC_RATE};
|
||||
use collections::slice::{MutableOrdSlice, CloneableVector};
|
||||
|
||||
let keyptr = FRAME_RATE_KEY.as_ptr() as *const i8;
|
||||
unsafe {
|
||||
retro_environment_cb = Some(cb);
|
||||
|
||||
retro_variables[0] =
|
||||
retro_variable { key: keyptr,
|
||||
let log_interface = retro_log_callback { log: transmute(0u) };
|
||||
retro_environment_cb.unwrap()(RETRO_ENVIRONMENT_GET_LOG_INTERFACE,
|
||||
transmute(&log_interface));
|
||||
retro_log_cb = Some(log_interface.log);
|
||||
|
||||
let no_content: *mut c_void =
|
||||
if NO_CONTENT {
|
||||
transmute(&NO_CONTENT_FLAG)
|
||||
} else {
|
||||
transmute(&REQUIRED_CONTENT_FLAG)
|
||||
};
|
||||
retro_environment_cb.unwrap()(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME,
|
||||
no_content);
|
||||
}
|
||||
|
||||
// reserve space for automatically implemented + null retro_variables
|
||||
let num_vars = ENV_VARS.len() + 2;
|
||||
let mut retro_variables = Vec::<retro_variable>::with_capacity(num_vars);
|
||||
// Rust needs to hold onto the & references until after the call to C
|
||||
let mut keystrings = Vec::<String>::with_capacity(num_vars);
|
||||
let mut descstrings = Vec::<String>::with_capacity(num_vars);
|
||||
|
||||
// add the automatic env variables
|
||||
retro_variables.push(
|
||||
retro_variable { key: FRAME_RATE_KEY.as_ptr() as *const i8,
|
||||
value: match CORE_LOGIC_RATE {
|
||||
LogicRate60 => LOW_FRAME_RATE_VALUES,
|
||||
LogicRate120 => MEDIUM_FRAME_RATE_VALUES,
|
||||
LogicRate720 => HIGH_FRAME_RATE_VALUES,
|
||||
}.as_ptr() as *const c_char };
|
||||
|
||||
retro_environment_cb.unwrap()(RETRO_ENVIRONMENT_SET_VARIABLES,
|
||||
retro_variables.as_mut_ptr() as *mut c_void);
|
||||
}.as_ptr() as *const c_char } );
|
||||
keystrings.push(String::from_str(FRAME_RATE_KEY));
|
||||
|
||||
let log_interface = retro_log_callback { log: transmute(0u) };
|
||||
retro_environment_cb.unwrap()(RETRO_ENVIRONMENT_GET_LOG_INTERFACE,
|
||||
transmute(&log_interface));
|
||||
retro_log_cb = Some(log_interface.log);
|
||||
// add the use env variables
|
||||
for var in ENV_VARS.iter() {
|
||||
for byte in var.key.as_bytes().iter() {
|
||||
if *byte == 0u8 { panic!("ENV_VAR key must not contain nulls."); }
|
||||
}
|
||||
let key = var.key.to_ascii_cstring();
|
||||
|
||||
for byte in var.desc.as_bytes().iter() {
|
||||
if *byte == 0u8 { panic!("ENV_VAR desc must not contain nulls."); }
|
||||
}
|
||||
// desc.len() + semicolon + space + [value.len() + pipe]
|
||||
let value_max_len =
|
||||
var.desc.len() + 1 + 1 +
|
||||
var.values.iter().fold(0, |sum, &x| sum + x.len() + 1);
|
||||
let mut value_string = String::with_capacity(value_max_len);
|
||||
|
||||
value_string.push_str(var.desc);
|
||||
value_string.push_str("; ");
|
||||
|
||||
for value in var.values.iter() {
|
||||
for byte in value.as_bytes().iter() {
|
||||
if *byte == 0u8 {
|
||||
panic!("ENV_VAR values must not contain nulls.");
|
||||
}
|
||||
}
|
||||
value_string.push_str(*value);
|
||||
value_string.push_str("|");
|
||||
}
|
||||
let value_cstring = value_string.to_ascii_cstring();
|
||||
|
||||
retro_variables.push(
|
||||
retro_variable { key: key.as_ptr() as *const c_char,
|
||||
value: value_cstring.as_ptr() as *const c_char } );
|
||||
keystrings.push(key);
|
||||
descstrings.push(value_cstring);
|
||||
}
|
||||
|
||||
retro_variables.push(retro_variable { key: 0u as *const c_char,
|
||||
value: 0u as *const c_char } );
|
||||
keystrings.push(String::from_str(""));
|
||||
|
||||
let mut key_sort= keystrings.clone();
|
||||
key_sort.sort();
|
||||
let mut key_unique = key_sort.to_vec();
|
||||
key_unique.dedup();
|
||||
if keystrings.len() != key_unique.len() {
|
||||
panic!("Duplicate environment variable keys are forbidden. Are you trying to manually implement an automatic environment variable?");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
retro_environment_cb.unwrap()(
|
||||
RETRO_ENVIRONMENT_SET_VARIABLES,
|
||||
retro_variables.as_mut_ptr() as *mut c_void);
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LogLevel
|
||||
@@ -152,9 +217,9 @@ pub fn retro_log(level: LogLevel, text: &str)
|
||||
{
|
||||
// TODO copy to the stack if text is short
|
||||
unsafe {
|
||||
let c_text = malloc_ascii_cstring(text);
|
||||
retro_log_cb.unwrap()(level as i32, "%s\n\0".as_ptr() as *const c_char, c_text);
|
||||
libc::free(c_text as *mut c_void);
|
||||
let c_text = text.to_ascii_cstring();
|
||||
retro_log_cb.unwrap()(level as i32, "%s\n\0".as_ptr() as *const c_char,
|
||||
c_text.as_ptr() as *const c_char);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,12 +227,14 @@ pub fn retro_log_panic(msg: &str, file: &str, line: uint)
|
||||
{
|
||||
// TODO copy to the stack if text is short
|
||||
unsafe {
|
||||
let c_msg = malloc_ascii_cstring(msg);
|
||||
let c_file = malloc_ascii_cstring(file);
|
||||
retro_log_cb.unwrap()(LogError as i32, "\"%s\" at %s line %u\n\0".as_ptr() as *const c_char, c_msg, c_file, line);
|
||||
libc::free(c_file as *mut c_void);
|
||||
libc::free(c_msg as *mut c_void);
|
||||
}
|
||||
let c_msg = msg.to_ascii_cstring();
|
||||
let c_file = file.to_ascii_cstring();
|
||||
retro_log_cb.unwrap()(
|
||||
LogError as i32, "\"%s\" at %s line %u\n\0".as_ptr() as *const c_char,
|
||||
c_msg.as_ptr() as *const c_char,
|
||||
c_file.as_ptr() as *const c_char,
|
||||
line);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_retro_system_av_info(info: &mut retro_system_av_info, fps: f64)
|
||||
@@ -319,14 +386,9 @@ pub unsafe extern "C" fn retro_get_system_info(info: *mut retro_system_info)
|
||||
|
||||
for retro_string in [CORE_NAME, CORE_VERSION, VALID_EXTENSIONS].iter()
|
||||
{
|
||||
if !retro_string.is_ascii() {
|
||||
panic!("Configuration strings must be ascii.");
|
||||
}
|
||||
if !retro_string.is_terminated() {
|
||||
panic!("Configuration strings must be null terminated.");
|
||||
}
|
||||
retro_string.check_valid();
|
||||
}
|
||||
|
||||
|
||||
(*info).library_name = CORE_NAME.as_ptr() as *const i8;
|
||||
(*info).library_version = CORE_VERSION.as_ptr() as *const i8;
|
||||
(*info).valid_extensions = VALID_EXTENSIONS.as_ptr() as *const i8;
|
||||
@@ -336,60 +398,44 @@ pub unsafe extern "C" fn retro_get_system_info(info: *mut retro_system_info)
|
||||
|
||||
trait RetroString
|
||||
{
|
||||
fn is_ascii(self) -> bool;
|
||||
fn is_terminated(self) -> bool;
|
||||
fn check_valid(self);
|
||||
fn to_ascii_cstring(self) -> String;
|
||||
}
|
||||
|
||||
impl RetroString for &'static str
|
||||
impl<'a> RetroString for &'a str
|
||||
{
|
||||
fn is_ascii(self) -> bool
|
||||
fn check_valid(self)
|
||||
{
|
||||
for b in self.as_bytes().iter()
|
||||
{
|
||||
if (b & 0x80) != 0 { return false; }
|
||||
if (b & 0x80) != 0 {
|
||||
panic!("All libretro strings must be ascii.");
|
||||
}
|
||||
}
|
||||
if self.as_bytes()[self.len() - 1] != 0u8 {
|
||||
panic!("All libretro strings must be null terminated.");
|
||||
}
|
||||
true
|
||||
}
|
||||
fn is_terminated(self) -> bool
|
||||
fn to_ascii_cstring(self) -> String
|
||||
{
|
||||
if self.as_bytes()[self.len() - 1] != 0u8 { return false; }
|
||||
true
|
||||
}
|
||||
}
|
||||
unsafe fn malloc_ascii_cstring(src: &str) -> *const c_char
|
||||
{
|
||||
let terminated_max_len = (src.as_bytes().len() + 1) as size_t;
|
||||
let dst: *mut c_char = libc::malloc(terminated_max_len) as *mut c_char;
|
||||
strip_utf_strlcpy(dst, src.as_bytes(), terminated_max_len);
|
||||
dst as *const c_char
|
||||
}
|
||||
|
||||
unsafe fn strip_utf_strlcpy(dst: *mut c_char, src: &[u8], dst_size: size_t)
|
||||
{
|
||||
if dst_size == 0 { return; }
|
||||
if dst_size == 1
|
||||
{
|
||||
*dst = 0i8;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut dst_offset: int = 0;
|
||||
let dst_last = dst_size - 1; // reserve space for NULL terminator
|
||||
for src_byte in src.iter()
|
||||
{
|
||||
if (*src_byte & 0x80) == 0
|
||||
let terminated_max_len = self.as_bytes().len() + 1;
|
||||
|
||||
let mut dst = String::with_capacity(terminated_max_len);
|
||||
|
||||
for src_byte in self.bytes()
|
||||
{
|
||||
*dst.offset(dst_offset) = *src_byte as i8;
|
||||
dst_offset = dst_offset + 1;
|
||||
if dst_offset == dst_last as int { break; }
|
||||
if (src_byte & 0x80) == 0
|
||||
{
|
||||
dst.push(src_byte as char);
|
||||
}
|
||||
}
|
||||
}
|
||||
*dst.offset(dst_offset) = 0i8;
|
||||
dst.push('\0');
|
||||
dst
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn retro_run()
|
||||
{
|
||||
@@ -418,7 +464,6 @@ pub extern fn retro_run()
|
||||
super::core_run();
|
||||
}
|
||||
|
||||
|
||||
unsafe {VIDEO_LOCK.lock_noguard();}
|
||||
unsafe {
|
||||
retro_video_refresh_cb.unwrap()(frame_buf as *const c_void,
|
||||
|
||||
Reference in new issue
Block a user