mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
flush saves to disk (#658)
* flush saves to disk * more * more * more * more * more * more * more * more
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@ sha2 = "0.10"
|
||||
sdl3-sys = { version = "0.6", features = ["build-from-source-static"] }
|
||||
sdl3-ttf-sys = { version = "0.6", features = ["build-from-source-static", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] }
|
||||
rfd = "0.17"
|
||||
tokio = {version = "1.46", features = ["rt-multi-thread", "macros"] }
|
||||
tokio = {version = "1.46", features = ["rt-multi-thread", "macros", "fs"] }
|
||||
spin_sleep = "1.3"
|
||||
encoding_rs = "0.8"
|
||||
mimalloc = "0.1"
|
||||
|
||||
@@ -208,7 +208,6 @@ pub struct Device {
|
||||
#[serde(skip, default = "set_rng")]
|
||||
pub rng: rand_chacha::ChaCha8Rng,
|
||||
pub vru: controller::vru::Vru,
|
||||
pub vru_window: controller::vru::VruWindow,
|
||||
pub transferpaks: [controller::transferpak::TransferPak; 4],
|
||||
pub cheats: cheats::Cheats,
|
||||
}
|
||||
@@ -482,10 +481,6 @@ impl Device {
|
||||
limit_freq: 2,
|
||||
limit_freq_check: std::time::Instant::now(),
|
||||
},
|
||||
vru_window: controller::vru::VruWindow {
|
||||
window_notifier: None,
|
||||
word_receiver: None,
|
||||
},
|
||||
vru: controller::vru::Vru {
|
||||
status: 0,
|
||||
voice_state: 0,
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ fn eeprom_write_block(device: &mut device::Device, block: usize, offset: usize,
|
||||
|
||||
device.pif.ram[status] = 0x00;
|
||||
|
||||
device.ui.storage.saves.eeprom.written = true
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Eeprom4k);
|
||||
}
|
||||
|
||||
fn time2data(device: &mut device::Device, offset: usize) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::device;
|
||||
use crate::netplay;
|
||||
use crate::ui;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub const CART_MASK: usize = 0xFFFFFFF;
|
||||
@@ -63,7 +64,7 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3
|
||||
.data
|
||||
.insert((masked_address + i) as u32, *item);
|
||||
}
|
||||
device.ui.storage.saves.romsave.written = true;
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Romsave);
|
||||
}
|
||||
|
||||
device.cart.latch = value & mask;
|
||||
@@ -94,7 +95,7 @@ pub fn dma_read(
|
||||
.unwrap_or(&0),
|
||||
);
|
||||
}
|
||||
device.ui.storage.saves.romsave.written = true;
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Romsave);
|
||||
}
|
||||
|
||||
device::pi::calculate_cycles(device, 1, length)
|
||||
|
||||
@@ -208,7 +208,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
.copy_from_slice(&data);
|
||||
i += 4;
|
||||
}
|
||||
device.ui.storage.saves.sdcard.written = true;
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Sdcard);
|
||||
}
|
||||
'U' => {
|
||||
device.cart.sc64.regs[SC64_DATA0_REG as usize] = 0;
|
||||
@@ -405,7 +405,7 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3
|
||||
device::memory::masked_write_32(&mut data, value, mask);
|
||||
device.ui.storage.saves.eeprom.data[masked_address..masked_address + 4]
|
||||
.copy_from_slice(&data.to_be_bytes());
|
||||
device.ui.storage.saves.eeprom.written = true
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Eeprom4k);
|
||||
} else {
|
||||
let masked_address = address as usize & SC64_BUFFER_MASK;
|
||||
let mut data = u32::from_be_bytes(
|
||||
@@ -429,7 +429,7 @@ pub fn dma_read(
|
||||
let buffer = if cart_addr & 0x2000 != 0 {
|
||||
device::cart::format_eeprom(device);
|
||||
cart_addr &= SC64_EEPROM_MASK as u32;
|
||||
device.ui.storage.saves.eeprom.written = true;
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Eeprom4k);
|
||||
&mut device.ui.storage.saves.eeprom.data
|
||||
} else {
|
||||
cart_addr &= SC64_BUFFER_MASK as u32;
|
||||
|
||||
@@ -107,7 +107,7 @@ fn write_mem_sram(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
device.ui.storage.saves.sram.data[masked_address..masked_address + 4]
|
||||
.copy_from_slice(&data.to_be_bytes());
|
||||
|
||||
device.ui.storage.saves.sram.written = true
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Sram);
|
||||
}
|
||||
|
||||
fn write_mem_flash(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
@@ -167,7 +167,7 @@ fn dma_read_sram(device: &mut device::Device, mut cart_addr: u32, mut dram_addr:
|
||||
j += 1;
|
||||
}
|
||||
|
||||
device.ui.storage.saves.sram.written = true
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Sram);
|
||||
}
|
||||
|
||||
fn dma_read_flash(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) {
|
||||
@@ -331,12 +331,12 @@ fn flashram_command(device: &mut device::Device, command: u32) {
|
||||
for i in 0..128 * 128 {
|
||||
device.ui.storage.saves.flash.data[offset + i] = 0xFF;
|
||||
}
|
||||
device.ui.storage.saves.flash.written = true
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Flash);
|
||||
} else if device.cart.flashram.mode == FlashramMode::ChipErase {
|
||||
for i in 0..FLASHRAM_SIZE {
|
||||
device.ui.storage.saves.flash.data[i] = 0xFF;
|
||||
}
|
||||
device.ui.storage.saves.flash.written = true
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Flash);
|
||||
} else {
|
||||
panic!("Unexpected flash erase command")
|
||||
}
|
||||
@@ -356,7 +356,7 @@ fn flashram_command(device: &mut device::Device, command: u32) {
|
||||
for i in 0..128 {
|
||||
device.ui.storage.saves.flash.data[offset + i] = device.cart.flashram.page_buf[i];
|
||||
}
|
||||
device.ui.storage.saves.flash.written = true;
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Flash);
|
||||
|
||||
/* clear program busy flag, set program success flag, transition to status mode */
|
||||
device.cart.flashram.status &= !0x01;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
pub const MEMPAK_SIZE: usize = 0x8000;
|
||||
const MPK_PAGE_SIZE: usize = 256;
|
||||
@@ -96,6 +97,6 @@ pub fn write(device: &mut device::Device, channel: usize, address: u16, data: us
|
||||
device.ui.storage.saves.mempak.data[offset..offset + size]
|
||||
.copy_from_slice(&device.pif.ram[data..data + size]);
|
||||
|
||||
device.ui.storage.saves.mempak.written = true
|
||||
ui::storage::schedule_save(device, ui::storage::SaveTypes::Mempak);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,6 @@ pub struct Vru {
|
||||
pub word_mappings: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct VruWindow {
|
||||
#[serde(skip)]
|
||||
pub window_notifier: Option<tokio::sync::mpsc::Sender<Option<Vec<String>>>>,
|
||||
#[serde(skip)]
|
||||
pub word_receiver: Option<tokio::sync::mpsc::Receiver<String>>,
|
||||
}
|
||||
|
||||
pub fn init(device: &mut device::Device) {
|
||||
reset_vru(device);
|
||||
create_word_mappings(device);
|
||||
@@ -187,13 +179,8 @@ pub fn process(device: &mut device::Device, channel: usize) {
|
||||
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = 0;
|
||||
}
|
||||
JCMD_VRU_READ => {
|
||||
let index = if let Some(window_notifier) = &device.vru_window.window_notifier {
|
||||
ui::vru::prompt_for_match(
|
||||
&device.vru.words,
|
||||
window_notifier,
|
||||
device.vru_window.word_receiver.as_mut().unwrap(),
|
||||
device.vi.frame_time,
|
||||
)
|
||||
let index = if let Some(weak) = &device.ui.weak {
|
||||
ui::vru::prompt_for_match(&device.vru.words, weak, device.vi.frame_time)
|
||||
} else {
|
||||
0x7FFF
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
|
||||
pub const EVENT_TYPE_NONE: usize = 0;
|
||||
pub const EVENT_TYPE_AI: usize = 1;
|
||||
@@ -12,7 +13,8 @@ pub const EVENT_TYPE_SPDMA: usize = 8;
|
||||
pub const EVENT_TYPE_COMPARE: usize = 9;
|
||||
pub const EVENT_TYPE_VRU: usize = 10;
|
||||
pub const EVENT_TYPE_PAK: usize = 11;
|
||||
pub const EVENT_TYPE_COUNT: usize = 12;
|
||||
pub const EVENT_TYPE_SAVE: usize = 12;
|
||||
pub const EVENT_TYPE_COUNT: usize = 13;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Event {
|
||||
@@ -60,6 +62,7 @@ fn get_event_handler(name: usize) -> fn(&mut device::Device) {
|
||||
EVENT_TYPE_COMPARE => device::cop0::compare_event,
|
||||
EVENT_TYPE_VRU => device::controller::vru::vru_talking_event,
|
||||
EVENT_TYPE_PAK => device::controller::pak_switch_event,
|
||||
EVENT_TYPE_SAVE => ui::storage::save_event,
|
||||
_ => dummy_event,
|
||||
}
|
||||
}
|
||||
|
||||
+34
-27
@@ -74,8 +74,15 @@ struct Args {
|
||||
clear_input_bindings: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
fn main() -> std::io::Result<()> {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.worker_threads(4)
|
||||
.thread_name("n64")
|
||||
.thread_stack_size(env!("N64_STACK_SIZE").parse().unwrap())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let dirs = ui::get_dirs();
|
||||
|
||||
std::fs::create_dir_all(dirs.config_dir)?;
|
||||
@@ -90,32 +97,29 @@ async fn main() -> std::io::Result<()> {
|
||||
file_path.display()
|
||||
)));
|
||||
};
|
||||
let handle = std::thread::Builder::new()
|
||||
.name("n64".to_string())
|
||||
.stack_size(env!("N64_STACK_SIZE").parse().unwrap())
|
||||
.spawn(move || {
|
||||
let mut device = device::Device::new();
|
||||
let overclock = device.ui.config.emulation.overclock;
|
||||
let disable_expansion_pak = device.ui.config.emulation.disable_expansion_pak;
|
||||
|
||||
let game_cheats = {
|
||||
let game_crc = ui::storage::get_game_crc(&rom_contents);
|
||||
let cheats = ui::config::Cheats::new();
|
||||
cheats.cheats.get(&game_crc).cloned().unwrap_or_default()
|
||||
};
|
||||
device::run_game(
|
||||
&mut device,
|
||||
rom_contents,
|
||||
ui::gui::GameSettings {
|
||||
fullscreen: args.fullscreen,
|
||||
overclock,
|
||||
disable_expansion_pak,
|
||||
cheats: game_cheats,
|
||||
},
|
||||
);
|
||||
})?;
|
||||
let handle = runtime.spawn(async move {
|
||||
let mut device = device::Device::new();
|
||||
let overclock = device.ui.config.emulation.overclock;
|
||||
let disable_expansion_pak = device.ui.config.emulation.disable_expansion_pak;
|
||||
|
||||
handle.join().unwrap();
|
||||
let game_cheats = {
|
||||
let game_crc = ui::storage::get_game_crc(&rom_contents);
|
||||
let cheats = ui::config::Cheats::new();
|
||||
cheats.cheats.get(&game_crc).cloned().unwrap_or_default()
|
||||
};
|
||||
device::run_game(
|
||||
&mut device,
|
||||
rom_contents,
|
||||
ui::gui::GameSettings {
|
||||
fullscreen: args.fullscreen,
|
||||
overclock,
|
||||
disable_expansion_pak,
|
||||
cheats: game_cheats,
|
||||
},
|
||||
);
|
||||
});
|
||||
runtime.block_on(handle).unwrap()
|
||||
} else if std::env::args().count() > 1 {
|
||||
let mut ui = ui::Ui::new();
|
||||
|
||||
@@ -157,7 +161,10 @@ async fn main() -> std::io::Result<()> {
|
||||
ui::input::bind_input_profile(&mut ui, profile, port);
|
||||
}
|
||||
} else {
|
||||
gui::app_window();
|
||||
runtime.block_on(async {
|
||||
gui::app_window();
|
||||
});
|
||||
}
|
||||
runtime.shutdown_timeout(std::time::Duration::from_secs(1));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ pub struct Ui {
|
||||
pub storage: Storage,
|
||||
pub video: Video,
|
||||
pub usb: Usb,
|
||||
pub weak: Option<slint::Weak<gui::AppWindow>>,
|
||||
}
|
||||
|
||||
impl Drop for Ui {
|
||||
@@ -156,27 +157,27 @@ impl Ui {
|
||||
write_to_disk: true,
|
||||
eeprom: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
write_pending: false,
|
||||
},
|
||||
sram: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
write_pending: false,
|
||||
},
|
||||
flash: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
write_pending: false,
|
||||
},
|
||||
mempak: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
write_pending: false,
|
||||
},
|
||||
sdcard: storage::Save {
|
||||
data: Vec::new(),
|
||||
written: false,
|
||||
write_pending: false,
|
||||
},
|
||||
romsave: storage::RomSave {
|
||||
data: std::collections::HashMap::new(),
|
||||
written: false,
|
||||
write_pending: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -195,6 +196,7 @@ impl Ui {
|
||||
usb_tx: None,
|
||||
cart_rx: None,
|
||||
},
|
||||
weak: None,
|
||||
dirs,
|
||||
with_sdl,
|
||||
}
|
||||
|
||||
+49
-134
@@ -24,11 +24,6 @@ pub struct GbPaths {
|
||||
pub ram: [Option<std::path::PathBuf>; 4],
|
||||
}
|
||||
|
||||
pub struct VruChannel {
|
||||
pub vru_window_notifier: Option<tokio::sync::mpsc::Sender<Option<Vec<String>>>>,
|
||||
pub vru_word_receiver: Option<tokio::sync::mpsc::Receiver<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GameSettings {
|
||||
pub fullscreen: bool,
|
||||
@@ -337,120 +332,66 @@ pub fn app_window() {
|
||||
save_settings(&app, &controller_paths);
|
||||
}
|
||||
|
||||
fn setup_vru_word_watcher(
|
||||
weak_vru: slint::Weak<AppWindow>,
|
||||
vru_word_notifier: tokio::sync::mpsc::Sender<String>,
|
||||
mut vru_window_receiver: tokio::sync::mpsc::Receiver<Option<Vec<String>>>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let notifier = vru_word_notifier.clone();
|
||||
let notifier_closed = vru_word_notifier.clone();
|
||||
let result = vru_window_receiver.recv().await;
|
||||
if let Some(Some(words)) = result {
|
||||
weak_vru
|
||||
.upgrade_in_event_loop(move |_handle| {
|
||||
let vru_dialog = VruDialog::new().unwrap();
|
||||
let vru_dialog_weak = vru_dialog.as_weak();
|
||||
|
||||
vru_dialog.on_vru_button_clicked(move |chosen_word| {
|
||||
notifier.try_send(chosen_word.to_string()).unwrap();
|
||||
vru_dialog_weak.unwrap().window().hide().unwrap();
|
||||
});
|
||||
|
||||
vru_dialog.window().on_close_requested(move || {
|
||||
notifier_closed.try_send("".to_string()).unwrap();
|
||||
slint::CloseRequestResponse::HideWindow
|
||||
});
|
||||
|
||||
let words_vec = slint::VecModel::default();
|
||||
for word in words {
|
||||
words_vec.push(word.into());
|
||||
}
|
||||
let words_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(words_vec);
|
||||
vru_dialog.set_words(slint::ModelRc::from(words_model));
|
||||
|
||||
vru_dialog.show().unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn run_rom(
|
||||
gb_paths: GbPaths,
|
||||
file_path: std::path::PathBuf,
|
||||
mut game_settings: GameSettings,
|
||||
vru_channel: VruChannel,
|
||||
netplay: Option<NetplayDevice>,
|
||||
usb: ui::Usb,
|
||||
weak: slint::Weak<AppWindow>,
|
||||
) {
|
||||
std::thread::Builder::new()
|
||||
.name("n64".to_string())
|
||||
.stack_size(env!("N64_STACK_SIZE").parse().unwrap())
|
||||
.spawn(move || {
|
||||
weak.upgrade_in_event_loop(move |handle| handle.set_game_running(true))
|
||||
.unwrap();
|
||||
|
||||
let mut device = device::Device::new();
|
||||
device.ui.config.rom_dir = file_path.parent().unwrap().to_path_buf();
|
||||
|
||||
for i in 0..4 {
|
||||
if gb_paths.rom[i].is_some() && gb_paths.ram[i].is_some() {
|
||||
device.transferpaks[i].cart.rom =
|
||||
std::fs::read(gb_paths.rom[i].as_ref().unwrap()).unwrap();
|
||||
|
||||
device.transferpaks[i].cart.ram =
|
||||
std::fs::read(gb_paths.ram[i].as_ref().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
device.ui.usb.usb_tx = usb.usb_tx;
|
||||
device.ui.usb.cart_rx = usb.cart_rx;
|
||||
|
||||
device.vru_window.window_notifier = vru_channel.vru_window_notifier;
|
||||
device.vru_window.word_receiver = vru_channel.vru_word_receiver;
|
||||
|
||||
if let Some(rom_contents) = device::get_rom_contents(file_path.as_path()) {
|
||||
if let Some(netplay_device) = netplay {
|
||||
device.netplay = Some(netplay::init(
|
||||
netplay_device.peer_addr,
|
||||
netplay_device.player_number,
|
||||
));
|
||||
} else {
|
||||
game_settings.cheats = {
|
||||
let game_crc = ui::storage::get_game_crc(&rom_contents);
|
||||
let cheats = ui::config::Cheats::new();
|
||||
cheats.cheats.get(&game_crc).cloned().unwrap_or_default()
|
||||
};
|
||||
}
|
||||
device::run_game(&mut device, rom_contents, game_settings);
|
||||
if device.netplay.is_some() {
|
||||
netplay::close(&mut device);
|
||||
}
|
||||
} else {
|
||||
println!("Could not read rom file");
|
||||
}
|
||||
|
||||
if let Some(vru_window_notifier) = device.vru_window.window_notifier {
|
||||
vru_window_notifier.try_send(None).unwrap();
|
||||
}
|
||||
|
||||
let rom_dir = device.ui.config.rom_dir.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
if let Some(rom_dir_str) = rom_dir.to_str() {
|
||||
handle.set_rom_dir(rom_dir_str.into());
|
||||
}
|
||||
handle.set_game_running(false);
|
||||
})
|
||||
tokio::spawn(async move {
|
||||
weak.upgrade_in_event_loop(move |handle| handle.set_game_running(true))
|
||||
.unwrap();
|
||||
|
||||
let mut device = device::Device::new();
|
||||
device.ui.config.rom_dir = file_path.parent().unwrap().to_path_buf();
|
||||
|
||||
for i in 0..4 {
|
||||
if gb_paths.rom[i].is_some() && gb_paths.ram[i].is_some() {
|
||||
device.transferpaks[i].cart.rom =
|
||||
std::fs::read(gb_paths.rom[i].as_ref().unwrap()).unwrap();
|
||||
|
||||
device.transferpaks[i].cart.ram =
|
||||
std::fs::read(gb_paths.ram[i].as_ref().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
device.ui.usb.usb_tx = usb.usb_tx;
|
||||
device.ui.usb.cart_rx = usb.cart_rx;
|
||||
|
||||
device.ui.weak = Some(weak.clone());
|
||||
|
||||
if let Some(rom_contents) = device::get_rom_contents(file_path.as_path()) {
|
||||
if let Some(netplay_device) = netplay {
|
||||
device.netplay = Some(netplay::init(
|
||||
netplay_device.peer_addr,
|
||||
netplay_device.player_number,
|
||||
));
|
||||
} else {
|
||||
game_settings.cheats = {
|
||||
let game_crc = ui::storage::get_game_crc(&rom_contents);
|
||||
let cheats = ui::config::Cheats::new();
|
||||
cheats.cheats.get(&game_crc).cloned().unwrap_or_default()
|
||||
};
|
||||
}
|
||||
device::run_game(&mut device, rom_contents, game_settings);
|
||||
if device.netplay.is_some() {
|
||||
netplay::close(&mut device);
|
||||
}
|
||||
} else {
|
||||
println!("Could not read rom file");
|
||||
}
|
||||
|
||||
let rom_dir = device.ui.config.rom_dir.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
if let Some(rom_dir_str) = rom_dir.to_str() {
|
||||
handle.set_rom_dir(rom_dir_str.into());
|
||||
}
|
||||
handle.set_game_running(false);
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn open_rom(app: &AppWindow, usb: ui::Usb) {
|
||||
@@ -486,25 +427,10 @@ fn open_rom(app: &AppWindow, usb: ui::Usb) {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let (vru_window_notifier, vru_window_receiver): (
|
||||
tokio::sync::mpsc::Sender<Option<Vec<String>>>,
|
||||
tokio::sync::mpsc::Receiver<Option<Vec<String>>>,
|
||||
) = tokio::sync::mpsc::channel(5);
|
||||
|
||||
let (vru_word_notifier, vru_word_receiver): (
|
||||
tokio::sync::mpsc::Sender<String>,
|
||||
tokio::sync::mpsc::Receiver<String>,
|
||||
) = tokio::sync::mpsc::channel(5);
|
||||
|
||||
let fullscreen = app.get_fullscreen();
|
||||
let overclock = app.get_overclock_n64_cpu();
|
||||
let emulate_vru = app.get_emulate_vru();
|
||||
let disable_expansion_pak = app.get_disable_expansion_pak();
|
||||
|
||||
if emulate_vru {
|
||||
setup_vru_word_watcher(app.as_weak(), vru_word_notifier, vru_window_receiver);
|
||||
}
|
||||
let weak = app.as_weak();
|
||||
tokio::spawn(async move {
|
||||
if let Some(file) = select_rom.await {
|
||||
@@ -533,17 +459,6 @@ fn open_rom(app: &AppWindow, usb: ui::Usb) {
|
||||
disable_expansion_pak,
|
||||
cheats: std::collections::HashMap::new(), // will be filled in later
|
||||
},
|
||||
if emulate_vru {
|
||||
VruChannel {
|
||||
vru_window_notifier: Some(vru_window_notifier),
|
||||
vru_word_receiver: Some(vru_word_receiver),
|
||||
}
|
||||
} else {
|
||||
VruChannel {
|
||||
vru_window_notifier: None,
|
||||
vru_word_receiver: None,
|
||||
}
|
||||
},
|
||||
None,
|
||||
usb,
|
||||
weak,
|
||||
|
||||
+1
-5
@@ -2,7 +2,7 @@ use crate::device;
|
||||
use crate::ui;
|
||||
use crate::ui::gui::{
|
||||
AppWindow, CustomNetplayServer, DispatcherDialog, ErrorDialog, GameSettings, GbPaths,
|
||||
NetplayCreate, NetplayDevice, NetplayJoin, NetplayWait, VruChannel, run_rom, save_settings,
|
||||
NetplayCreate, NetplayDevice, NetplayJoin, NetplayWait, run_rom, save_settings,
|
||||
};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -1084,10 +1084,6 @@ fn setup_wait_window(
|
||||
disable_expansion_pak: game_settings.disable_expansion_pak,
|
||||
cheats: game_settings.cheats,
|
||||
},
|
||||
VruChannel {
|
||||
vru_window_notifier: None,
|
||||
vru_word_receiver: None,
|
||||
},
|
||||
Some(NetplayDevice {
|
||||
peer_addr: socket_addr,
|
||||
player_number: player_number as u8,
|
||||
|
||||
+65
-27
@@ -28,13 +28,13 @@ pub struct Paths {
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Save {
|
||||
pub data: Vec<u8>,
|
||||
pub written: bool,
|
||||
pub write_pending: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct RomSave {
|
||||
pub data: std::collections::HashMap<u32, u8>,
|
||||
pub written: bool,
|
||||
pub write_pending: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
@@ -357,7 +357,7 @@ pub fn compress_file(data: &[(&[u8], &str)]) -> Vec<u8> {
|
||||
fn writeback_sdcard(device: &mut device::Device) {
|
||||
let length;
|
||||
let save_data: &[u8];
|
||||
if device.ui.storage.saves.eeprom.written {
|
||||
if device.ui.storage.saves.eeprom.write_pending {
|
||||
if device
|
||||
.ui
|
||||
.storage
|
||||
@@ -369,12 +369,15 @@ fn writeback_sdcard(device: &mut device::Device) {
|
||||
length = 4;
|
||||
}
|
||||
save_data = device.ui.storage.saves.eeprom.data.as_ref();
|
||||
} else if device.ui.storage.saves.sram.written {
|
||||
device.ui.storage.saves.eeprom.write_pending = false;
|
||||
} else if device.ui.storage.saves.sram.write_pending {
|
||||
length = device.ui.storage.saves.sram.data.len() / 512;
|
||||
save_data = device.ui.storage.saves.sram.data.as_ref();
|
||||
} else if device.ui.storage.saves.flash.written {
|
||||
device.ui.storage.saves.sram.write_pending = false;
|
||||
} else if device.ui.storage.saves.flash.write_pending {
|
||||
length = device.ui.storage.saves.flash.data.len() / 512;
|
||||
save_data = device.ui.storage.saves.flash.data.as_ref();
|
||||
device.ui.storage.saves.flash.write_pending = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -384,37 +387,69 @@ fn writeback_sdcard(device: &mut device::Device) {
|
||||
device.ui.storage.saves.sdcard.data[offset..offset + 512]
|
||||
.copy_from_slice(&save_data[i * 512..(i + 1) * 512]);
|
||||
}
|
||||
device.ui.storage.saves.sdcard.written = true;
|
||||
device.ui.storage.saves.sdcard.write_pending = true;
|
||||
}
|
||||
|
||||
pub fn write_saves(device: &mut device::Device) {
|
||||
if device.netplay.is_none() || device.netplay.as_ref().unwrap().player_number == 0 {
|
||||
if device.ui.storage.saves.write_to_disk {
|
||||
if device.ui.storage.saves.eeprom.written {
|
||||
write_save(&device.ui, SaveTypes::Eeprom16k)
|
||||
if device.ui.storage.saves.eeprom.write_pending {
|
||||
write_save(&mut device.ui, SaveTypes::Eeprom16k)
|
||||
}
|
||||
if device.ui.storage.saves.sram.written {
|
||||
write_save(&device.ui, SaveTypes::Sram)
|
||||
if device.ui.storage.saves.sram.write_pending {
|
||||
write_save(&mut device.ui, SaveTypes::Sram)
|
||||
}
|
||||
if device.ui.storage.saves.flash.written {
|
||||
write_save(&device.ui, SaveTypes::Flash)
|
||||
if device.ui.storage.saves.flash.write_pending {
|
||||
write_save(&mut device.ui, SaveTypes::Flash)
|
||||
}
|
||||
if device.ui.storage.saves.romsave.written {
|
||||
write_save(&device.ui, SaveTypes::Romsave)
|
||||
if device.ui.storage.saves.romsave.write_pending {
|
||||
write_save(&mut device.ui, SaveTypes::Romsave)
|
||||
}
|
||||
} else {
|
||||
writeback_sdcard(device)
|
||||
}
|
||||
if device.ui.storage.saves.mempak.written {
|
||||
write_save(&device.ui, SaveTypes::Mempak)
|
||||
if device.ui.storage.saves.mempak.write_pending {
|
||||
write_save(&mut device.ui, SaveTypes::Mempak)
|
||||
}
|
||||
if device.ui.storage.saves.sdcard.written {
|
||||
write_save(&device.ui, SaveTypes::Sdcard)
|
||||
if device.ui.storage.saves.sdcard.write_pending {
|
||||
write_save(&mut device.ui, SaveTypes::Sdcard)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_save(ui: &ui::Ui, save_type: SaveTypes) {
|
||||
pub fn save_event(device: &mut device::Device) {
|
||||
write_saves(device);
|
||||
}
|
||||
|
||||
pub fn schedule_save(device: &mut device::Device, save_type: SaveTypes) {
|
||||
match save_type {
|
||||
SaveTypes::Eeprom4k | SaveTypes::Eeprom16k => {
|
||||
device.ui.storage.saves.eeprom.write_pending = true;
|
||||
}
|
||||
SaveTypes::Sram => {
|
||||
device.ui.storage.saves.sram.write_pending = true;
|
||||
}
|
||||
SaveTypes::Flash => {
|
||||
device.ui.storage.saves.flash.write_pending = true;
|
||||
}
|
||||
SaveTypes::Mempak => {
|
||||
device.ui.storage.saves.mempak.write_pending = true;
|
||||
}
|
||||
SaveTypes::Sdcard => {
|
||||
device.ui.storage.saves.sdcard.write_pending = true;
|
||||
}
|
||||
SaveTypes::Romsave => {
|
||||
device.ui.storage.saves.romsave.write_pending = true;
|
||||
}
|
||||
}
|
||||
device::events::create_event(
|
||||
device,
|
||||
device::events::EVENT_TYPE_SAVE,
|
||||
device.cpu.clock_rate, // 1 second
|
||||
);
|
||||
}
|
||||
|
||||
fn write_save(ui: &mut ui::Ui, save_type: SaveTypes) {
|
||||
let path: &std::path::Path;
|
||||
let data: &Vec<u8>;
|
||||
let rom_save_data: Vec<u8>;
|
||||
@@ -422,35 +457,38 @@ fn write_save(ui: &ui::Ui, save_type: SaveTypes) {
|
||||
SaveTypes::Eeprom4k | SaveTypes::Eeprom16k => {
|
||||
path = ui.storage.paths.eep_file_path.as_ref();
|
||||
data = ui.storage.saves.eeprom.data.as_ref();
|
||||
ui.storage.saves.eeprom.write_pending = false;
|
||||
}
|
||||
SaveTypes::Sram => {
|
||||
path = ui.storage.paths.sra_file_path.as_ref();
|
||||
data = ui.storage.saves.sram.data.as_ref();
|
||||
ui.storage.saves.sram.write_pending = false;
|
||||
}
|
||||
SaveTypes::Flash => {
|
||||
path = ui.storage.paths.fla_file_path.as_ref();
|
||||
data = ui.storage.saves.flash.data.as_ref();
|
||||
ui.storage.saves.flash.write_pending = false;
|
||||
}
|
||||
SaveTypes::Mempak => {
|
||||
path = ui.storage.paths.pak_file_path.as_ref();
|
||||
data = ui.storage.saves.mempak.data.as_ref();
|
||||
ui.storage.saves.mempak.write_pending = false;
|
||||
}
|
||||
SaveTypes::Sdcard => {
|
||||
path = ui.storage.paths.sdcard_file_path.as_ref();
|
||||
data = ui.storage.saves.sdcard.data.as_ref();
|
||||
ui.storage.saves.sdcard.write_pending = false;
|
||||
}
|
||||
SaveTypes::Romsave => {
|
||||
path = ui.storage.paths.romsave_file_path.as_ref();
|
||||
rom_save_data = postcard::to_stdvec(&ui.storage.saves.romsave.data).unwrap();
|
||||
data = rom_save_data.as_ref();
|
||||
ui.storage.saves.romsave.write_pending = false;
|
||||
}
|
||||
}
|
||||
let result = std::fs::write(path, data);
|
||||
if result.is_err() {
|
||||
panic!(
|
||||
"could not save {} {}",
|
||||
path.display(),
|
||||
result.err().unwrap()
|
||||
)
|
||||
}
|
||||
let save_data = data.clone();
|
||||
let save_path = path.to_path_buf();
|
||||
tokio::spawn(async move {
|
||||
tokio::fs::write(save_path, save_data).await.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,10 +53,6 @@ fn upload_rom(
|
||||
disable_expansion_pak: handle.get_disable_expansion_pak(),
|
||||
cheats: std::collections::HashMap::new(), // will be filled in later
|
||||
},
|
||||
ui::gui::VruChannel {
|
||||
vru_window_notifier: None,
|
||||
vru_word_receiver: None,
|
||||
},
|
||||
None,
|
||||
ui::Usb {
|
||||
usb_tx: Some(usb_tx),
|
||||
|
||||
+38
-5
@@ -1,16 +1,49 @@
|
||||
use crate::gui;
|
||||
use slint::ComponentHandle;
|
||||
|
||||
pub fn prompt_for_match(
|
||||
words: &[String],
|
||||
window_notifier: &tokio::sync::mpsc::Sender<Option<Vec<String>>>,
|
||||
word_index_receiver: &mut tokio::sync::mpsc::Receiver<String>,
|
||||
weak_vru: &slint::Weak<gui::AppWindow>,
|
||||
frame_time: f64,
|
||||
) -> u16 {
|
||||
let mut dedup_words = words.to_owned();
|
||||
dedup_words.sort();
|
||||
dedup_words.dedup();
|
||||
window_notifier.try_send(Some(dedup_words)).unwrap();
|
||||
let mut result = word_index_receiver.try_recv();
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
weak_vru
|
||||
.upgrade_in_event_loop(move |_handle| {
|
||||
let vru_dialog = gui::VruDialog::new().unwrap();
|
||||
let vru_dialog_weak = vru_dialog.as_weak();
|
||||
|
||||
let tx_clicked = tx.clone();
|
||||
|
||||
vru_dialog.on_vru_button_clicked(move |chosen_word| {
|
||||
tx_clicked.try_send(chosen_word.to_string()).unwrap();
|
||||
vru_dialog_weak.unwrap().window().hide().unwrap();
|
||||
});
|
||||
|
||||
vru_dialog.window().on_close_requested(move || {
|
||||
tx.try_send("".to_string()).unwrap();
|
||||
slint::CloseRequestResponse::HideWindow
|
||||
});
|
||||
|
||||
let words_vec = slint::VecModel::default();
|
||||
for word in dedup_words {
|
||||
words_vec.push(word.into());
|
||||
}
|
||||
let words_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(words_vec);
|
||||
vru_dialog.set_words(slint::ModelRc::from(words_model));
|
||||
|
||||
vru_dialog.show().unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut result = rx.try_recv();
|
||||
while result.is_err() {
|
||||
result = word_index_receiver.try_recv();
|
||||
result = rx.try_recv();
|
||||
std::thread::sleep(std::time::Duration::from_secs_f64(frame_time));
|
||||
unsafe { sdl3_sys::events::SDL_PumpEvents() }; // so the OS doesn't complain about the game window being frozen
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user