extern crate libc;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem;
mod sys {
use libc;
#[repr(C)]
pub struct Info {
pub library_name: *const libc::c_char,
pub library_version: *const libc::c_char,
pub pixel_format: u8,
pub min_width: u32,
pub min_height: u32,
pub max_width: u32,
pub max_height: u32,
}
}
#[doc(hidden)]
type UnsafeVideoRefreshFn = extern "C" fn(*mut libc::c_void, *const u8, u32, u32) -> ();
enum Input {
Keyboard = 0,
Joypad = 1,
}
#[derive(Clone, Copy)]
pub enum Joypad {
A,
B,
X,
Y,
Select,
Start,
Up,
Down,
Left,
Right,
}
#[derive(Clone, Copy)]
pub enum Key {
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
Num1,
Num2,
Num3,
Num4,
Num5,
Num6,
Num7,
Num8,
Num9,
Num0,
Backspace,
Tab,
Return,
Escape,
Space,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
PrintScreen,
ScrollLock,
Pause,
Insert,
Home,
PageUp,
Delete,
End,
PageDown,
Right,
Left,
Down,
Up,
}
#[doc(hidden)]
type UnsafeInputStateFn = extern "C" fn(*mut libc::c_void, u8, u8, u32) -> i16;
#[derive(Clone, Copy)]
pub enum PixelFormat {
R3_G3_B2,
R5_B5_G6,
R8_G8_B8,
R10_G10_B10,
}
pub struct Info {
library_name: CString,
library_version: CString,
pixel_format: PixelFormat,
min_width: u32,
min_height: u32,
max_width: u32,
max_height: u32,
}
impl Info {
pub fn new(name: &str, version: &str) -> Self {
Info {
library_name: CString::new(name).unwrap(),
library_version: CString::new(version).unwrap(),
pixel_format: PixelFormat::R5_B5_G6,
min_width: 0,
min_height: 0,
max_width: 0,
max_height: 0,
}
}
pub fn pixel_format(self, pixel_format: PixelFormat) -> Self {
Info { pixel_format: pixel_format, ..self }
}
pub fn size(self, width: u32, height: u32) -> Self {
Info {
min_width: width,
min_height: height,
max_width: if self.max_width == 0 {
width
} else {
self.max_width
},
max_height: if self.max_height == 0 {
height
} else {
self.max_height
},
..self
}
}
pub fn max_size(self, width: u32, height: u32) -> Self {
Info {
max_width: width,
max_height: height,
..self
}
}
}
pub struct Runtime {
video_refresh_fn: Option<UnsafeVideoRefreshFn>,
input_state_fn: Option<UnsafeInputStateFn>,
userdata: *mut libc::c_void,
}
impl Runtime {
pub fn video_refresh(&self, data: &[u8], width: u32, height: u32) {
if let Some(ref video_refresh_fn) = self.video_refresh_fn {
(video_refresh_fn)(self.userdata, data.as_ptr(), width, height);
}
}
pub fn input_keyboard_state(&self, port: u8, key: Key) -> bool {
if let Some(ref input_state_fn) = self.input_state_fn {
(input_state_fn)(self.userdata, port, Input::Keyboard as u8, key as u32) == 1
} else {
false
}
}
}
pub struct Bundle {
runtime: Runtime,
core: Box<Core>,
info: Info,
}
pub trait Core {
fn info(&self) -> Info;
fn reset(&mut self);
fn run_next(&mut self, &mut Runtime);
fn rom_insert(&mut self, filename: &str);
fn rom_remove(&mut self);
}
#[doc(hidden)]
pub unsafe fn new<T: 'static + Core + Default>(userdata: *mut libc::c_void) -> Box<Bundle> {
let core = Box::new(T::default());
Box::new(Bundle {
info: core.info(),
core: core,
runtime: Runtime {
userdata: userdata,
video_refresh_fn: None,
input_state_fn: None,
},
})
}
#[macro_export]
macro_rules! ax_generate_main (($lib:ident) => {
#[link_args = "-export-dynamic"]
extern "C" {}
extern crate libc;
extern crate $lib;
#[link(name = "axal")]
extern "C" {
fn axal_main(argc: libc::c_int, argv: *const *const libc::c_schar) -> ();
}
fn main() {
unsafe {
axal_main(0, std::ptr::null());
let _ = $lib::ax_new(std::ptr::null_mut());
}
}
});
#[macro_export]
macro_rules! ax_generate_lib (($t:path) => {
#[no_mangle]
pub unsafe extern "C" fn ax_new(userdata: *mut u8) -> *mut $crate::Bundle {
use std::mem;
mem::transmute($crate::new::<$t>(mem::transmute(userdata)))
}
});
#[no_mangle]
pub unsafe extern "C" fn ax_get_info(ptr: *mut Bundle, info: *mut sys::Info) {
if info.is_null() {
return;
}
(*info).library_name = (*ptr).info.library_name.as_ptr();
(*info).library_version = (*ptr).info.library_version.as_ptr();
(*info).pixel_format = (*ptr).info.pixel_format as u8;
(*info).min_width = (*ptr).info.min_width;
(*info).min_height = (*ptr).info.min_height;
(*info).max_width = (*ptr).info.max_width;
(*info).max_height = (*ptr).info.max_height;
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn ax_delete(ptr: *mut Bundle) {
let _drop_me: Box<Bundle> = mem::transmute(ptr);
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn ax_reset(ptr: *mut Bundle) {
(*ptr).core.reset();
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn ax_set_video_refresh(ptr: *mut Bundle, cb: UnsafeVideoRefreshFn) {
(*ptr).runtime.video_refresh_fn = Some(cb);
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn ax_set_input_state(ptr: *mut Bundle, cb: UnsafeInputStateFn) {
(*ptr).runtime.input_state_fn = Some(cb);
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn ax_rom_insert(ptr: *mut Bundle, filename: *const libc::c_char) {
let filename = CStr::from_ptr(filename).to_str().unwrap();
(*ptr).core.rom_insert(filename);
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn ax_rom_remove(ptr: *mut Bundle) {
(*ptr).core.rom_remove();
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn ax_run_next(ptr: *mut Bundle) {
(*ptr).core.run_next(&mut (*ptr).runtime);
}