diff --git a/parallel-rdp/interface.cpp b/parallel-rdp/interface.cpp index 682caf72..5b6d26a5 100644 --- a/parallel-rdp/interface.cpp +++ b/parallel-rdp/interface.cpp @@ -702,8 +702,9 @@ void rdp_check_framebuffers(uint32_t address, uint32_t length) { size_t rdp_state_size() { return sizeof(RDP_DEVICE); } -void rdp_save_state(uint8_t *state) { - processor->wait_for_timeline(processor->signal_timeline()); +void rdp_save_state(uint8_t *state, bool rewind) { + if (!rewind) // speed hack for rewind + processor->wait_for_timeline(processor->signal_timeline()); memcpy(state, &rdp_device, sizeof(RDP_DEVICE)); } diff --git a/parallel-rdp/interface.hpp b/parallel-rdp/interface.hpp index 7de41daa..2cc23844 100644 --- a/parallel-rdp/interface.hpp +++ b/parallel-rdp/interface.hpp @@ -61,7 +61,7 @@ uint64_t rdp_process_commands(); void rdp_onscreen_message(const char *message, MESSAGE_LENGTH milliseconds); void rdp_check_framebuffers(uint32_t address, uint32_t length); size_t rdp_state_size(); -void rdp_save_state(uint8_t *state); +void rdp_save_state(uint8_t *state, bool rewind); void rdp_load_state(GFX_INFO _gfx_info, const uint8_t *state); void rdp_set_fps(uint32_t fps, uint32_t vis); diff --git a/src/device.rs b/src/device.rs index 298952ec..abf7e251 100644 --- a/src/device.rs +++ b/src/device.rs @@ -255,32 +255,26 @@ pub fn zero_m128i() -> __m128i { } impl Device { - pub fn clone_without_ui(&self) -> Device { - Device { - netplay: None, - ui: ui::Ui::default(), - byte_swap: self.byte_swap, - savestate: self.savestate.clone(), - cpu: self.cpu.clone(), - pif: self.pif.clone(), - cart: self.cart.clone(), - memory: self.memory.clone(), - rsp: self.rsp.clone(), - rdp: self.rdp.clone(), - rdram: self.rdram.clone(), - mi: self.mi.clone(), - pi: self.pi.clone(), - vi: self.vi.clone(), - ai: self.ai.clone(), - si: self.si.clone(), - ri: self.ri.clone(), - rng: self.rng.clone(), - vru: self.vru.clone(), - transferpaks: self.transferpaks.clone(), - cheats: self.cheats.clone(), - } + pub fn clone_state(&mut self, device: &Device) { + self.cpu.clone_from(&device.cpu); + self.pif.clone_from(&device.pif); + self.cart.clone_from(&device.cart); + self.memory.clone_from(&device.memory); + self.rsp.clone_from(&device.rsp); + self.rdp.clone_from(&device.rdp); + self.rdram.clone_from(&device.rdram); + self.mi.clone_from(&device.mi); + self.pi.clone_from(&device.pi); + self.vi.clone_from(&device.vi); + self.ai.clone_from(&device.ai); + self.si.clone_from(&device.si); + self.ri.clone_from(&device.ri); + self.vru.clone_from(&device.vru); + self.transferpaks.clone_from(&device.transferpaks); + self.cheats.clone_from(&device.cheats); } - pub fn new() -> Device { + + pub fn new(with_ui: bool) -> Device { let mut byte_swap: usize = 0; let test: [u8; 4] = [1, 2, 3, 4]; // if the host computer is little endian, that means the RDRAM will be stored as little endian @@ -290,7 +284,11 @@ impl Device { } Device { netplay: None, - ui: ui::Ui::new(), + ui: if with_ui { + ui::Ui::new() + } else { + ui::Ui::default() + }, byte_swap, savestate: savestates::Savestate { save_state: false, diff --git a/src/lib.rs b/src/lib.rs index b263f6e9..e051a68e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,7 +137,7 @@ pub async fn run(args: Args, arg_count: usize) -> std::io::Result<()> { return Err(Error::other("Savestate slot must be between 0 and 9")); } - let mut device = device::Device::new(); + let mut device = device::Device::new(true); device.ui.config.recent_roms.retain(|x| *x != game); device.ui.config.recent_roms.insert(0, game); diff --git a/src/savestates.rs b/src/savestates.rs index e0f2edbe..b836e387 100644 --- a/src/savestates.rs +++ b/src/savestates.rs @@ -5,6 +5,7 @@ use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; use serde::ser::{Serialize, SerializeSeq, Serializer}; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; +use std::ops::Deref; struct M128iArrayVisitor; @@ -81,35 +82,55 @@ pub struct Savestate { } pub struct SavestateData { - device_data: Vec, - saves_data: Vec, + device: device::Device, + saves: ui::storage::Saves, rdp_state: Vec, ra_state: Vec, } +static DEVICE_CLONE: std::sync::LazyLock> = + std::sync::LazyLock::new(|| std::sync::Mutex::new(device::Device::new(false))); + +static SAVES_CLONE: std::sync::LazyLock> = + std::sync::LazyLock::new(|| std::sync::Mutex::new(ui::storage::Saves::default())); + pub fn create_savestate(device: &mut device::Device, rewind: bool) { + if let Ok(mut device_clone) = DEVICE_CLONE.lock() + && let Ok(mut saves_clone) = SAVES_CLONE.lock() + { + device_clone.clone_state(device); + saves_clone.clone_from(&device.ui.storage.saves); + } else { + ui::video::onscreen_message( + "Failed to create savestate", + ui::video::MESSAGE_LENGTH_MESSAGE_SHORT, + ); + return; + } + let mut rdp_state: Vec = vec![0; ui::video::state_size()]; - ui::video::save_state(rdp_state.as_mut_ptr()); + ui::video::save_state(rdp_state.as_mut_ptr(), rewind); let mut ra_state: Vec = vec![0; retroachievements::state_size()]; retroachievements::save_state(ra_state.as_mut_ptr(), ra_state.len()); let save_path = device.ui.storage.paths.savestate_file_path.clone(); - - let device_clone = device.clone_without_ui(); - let saves_clone = device.ui.storage.saves.clone(); let save_state_slot = device.ui.storage.save_state_slot; let rewind_pool = device.savestate.rewind_pool.clone(); - tokio::spawn(async move { + + tokio::task::spawn_blocking(move || { let mut error = false; - if let Ok(device_data) = postcard::to_stdvec(&device_clone) - && let Ok(saves_data) = postcard::to_stdvec(&saves_clone) + + if let Ok(mut device_clone) = DEVICE_CLONE.lock() + && let Ok(saves_clone) = SAVES_CLONE.lock() { if rewind { + let mut state = device::Device::new(false); + std::mem::swap(&mut state, &mut *device_clone); if let Ok(mut pool) = rewind_pool.lock() { pool.push_back(SavestateData { - device_data, - saves_data, + device: state, + saves: saves_clone.clone(), rdp_state, ra_state, }); @@ -118,13 +139,16 @@ pub fn create_savestate(device: &mut device::Device, rewind: bool) { } } } else { - if let Ok(compressed_file) = ui::storage::compress_file(&[ - (&device_data, "device"), - (&saves_data, "saves"), - (&rdp_state, "rdp_state"), - (&ra_state, "ra_state"), - ]) { - if let Err(e) = tokio::fs::write(save_path, compressed_file).await { + if let Ok(device_data) = postcard::to_stdvec(device_clone.deref()) + && let Ok(saves_data) = postcard::to_stdvec(saves_clone.deref()) + && let Ok(compressed_file) = ui::storage::compress_file(&[ + (&device_data, "device"), + (&saves_data, "saves"), + (&rdp_state, "rdp_state"), + (&ra_state, "ra_state"), + ]) + { + if let Err(e) = std::fs::write(save_path, compressed_file) { eprintln!("Error writing savestate: {}", e); error = true; } @@ -136,6 +160,7 @@ pub fn create_savestate(device: &mut device::Device, rewind: bool) { } else { error = true; } + if error { ui::video::onscreen_message( &format!("Failed to create savestate in slot {}", save_state_slot), @@ -171,55 +196,54 @@ pub fn load_savestate(device: &mut device::Device, rewind: bool) { && let Ok(saves_data) = ui::storage::decompress_file(savestate, "saves") && let Ok(rdp_state) = ui::storage::decompress_file(savestate, "rdp_state") && let Ok(ra_state) = ui::storage::decompress_file(savestate, "ra_state") + && let Ok(state) = postcard::from_bytes::(&device_data) + && let Ok(saves) = postcard::from_bytes(&saves_data) { Some(SavestateData { - device_data, - saves_data, + device: state, + saves, rdp_state, ra_state, }) } else { None }; - if let Some(state_data) = state_data - && let Ok(mut state) = postcard::from_bytes::(&state_data.device_data) - && let Ok(saves) = postcard::from_bytes(&state_data.saves_data) - && device.rdram.size == state.rdram.size + + if let Some(mut state) = state_data + && device.rdram.size == state.device.rdram.size { - device.savestate.last_rewind_saved = state.vi.elapsed_time; + device.savestate.last_rewind_saved = state.device.vi.elapsed_time; - device.ui.storage.saves = saves; + std::mem::swap(&mut device.ui.storage.saves, &mut state.saves); - std::mem::swap(&mut device.cpu, &mut state.cpu); - std::mem::swap(&mut device.pif, &mut state.pif); + std::mem::swap(&mut device.cpu, &mut state.device.cpu); + std::mem::swap(&mut device.pif, &mut state.device.pif); - let rom = device.cart.rom.clone(); - std::mem::swap(&mut device.cart, &mut state.cart); - device.cart.rom = rom; + std::mem::swap(&mut device.cart, &mut state.device.cart); + std::mem::swap(&mut device.cart.rom, &mut state.device.cart.rom); // ROM is not included in the savestate - std::mem::swap(&mut device.memory, &mut state.memory); - std::mem::swap(&mut device.rsp, &mut state.rsp); - std::mem::swap(&mut device.rdp, &mut state.rdp); + std::mem::swap(&mut device.memory, &mut state.device.memory); + std::mem::swap(&mut device.rsp, &mut state.device.rsp); + std::mem::swap(&mut device.rdp, &mut state.device.rdp); - device.rdram.mem.clone_from(&state.rdram.mem); - device.rdram.regs = state.rdram.regs; + device.rdram.mem.clone_from(&state.device.rdram.mem); // RDRAM address should not change + std::mem::swap(&mut device.rdram.regs, &mut state.device.rdram.regs); - std::mem::swap(&mut device.mi, &mut state.mi); - std::mem::swap(&mut device.pi, &mut state.pi); - std::mem::swap(&mut device.vi, &mut state.vi); - std::mem::swap(&mut device.ai, &mut state.ai); - std::mem::swap(&mut device.si, &mut state.si); - std::mem::swap(&mut device.ri, &mut state.ri); - std::mem::swap(&mut device.vru, &mut state.vru); - std::mem::swap(&mut device.cheats, &mut state.cheats); + std::mem::swap(&mut device.mi, &mut state.device.mi); + std::mem::swap(&mut device.pi, &mut state.device.pi); + std::mem::swap(&mut device.vi, &mut state.device.vi); + std::mem::swap(&mut device.ai, &mut state.device.ai); + std::mem::swap(&mut device.si, &mut state.device.si); + std::mem::swap(&mut device.ri, &mut state.device.ri); + std::mem::swap(&mut device.vru, &mut state.device.vru); + std::mem::swap(&mut device.cheats, &mut state.device.cheats); - let mut tpak_rom = [vec![], vec![], vec![], vec![]]; - for (i, item) in tpak_rom.iter_mut().enumerate() { - *item = device.transferpaks[i].cart.rom.clone(); - } - std::mem::swap(&mut device.transferpaks, &mut state.transferpaks); - for (i, item) in tpak_rom.iter().enumerate() { - device.transferpaks[i].cart.rom = item.clone(); + std::mem::swap(&mut device.transferpaks, &mut state.device.transferpaks); + for (i, item) in device.transferpaks.iter_mut().enumerate() { + std::mem::swap( + &mut item.cart.rom, + &mut state.device.transferpaks[i].cart.rom, + ); // ROM is not included in the savestate } device::memory::init(device); @@ -276,11 +300,11 @@ pub fn load_savestate(device: &mut device::Device, rewind: bool) { } ui::audio::update_freq(device); - ui::video::load_state(device, state_data.rdp_state.as_ptr()); + ui::video::load_state(device, state.rdp_state.as_ptr()); retroachievements::set_rdram(device.rdram.mem.as_ptr(), device.rdram.size as usize); - if !state_data.ra_state.is_empty() { - retroachievements::load_state(state_data.ra_state.as_ptr(), state_data.ra_state.len()); + if !state.ra_state.is_empty() { + retroachievements::load_state(state.ra_state.as_ptr(), state.ra_state.len()); } else { retroachievements::load_state(std::ptr::null(), 0); } diff --git a/src/ui/video.rs b/src/ui/video.rs index 01daff06..ac9f2126 100644 --- a/src/ui/video.rs +++ b/src/ui/video.rs @@ -161,8 +161,8 @@ pub fn state_size() -> usize { unsafe { rdp_state_size() } } -pub fn save_state(rdp_state: *mut u8) { - unsafe { rdp_save_state(rdp_state) } +pub fn save_state(rdp_state: *mut u8, rewind: bool) { + unsafe { rdp_save_state(rdp_state, rewind) } } pub fn load_state(device: &mut device::Device, rdp_state: *const u8) {