2019-11-15 04:16:44 +01:00
|
|
|
extern crate rustro;
|
2019-11-15 06:21:58 +01:00
|
|
|
extern crate sdl2;
|
|
|
|
|
|
|
|
use rustro::retro;
|
|
|
|
use rustro::retro::ffi::GameGeometry;
|
2019-11-15 10:15:51 +01:00
|
|
|
use rustro::retro::ffi::SystemAvInfo;
|
2019-11-11 01:24:29 +01:00
|
|
|
|
2019-11-11 08:21:38 +01:00
|
|
|
use std::ffi::CStr;
|
2019-11-15 06:21:58 +01:00
|
|
|
use std::path::{PathBuf, Path};
|
|
|
|
use std::time::Duration;
|
2019-11-11 01:24:29 +01:00
|
|
|
|
|
|
|
use structopt::StructOpt;
|
|
|
|
|
2019-11-15 10:15:51 +01:00
|
|
|
use sdl2::render::{WindowCanvas, Texture, TextureCreator};
|
2019-11-15 06:21:58 +01:00
|
|
|
use sdl2::rect::Rect;
|
2019-11-15 10:15:51 +01:00
|
|
|
use sdl2::event::Event;
|
|
|
|
use sdl2::keyboard::Keycode;
|
|
|
|
use sdl2::video::WindowContext;
|
|
|
|
use std::io::Write;
|
2019-11-15 06:21:58 +01:00
|
|
|
|
|
|
|
struct MyEmulator {
|
|
|
|
retro: retro::wrapper::LibretroWrapper,
|
2019-11-15 10:15:51 +01:00
|
|
|
av_info: SystemAvInfo,
|
2019-11-15 06:21:58 +01:00
|
|
|
pixel_format: sdl2::pixels::PixelFormatEnum,
|
2019-11-15 10:15:51 +01:00
|
|
|
texture: Option<Texture>,
|
2019-11-15 06:21:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl MyEmulator {
|
|
|
|
pub fn with_core(core: impl AsRef<Path>) -> Self {
|
|
|
|
let lib = libloading::Library::new(core.as_ref()).unwrap();
|
|
|
|
let raw_retro = retro::loading::LibretroApi::from_library(lib).unwrap();
|
|
|
|
let retro = retro::wrapper::LibretroWrapper::from(raw_retro);
|
|
|
|
println!("api version: {}", retro.as_ref().api_version());
|
|
|
|
println!("name: {}", unsafe { CStr::from_ptr(retro.as_ref().get_system_info().library_name) }.to_string_lossy());
|
|
|
|
|
2019-11-15 10:15:51 +01:00
|
|
|
let av_info = retro.as_ref().get_system_av_info();
|
2019-11-15 06:21:58 +01:00
|
|
|
|
|
|
|
let pixel_format = sdl2::pixels::PixelFormatEnum::ABGR1555;
|
2019-11-15 10:15:51 +01:00
|
|
|
let mut result = MyEmulator { retro, av_info, pixel_format, texture: None };
|
2019-11-15 06:21:58 +01:00
|
|
|
|
|
|
|
retro::wrapper::register_handler(&mut result);
|
|
|
|
result.retro.as_ref().init();
|
|
|
|
result
|
|
|
|
}
|
2019-11-11 01:24:29 +01:00
|
|
|
|
2019-11-15 06:21:58 +01:00
|
|
|
pub fn run(&self) {
|
|
|
|
self.retro.as_ref().run();
|
2019-11-15 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn base_dimensions(&self) -> (u32, u32) {
|
|
|
|
(self.av_info.geometry.base_width, self.av_info.geometry.base_height)
|
2019-11-15 06:21:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_game(&self, rom: impl AsRef<Path>) {
|
|
|
|
self.retro.as_ref().load_game(Some(rom.as_ref()), None, None);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl retro::convert::Handler for MyEmulator {
|
2019-11-11 01:24:29 +01:00
|
|
|
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
|
2019-11-15 11:05:37 +01:00
|
|
|
println!("self ref: {:?}", self as *mut Self);
|
2019-11-15 10:15:51 +01:00
|
|
|
//println!("video_refresh {}x{}", width, height);
|
|
|
|
//let rect = Rect::new(0, 0, width, height);
|
2019-11-15 10:40:02 +01:00
|
|
|
if let Some(tex) = self.texture.as_mut() {
|
|
|
|
tex.with_lock(None, |tex_buffer: &mut [u8], tex_pitch: usize| {
|
|
|
|
let src_pitch = pitch as usize;
|
|
|
|
let src_width = width as usize;
|
|
|
|
for y in 0..(height as usize) {
|
|
|
|
let src_slice = (y * src_pitch)..((y * src_pitch) + src_width);
|
|
|
|
let tex_slice = (y * tex_pitch)..((y * tex_pitch) + src_width);
|
|
|
|
tex_buffer[tex_slice].copy_from_slice(&data[src_slice]);
|
|
|
|
}
|
|
|
|
}).unwrap();
|
|
|
|
//tex.update(None, data, pitch as usize).unwrap();
|
2019-11-15 10:15:51 +01:00
|
|
|
}
|
2019-11-15 06:21:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn set_pixel_format(&mut self, pix_fmt: retro::ffi::PixelFormat) -> bool {
|
|
|
|
self.pixel_format = match pix_fmt {
|
|
|
|
retro::ffi::PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555,
|
|
|
|
retro::ffi::PixelFormat::ARGB8888 => sdl2::pixels::PixelFormatEnum::ARGB8888,
|
|
|
|
retro::ffi::PixelFormat::RGB565 => sdl2::pixels::PixelFormatEnum::RGB565,
|
|
|
|
};
|
|
|
|
true
|
2019-11-11 01:24:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:15:51 +01:00
|
|
|
pub fn main() -> failure::Fallible<()> {
|
2019-11-15 06:21:58 +01:00
|
|
|
let opt: Opt = Opt::from_args();
|
|
|
|
let mut emu = MyEmulator::with_core(&opt.core);
|
|
|
|
emu.load_game(&opt.rom);
|
|
|
|
|
2019-11-15 10:15:51 +01:00
|
|
|
let sdl_context = sdl2::init().map_err(failure::err_msg)?;
|
|
|
|
let video_subsystem = sdl_context.video().map_err(failure::err_msg)?;
|
|
|
|
|
|
|
|
let (width, height) = emu.base_dimensions();
|
|
|
|
let window = video_subsystem.window("rust libretro", width, height)
|
|
|
|
.opengl()
|
|
|
|
.build()?;
|
|
|
|
|
|
|
|
let mut canvas = window.into_canvas().build()?;
|
|
|
|
let texture_creator = canvas.texture_creator();
|
|
|
|
emu.texture = texture_creator.create_texture_streaming(emu.pixel_format, width, height).ok();
|
2019-11-15 11:05:37 +01:00
|
|
|
|
|
|
|
let mut x: u8 = 1;
|
|
|
|
println!("emu ref: {:?}", &emu as *const MyEmulator);
|
2019-11-15 10:15:51 +01:00
|
|
|
|
|
|
|
let mut event_pump = sdl_context.event_pump().map_err(failure::err_msg)?;
|
|
|
|
'running: loop {
|
|
|
|
for event in event_pump.poll_iter() {
|
|
|
|
match event {
|
|
|
|
Event::Quit {..}
|
|
|
|
| Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
|
|
|
break 'running
|
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
2019-11-15 11:05:37 +01:00
|
|
|
if let Some(tex) = emu.texture.as_mut() {
|
|
|
|
tex.with_lock(None, |tex_buffer: &mut [u8], tex_pitch: usize| {
|
|
|
|
for i in 0..tex_buffer.len() {
|
|
|
|
tex_buffer[i] = x;
|
|
|
|
x = (x + 1) % 255;
|
|
|
|
}
|
|
|
|
}).unwrap();
|
|
|
|
//tex.update(None, data, pitch as usize).unwrap();
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:15:51 +01:00
|
|
|
// The rest of the game loop goes here...
|
2019-11-15 06:21:58 +01:00
|
|
|
emu.run();
|
2019-11-15 10:15:51 +01:00
|
|
|
canvas.clear();
|
|
|
|
canvas.copy(emu.texture.as_ref().unwrap(), None, Some(Rect::new(0, 0, width, height))).map_err(failure::err_msg)?;
|
|
|
|
canvas.present();
|
2019-11-15 06:21:58 +01:00
|
|
|
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
|
|
|
}
|
2019-11-15 10:15:51 +01:00
|
|
|
Ok(())
|
2019-11-15 06:21:58 +01:00
|
|
|
}
|
2019-11-11 01:24:29 +01:00
|
|
|
|
|
|
|
#[derive(StructOpt)]
|
|
|
|
struct Opt {
|
|
|
|
/// Core module to use.
|
|
|
|
#[structopt(short, long, parse(from_os_str))]
|
|
|
|
core: PathBuf,
|
|
|
|
/// Rom to load using the core.
|
|
|
|
#[structopt(short, long, parse(from_os_str))]
|
|
|
|
rom: PathBuf,
|
|
|
|
}
|