From 26429ac09bb50b4f9777d81c37fa038285727296 Mon Sep 17 00:00:00 2001 From: Vivian Lim Date: Mon, 23 Dec 2019 19:53:19 -0800 Subject: [PATCH] it compiles and can load save states... --- .vscode/launch.json | 33 ++++++++++++ examples/sdl2_emulator.rs | 107 +++++++++++++++++++++++++++++++++++++- src/sync/memory_rw.rs | 4 ++ src/sync/mod.rs | 3 +- src/sync/pokemon_rb.rs | 28 +++++++++- 5 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/sync/memory_rw.rs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..39c6af3 --- /dev/null +++ b/.vscode/launch.json @@ -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, + } + ] +} \ No newline at end of file diff --git a/examples/sdl2_emulator.rs b/examples/sdl2_emulator.rs index d00ef06..8aa6e5b 100644 --- a/examples/sdl2_emulator.rs +++ b/examples/sdl2_emulator.rs @@ -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, pressed_keys: Vec, + + // memory + memory_map: Vec, + + 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) { + 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 = 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 = 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(&self, offset: usize, bank_switch: usize) -> Option<&T> { + let length = std::mem::size_of::(); + match self.find_bank(offset, length, bank_switch) { + Some(ptr) => Some(unsafe { &*(ptr as *mut T) }), + None => None + } + } + + fn poke_region(&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, + /// Serialized game state to load + #[structopt(long, parse(from_os_str))] + state: Option, } fn button_map(retro_button: &JoypadButton) -> Option