Support manual/automatic ROM reloading
This commit is contained in:
+58
-2
@@ -1,10 +1,11 @@
|
||||
use anyhow::Result;
|
||||
use notify::Watcher;
|
||||
use rand::Rng;
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{Read, Seek as _, SeekFrom, Write as _},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
sync::{Arc, atomic::AtomicBool},
|
||||
};
|
||||
|
||||
use crate::emulator::{SimId, game_info::GameInfo, shrooms_vb_util::rom_from_isx};
|
||||
@@ -15,10 +16,12 @@ pub struct Cart {
|
||||
sram_file: File,
|
||||
pub sram: Vec<u8>,
|
||||
pub info: Arc<GameInfo>,
|
||||
watcher: Option<notify::RecommendedWatcher>,
|
||||
changed: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Cart {
|
||||
pub fn load(file_path: &Path, sim_id: SimId) -> Result<Self> {
|
||||
pub fn load(file_path: &Path, sim_id: SimId, watch: bool) -> Result<Self> {
|
||||
let rom = fs::read(file_path)?;
|
||||
let (rom, info) = try_parse_isx(file_path, &rom)
|
||||
.or_else(|| try_parse_elf(file_path, &rom))
|
||||
@@ -45,12 +48,21 @@ impl Cart {
|
||||
sram
|
||||
};
|
||||
|
||||
let changed = Arc::new(AtomicBool::new(false));
|
||||
let watcher = if watch {
|
||||
build_watcher(file_path, changed.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Cart {
|
||||
file_path: file_path.to_path_buf(),
|
||||
rom,
|
||||
sram_file,
|
||||
sram,
|
||||
info: Arc::new(info),
|
||||
watcher,
|
||||
changed,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -59,6 +71,50 @@ impl Cart {
|
||||
self.sram_file.write_all(&self.sram)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn changed(&self) -> bool {
|
||||
self.changed.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn is_watching(&self) -> bool {
|
||||
self.watcher.is_some()
|
||||
}
|
||||
|
||||
pub fn restart_watching(&mut self) {
|
||||
self.changed = Arc::new(AtomicBool::new(false));
|
||||
if let Some(mut watcher) = self.watcher.take() {
|
||||
let _ = watcher.unwatch(&self.file_path);
|
||||
};
|
||||
self.watcher = build_watcher(&self.file_path, self.changed.clone());
|
||||
}
|
||||
|
||||
pub fn stop_watching(&mut self) {
|
||||
self.changed = Arc::new(AtomicBool::new(false));
|
||||
self.watcher = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn build_watcher(file_path: &Path, changed: Arc<AtomicBool>) -> Option<notify::RecommendedWatcher> {
|
||||
let file_path = file_path.to_path_buf();
|
||||
let mut watcher =
|
||||
notify::recommended_watcher(move |event: Result<notify::Event, notify::Error>| {
|
||||
let Ok(e) = event else {
|
||||
return;
|
||||
};
|
||||
let modified = !matches!(
|
||||
e.kind,
|
||||
notify::EventKind::Access(_)
|
||||
| notify::EventKind::Modify(notify::event::ModifyKind::Metadata(_))
|
||||
);
|
||||
if modified {
|
||||
changed.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
})
|
||||
.ok()?;
|
||||
watcher
|
||||
.watch(&file_path, notify::RecursiveMode::NonRecursive)
|
||||
.ok()?;
|
||||
Some(watcher)
|
||||
}
|
||||
|
||||
fn try_parse_isx(file_path: &Path, data: &[u8]) -> Option<(Vec<u8>, GameInfo)> {
|
||||
|
||||
Reference in New Issue
Block a user