it compiles and can load save states...

This commit is contained in:
Vivian Lim 2019-12-23 19:53:19 -08:00
parent 772410730d
commit 26429ac09b
5 changed files with 172 additions and 3 deletions

33
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,33 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "sync: pokeblue",
"type": "cppvsdbg",
"request": "launch",
"program": "target/debug/examples/sdl2_emulator.exe",
"args": ["--core", "./data/gambatte_libretro.dll", "--rom", "./data/pokeblue.gb", "--state", "./data/pkblue-route1.sav"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [{
"name": "RUST_BACKTRACE",
"value": "1"
}],
"externalConsole": false,
},
{
"name": "sync: sonic2",
"type": "cppvsdbg",
"request": "launch",
"program": "target/debug/examples/sdl2_emulator.exe",
"args": ["--core", "./data/genesis_plus_gx_libretro.dll", "--rom", "./data/sonic2.bin"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
}
]
}

View File

@ -8,11 +8,15 @@ use ferretro::retro::constants::{InputIndex, JoypadButton, AnalogAxis, DeviceTyp
use ferretro::retro::wrapped_types::{ControllerDescription2, InputDescriptor2, InputDeviceId, SubsystemInfo2, Variable2};
use ferretro::retro::wrapper::LibretroWrapper;
use ferretro_synced::sync::memory_rw::MemoryRw;
use ferretro_synced::sync::pokemon_rb::SyncedPokemonRedBlue;
use std::ffi::CStr;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::time::{Duration, Instant};
use std::convert::TryFrom;
use structopt::StructOpt;
@ -49,6 +53,11 @@ struct MyEmulator {
gamepad_subsys: sdl2::GameControllerSubsystem,
gamepads: Vec<GameController>,
pressed_keys: Vec<Keycode>,
// memory
memory_map: Vec<MyMemoryMap>,
synced_pokemonrb: SyncedPokemonRedBlue,
}
impl MyEmulator {
@ -115,6 +124,9 @@ impl MyEmulator {
let pressed_keys = Vec::new();
let memory_map = Vec::new();
let synced_pokemonrb = std::default::Default::default();
let emu = MyEmulator {
retro,
core_path,
@ -132,6 +144,8 @@ impl MyEmulator {
gamepad_subsys,
gamepads,
pressed_keys,
memory_map,
synced_pokemonrb,
};
let mut pin_emu = Box::pin(emu);
retro::wrapper::set_handler(pin_emu.as_mut());
@ -164,9 +178,13 @@ impl MyEmulator {
// similar hack to the sample rate, make sure we don't divide by zero.
let mut spf = 1.0 / self.av_info.timing.fps;
if spf.is_nan() {
if spf.is_nan() || spf.is_infinite() {
spf = 1.0 / 60.0;
}
let new_data = SyncedPokemonRedBlue::copy_from_emu(self);
std::mem::replace(&mut self.synced_pokemonrb, new_data);
Duration::from_secs_f64(spf)
.checked_sub(frame_begin.elapsed())
.map(std::thread::sleep);
@ -194,6 +212,20 @@ impl MyEmulator {
}
}
pub fn load_state(&self, state: impl AsRef<Path>) {
let path = state.as_ref();
let mut v = Vec::new();
if let Ok(mut f) = std::fs::File::open(path) {
if f.read_to_end(&mut v).is_ok() {
let data = v.as_ref();
self.retro
.unserialize(data)
.unwrap();
}
}
}
pub fn update_key_state<'a>(&mut self, keyboard_state: &sdl2::keyboard::KeyboardState<'a>){
let keys: Vec<Keycode> = keyboard_state.pressed_scancodes().filter_map(Keycode::from_scancode).collect();
self.pressed_keys = keys;
@ -245,6 +277,28 @@ impl MyEmulator {
_ => 0
}
}
fn find_bank(&self, offset: usize, length: usize, bank_switch: usize) -> Option<*const std::ffi::c_void> {
if self.memory_map.len() == 0 {
return None;
}
for item in self.memory_map.iter() {
if (item.start <= offset) && (offset < item.end) {
if offset + length > item.end {
println!("({:x}, {:x}) overruns ({:x}, {:x}) memory bank.", offset, length, item.start, item.end);
return None;
}
let bank_size = item.end - item.start;
let relative_offset = isize::try_from(offset - item.start + (bank_size * bank_switch)).unwrap();
return unsafe { Some(item.pointer.offset(relative_offset)) }
}
}
println!("({:x}, {:x}) address range not found in any memory map region.", offset, length);
None
}
}
impl Drop for MyEmulator {
@ -380,6 +434,50 @@ impl retro::wrapper::Handler for MyEmulator {
fn log_print(&mut self, level: retro::ffi::LogLevel, msg: &str) {
eprint!("[{:?}] {}", level, msg);
}
fn set_memory_maps(&mut self, memory_map: MemoryMap) -> bool {
let mut descs: Vec<MyMemoryMap> = Vec::new();
let num_descriptors = isize::try_from(memory_map.num_descriptors).unwrap();
for i in 0..num_descriptors {
let desc = unsafe { &*memory_map.descriptors.offset(i) };
let mut length = desc.len;
if desc.select > 0 { // FIXME: hack for oversized SRAM eating addr space
length = (!desc.select + 1) & 0xffffffff;
println!("truncating memory region {:x} from size {:x} to {:x} banks of size {:x}", desc.start, desc.len, desc.len/length, length)
}
descs.push(MyMemoryMap {
start: desc.start,
end: desc.start + length,
pointer: desc.ptr,
})
}
self.memory_map = descs;
true
}
}
impl MemoryRw for MyEmulator {
fn peek_region<T>(&self, offset: usize, bank_switch: usize) -> Option<&T> {
let length = std::mem::size_of::<T>();
match self.find_bank(offset, length, bank_switch) {
Some(ptr) => Some(unsafe { &*(ptr as *mut T) }),
None => None
}
}
fn poke_region<T>(&mut self, offset: usize, data: T, bank_switch: usize) {
}
}
struct MyMemoryMap {
start: usize,
end: usize,
pointer: *const std::ffi::c_void,
}
struct MySdlAudio {
@ -403,6 +501,10 @@ pub fn main() -> failure::Fallible<()> {
let opt: Opt = Opt::from_args();
let mut emu = MyEmulator::new(&opt.core, &opt.system);
emu.load_game(&opt.rom);
match (&opt.state) {
Some(s) => emu.load_state(s),
None => ()
}
emu.run();
Ok(())
@ -419,6 +521,9 @@ struct Opt {
/// System directory, often containing BIOS files
#[structopt(short, long, parse(from_os_str))]
system: Option<PathBuf>,
/// Serialized game state to load
#[structopt(long, parse(from_os_str))]
state: Option<PathBuf>,
}
fn button_map(retro_button: &JoypadButton) -> Option<Button> {

4
src/sync/memory_rw.rs Normal file
View File

@ -0,0 +1,4 @@
pub trait MemoryRw {
fn peek_region<T>(&self, offset: usize, bank_switch: usize) -> Option<&T>;
fn poke_region<T>(&mut self, offset: usize, data: T, bank_switch: usize);
}

View File

@ -1 +1,2 @@
pub mod pokemon_rb;
pub mod pokemon_rb;
pub mod memory_rw;

View File

@ -1,3 +1,6 @@
use std::default::Default;
use std::fmt::Display;
use crate::sync::memory_rw::MemoryRw;
use ferretro::retro::wrapper::LibretroWrapper; // want LibretroWrapper.api : LibretroApi.get_memory()
pub struct SyncedPokemonRedBlue {
@ -7,7 +10,30 @@ pub struct SyncedPokemonRedBlue {
}
impl SyncedPokemonRedBlue {
pub fn copy_from_emu () {
pub fn copy_from_emu<T: MemoryRw>(emu: &T) -> SyncedPokemonRedBlue {
let result = SyncedPokemonRedBlue {
active_pokemon_raw: *emu.peek_region(0xd009, 0).unwrap_or_else(|| &[0; 0x27]),
player_and_party: *emu.peek_region(0xd158, 0).unwrap_or_else(|| &[0; 0x19e]),
pokemon_out: *emu.peek_region(0xcc2f, 0).unwrap_or(&0),
};
println!("{}", result);
result
}
}
impl Default for SyncedPokemonRedBlue {
fn default() -> Self {
SyncedPokemonRedBlue {
active_pokemon_raw: [0; 0x27],
player_and_party: [0; 0x19e],
pokemon_out: 0,
}
}
}
impl Display for SyncedPokemonRedBlue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "active: {:?}", &self.active_pokemon_raw[0..32])
}
}