diff --git a/Cargo.lock b/Cargo.lock index fa80975..ad1d790 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2232,6 +2232,7 @@ dependencies = [ "regex", "reqwest", "rfd", + "rustc-hash 2.1.2", "sdl3-image-sys", "sdl3-src", "sdl3-sys", diff --git a/Cargo.toml b/Cargo.toml index 9c54207..e44058b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ encoding_rs = "0.8" mimalloc = "0.1" regex = "1.11" tokio-tungstenite = "0.29" +rustc-hash = "2.1" futures = "0.3" socket2 = { version = "0.6", features = ["all"] } rand = "0.10" diff --git a/src/cheats.rs b/src/cheats.rs index d053d2d..31cb3a6 100644 --- a/src/cheats.rs +++ b/src/cheats.rs @@ -26,7 +26,7 @@ pub type Cheat = std::collections::BTreeMap>, + cheat_settings: rustc_hash::FxHashMap>, ) { let cheats = serde_json::from_slice::(include_bytes!("../data/cheats.json")) .unwrap() diff --git a/src/device.rs b/src/device.rs index 331493b..70beeef 100644 --- a/src/device.rs +++ b/src/device.rs @@ -16,7 +16,7 @@ const N64_EXTENSIONS_UNCOMPRESSED: [&str; 8] = use rand::{Rng, SeedableRng}; use crate::{cheats, netplay, retroachievements, savestates, ui}; -use std::{collections::HashMap, io::Read}; +use std::io::Read; pub mod ai; pub mod cache; @@ -310,20 +310,8 @@ impl Device { is_event: false, instrs: [cop0::reserved; 32], instrs2: [cop0::reserved; 32], - tlb_lut_w: vec![ - tlb::TlbLut { - address: 0, - cached: false, - }; - 0x100000 - ], - tlb_lut_r: vec![ - tlb::TlbLut { - address: 0, - cached: false, - }; - 0x100000 - ], + tlb_lut_w: vec![], + tlb_lut_r: vec![], tlb_entries: [tlb::TlbEntry { mask: 0, vpn2: 0, @@ -558,7 +546,7 @@ impl Device { word_buffer: [0; 40], words: Vec::new(), talking: false, - word_mappings: HashMap::new(), + word_mappings: rustc_hash::FxHashMap::default(), }, rng: set_rng(), transferpaks: [ diff --git a/src/device/controller/vru.rs b/src/device/controller/vru.rs index 9e2cf8b..e3bf841 100644 --- a/src/device/controller/vru.rs +++ b/src/device/controller/vru.rs @@ -1,7 +1,6 @@ use crate::device; #[cfg(all(feature = "gui", not(target_os = "android")))] use crate::ui; -use std::collections::HashMap; const JCMD_VRU_READ: u8 = 0x09; const JCMD_VRU_WRITE: u8 = 0x0A; @@ -28,7 +27,7 @@ pub struct Vru { pub word_buffer: [u16; 40], pub words: Vec, pub talking: bool, - pub word_mappings: HashMap, + pub word_mappings: rustc_hash::FxHashMap, } pub fn init(device: &mut device::Device) { @@ -186,7 +185,7 @@ pub fn process(device: &mut device::Device, channel: usize) { #[cfg(any(not(feature = "gui"), target_os = "android"))] let index = 0x7FFF; let num_results = if index == 0x7FFF { 0 } else { 1 }; - let data: HashMap = HashMap::from([ + let data: rustc_hash::FxHashMap = rustc_hash::FxHashMap::from_iter([ (0, 0x8000), (2, 0x0F00), (4, 0), // error flags @@ -247,7 +246,7 @@ pub fn vru_talking_event(device: &mut device::Device) { } fn create_word_mappings(device: &mut device::Device) { - device.vru.word_mappings = HashMap::from([ + device.vru.word_mappings = rustc_hash::FxHashMap::from_iter([ ( String::from("03A50024000303CF00A80003036000EA"), String::from("pikachu"), diff --git a/src/device/cop0.rs b/src/device/cop0.rs index 8e55e8d..0dd83d1 100644 --- a/src/device/cop0.rs +++ b/src/device/cop0.rs @@ -100,7 +100,9 @@ pub struct Cop0 { pub instrs: [fn(&mut device::Device, u32); 32], #[serde(skip, default = "savestates::default_instructions")] pub instrs2: [fn(&mut device::Device, u32); 32], + #[serde(skip)] pub tlb_lut_r: Vec, + #[serde(skip)] pub tlb_lut_w: Vec, pub tlb_entries: [device::tlb::TlbEntry; 32], pub is_event: bool, @@ -369,6 +371,7 @@ pub fn init(device: &mut device::Device) { ]; map_instructions(device); + device::tlb::init(device); // taken from VR4300 manual device.cpu.cop0.regs[COP0_RANDOM_REG] = 0b00000000000000000000000000011111; diff --git a/src/device/tlb.rs b/src/device/tlb.rs index 70b2104..1c57000 100644 --- a/src/device/tlb.rs +++ b/src/device/tlb.rs @@ -30,6 +30,23 @@ pub struct TlbEntry { pub phys_odd: u64, } +pub fn init(device: &mut device::Device) { + device.cpu.cop0.tlb_lut_r.resize( + 0x100000, + device::tlb::TlbLut { + address: 0, + cached: false, + }, + ); + device.cpu.cop0.tlb_lut_w.resize( + 0x100000, + device::tlb::TlbLut { + address: 0, + cached: false, + }, + ); +} + pub fn read(device: &mut device::Device, index: u64) { if index > 31 { return; @@ -181,7 +198,7 @@ fn tlb_unmap(device: &mut device::Device, index: u64) { } } -fn tlb_map(device: &mut device::Device, index: u64) { +pub fn tlb_map(device: &mut device::Device, index: u64) { let e = &device.cpu.cop0.tlb_entries[index as usize]; if e.v_even != 0 diff --git a/src/netplay.rs b/src/netplay.rs index 24c53d2..63e1b0d 100644 --- a/src/netplay.rs +++ b/src/netplay.rs @@ -38,7 +38,7 @@ pub struct PlayerData { lag: u8, count: u32, pub reg_id: u32, - input_events: std::collections::HashMap, + input_events: rustc_hash::FxHashMap, } struct InputEvent { @@ -310,25 +310,25 @@ pub fn init(peer_addr: std::net::SocketAddr, player_number: u8) -> Netplay { lag: 0, count: 0, reg_id: reg_id[0], - input_events: std::collections::HashMap::new(), + input_events: rustc_hash::FxHashMap::default(), }, PlayerData { lag: 0, count: 0, reg_id: reg_id[1], - input_events: std::collections::HashMap::new(), + input_events: rustc_hash::FxHashMap::default(), }, PlayerData { lag: 0, count: 0, reg_id: reg_id[2], - input_events: std::collections::HashMap::new(), + input_events: rustc_hash::FxHashMap::default(), }, PlayerData { lag: 0, count: 0, reg_id: reg_id[3], - input_events: std::collections::HashMap::new(), + input_events: rustc_hash::FxHashMap::default(), }, ], } diff --git a/src/savestates.rs b/src/savestates.rs index 6dee6de..59156d7 100644 --- a/src/savestates.rs +++ b/src/savestates.rs @@ -97,12 +97,13 @@ pub fn create_savestate(device: &mut device::Device, rewind: bool) { 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 { let mut error = false; if let Ok(device_data) = postcard::to_stdvec(&device_clone) - && let Ok(saves_data) = postcard::to_stdvec(&device_clone.ui.storage.saves) + && let Ok(saves_data) = postcard::to_stdvec(&saves_clone) { if rewind { if let Ok(mut pool) = rewind_pool.lock() { @@ -246,6 +247,11 @@ pub fn load_savestate(device: &mut device::Device, rewind: bool) { } } + device::tlb::init(device); + for i in 0..32 { + device::tlb::tlb_map(device, i); + } + device::pif::connect_pif_channels(device); for i in 0..4 { if let Some(handler) = device.pif.channels[i].pak_handler { diff --git a/src/ui.rs b/src/ui.rs index 9e15d99..0a9aa1f 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -85,7 +85,7 @@ pub struct Usb { pub struct GameSettings { pub overclock: bool, pub disable_expansion_pak: bool, - pub cheats: std::collections::HashMap>, + pub cheats: rustc_hash::FxHashMap>, pub load_savestate_slot: Option, } @@ -259,7 +259,7 @@ impl Ui { write_pending: false, }, romsave: storage::RomSave { - data: std::collections::HashMap::new(), + data: rustc_hash::FxHashMap::default(), write_pending: false, }, }, diff --git a/src/ui/config.rs b/src/ui/config.rs index be3d42a..0b2e7da 100644 --- a/src/ui/config.rs +++ b/src/ui/config.rs @@ -66,8 +66,7 @@ pub struct Emulation { #[derive(serde::Serialize, serde::Deserialize)] pub struct Cheats { - pub cheats: - std::collections::HashMap>>, + pub cheats: rustc_hash::FxHashMap>>, } #[derive(Default, serde::Serialize, serde::Deserialize)] @@ -109,7 +108,7 @@ impl Cheats { } } Cheats { - cheats: std::collections::HashMap::new(), + cheats: rustc_hash::FxHashMap::default(), } } } diff --git a/src/ui/gui.rs b/src/ui/gui.rs index edf7037..636d256 100644 --- a/src/ui/gui.rs +++ b/src/ui/gui.rs @@ -72,7 +72,7 @@ fn run_with_path(weak: slint::Weak, path: std::path::PathBuf) { ui::GameSettings { overclock: handle.get_overclock_n64_cpu(), disable_expansion_pak: handle.get_disable_expansion_pak(), - cheats: std::collections::HashMap::new(), // will be filled in later + cheats: rustc_hash::FxHashMap::default(), // will be filled in later load_savestate_slot: None, }, None, @@ -702,7 +702,7 @@ fn open_rom(app: &AppWindow) { ui::GameSettings { overclock, disable_expansion_pak, - cheats: std::collections::HashMap::new(), // will be filled in later + cheats: rustc_hash::FxHashMap::default(), // will be filled in later load_savestate_slot: None, }, None, diff --git a/src/ui/netplay.rs b/src/ui/netplay.rs index 94bf762..4668759 100644 --- a/src/ui/netplay.rs +++ b/src/ui/netplay.rs @@ -19,7 +19,7 @@ pub struct NetplayRoom { md5: Option, game_name: Option, pub port: Option, - features: Option>, + features: Option>, buffer_target: Option, } @@ -60,9 +60,8 @@ async fn get_local_servers() -> Vec<(String, String)> { broadcast_sock.recv(&mut buffer), ) .await - && let Ok(data) = serde_json::from_slice::>( - &buffer[..result], - ) + && let Ok(data) = + serde_json::from_slice::>(&buffer[..result]) { let mut local_servers = vec![]; for server in data.iter() { @@ -207,7 +206,7 @@ fn setup_create_window( if let Ok(response) = response && let Ok(server) = response - .json::>() + .json::>() .await { let server_url = server.values().next().unwrap(); @@ -427,19 +426,19 @@ fn update_sessions(weak: slint::Weak) { .header("netplay-id", env!("NETPLAY_ID")) .send(); tokio::spawn(async move { - let mut dispatcher_servers = std::collections::HashMap::new(); + let mut dispatcher_servers = rustc_hash::FxHashMap::default(); let response = task.await; if let Ok(response) = response && let Ok(servers) = response - .json::>() + .json::>() .await { dispatcher_servers = servers; } let weak2 = weak.clone(); weak.upgrade_in_event_loop(move |handle| { - let mut servers: std::collections::HashMap = - std::collections::HashMap::new(); + let mut servers: rustc_hash::FxHashMap = + rustc_hash::FxHashMap::default(); let server_names = handle.get_netplay_server_names(); let server_urls = handle.get_netplay_server_urls(); for (i, server_name) in server_names.iter().enumerate() { @@ -529,7 +528,7 @@ fn create_session( .as_millis() .to_string(); let hasher = Sha256::new().chain_update(&now_utc).chain_update(EMU_NAME); - let mut features = std::collections::HashMap::new(); + let mut features = rustc_hash::FxHashMap::default(); if !game_cheats.is_empty() { features.insert("cheats".to_string(), game_cheats); @@ -1126,7 +1125,7 @@ pub fn netplay_window(app: &AppWindow) { ui::GameSettings { overclock: handle.get_overclock_n64_cpu(), disable_expansion_pak: handle.get_disable_expansion_pak(), - cheats: std::collections::HashMap::new(), // not used here + cheats: rustc_hash::FxHashMap::default(), // not used here load_savestate_slot: None, }, write_sender, diff --git a/src/ui/storage.rs b/src/ui/storage.rs index 07c837a..034410b 100644 --- a/src/ui/storage.rs +++ b/src/ui/storage.rs @@ -26,19 +26,19 @@ pub struct Paths { // the bool indicates whether the save has been written to // if that is the case, it will be flushed to the disk when the program closes -#[derive(Default, serde::Serialize, serde::Deserialize)] +#[derive(Default, Clone, serde::Serialize, serde::Deserialize)] pub struct Save { pub data: Vec, pub write_pending: bool, } -#[derive(Default, serde::Serialize, serde::Deserialize)] +#[derive(Default, Clone, serde::Serialize, serde::Deserialize)] pub struct RomSave { - pub data: std::collections::HashMap, + pub data: rustc_hash::FxHashMap, pub write_pending: bool, } -#[derive(Default, serde::Serialize, serde::Deserialize)] +#[derive(Default, Clone, serde::Serialize, serde::Deserialize)] pub struct Saves { pub eeprom: Save, pub sram: Save,