it compiles and can load save states...
This commit is contained in:
parent
772410730d
commit
26429ac09b
|
@ -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,
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
pub mod pokemon_rb;
|
||||
pub mod pokemon_rb;
|
||||
pub mod memory_rw;
|
|
@ -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])
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue