drag and drop working for save states

This commit is contained in:
Theron 2020-02-29 22:26:59 -06:00
parent 7f5a71cf07
commit c7b331cec1
4 changed files with 27 additions and 33 deletions

View File

@ -23,7 +23,7 @@ ___________________
-------------------
Save state: F5
Load state: F9
(Only one save state at a time supported currently: saving a second time overwrites the first.)
(Saving state a second time overwrites the first unless you copy/rename the `.dat` file. You can drag and drop any `.dat` file onto the window to load it.)
```
The code aims to follow the explanations from the [NES dev wiki](https://wiki.nesdev.com/w/index.php/NES_reference_guide) where possible, especially in the PPU, and the comments quote from it often. Thanks to everyone who contributes to that wiki/forum, and to Michael Fogleman's [NES](https://github.com/fogleman/nes) and Scott Ferguson's [Fergulator](https://github.com/scottferg/Fergulator) for getting me unstuck at several points.
@ -47,9 +47,9 @@ The code aims to follow the explanations from the [NES dev wiki](https://wiki.ne
- DMC audio channel, high- and low-pass filters
- Better GUI and distributable solution
- Better GUI
- Save states
- Better save state handling
- Player 2 controller?

View File

@ -2,8 +2,7 @@ pub type ApuData = super::Apu;
impl super::Apu{
pub fn save_state(&self) -> ApuData {
let x: ApuData = self.clone();
x
self.clone()
}
pub fn load_state(&mut self, data: ApuData) {

View File

@ -13,13 +13,14 @@ use apu::Apu;
use cartridge::get_mapper;
use input::poll_buttons;
use screen::{init_window, draw_pixel, draw_to_window};
use state::{save_state, load_state};
use state::{save_state, load_state, change_file_extension};
use std::sync::{Arc, Mutex};
use std::time::{Instant, Duration};
use sdl2::keyboard::Keycode;
use sdl2::event::Event;
use sdl2::pixels::PixelFormatEnum;
use std::path::Path;
// use cpuprofiler::PROFILER;
@ -96,12 +97,20 @@ fn main() -> Result<(), String> {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. }
=> break 'running,
Event::KeyDown{ keycode: Some(Keycode::F5), .. } => {
let res: Result<(), String> = save_state(&cpu, &filename)
let dat_filename = state::change_file_extension(&filename, "dat").unwrap();
let res: Result<(), String> = save_state(&cpu, dat_filename)
.or_else(|e| {println!("{}", e); Ok(())});
res.unwrap();
},
Event::KeyDown{ keycode: Some(Keycode::F9), .. } => {
let res: Result<(), String> = load_state(&mut cpu, &filename)
let dat_filename = state::change_file_extension(&filename, "dat").unwrap();
let res: Result<(), String> = load_state(&mut cpu, dat_filename)
.or_else(|e| {println!("{}", e); Ok(())});
res.unwrap();
},
Event::DropFile{ timestamp: _t, window_id: _w, filename: f } => {
let p = Path::new(&f).to_path_buf();
let res: Result<(), String> = load_state(&mut cpu, p)
.or_else(|e| {println!("{}", e); Ok(())});
res.unwrap();
},

View File

@ -4,7 +4,7 @@ use super::apu;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::path::{Path, PathBuf};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
@ -14,7 +14,7 @@ struct SaveState {
apu: apu::serialize::ApuData,
}
pub fn save_state(cpu: &cpu::Cpu, filename: &str) -> Result<(), String> {
pub fn save_state(cpu: &cpu::Cpu, save_file: PathBuf) -> Result<(), String> {
let data = SaveState{
cpu: cpu.save_state(),
ppu: cpu.ppu.save_state(),
@ -22,16 +22,6 @@ pub fn save_state(cpu: &cpu::Cpu, filename: &str) -> Result<(), String> {
};
let serialized = serde_json::to_string(&data)
.map_err(|e| e.to_string())?;
let path = match Path::new(&filename).parent() {
Some(p) => p,
None => return Err("couldn't convert filename to path".to_string()),
};
let stem = match Path::new(&filename).file_stem() {
Some(s) => s,
None => return Err("couldn't get file stem".to_string()),
};
let mut save_file = path.join(stem);
save_file.set_extension("dat");
println!("state saved to file: {:?}", save_file);
let mut f = File::create(&save_file)
.expect("could not create output file for save state");
@ -39,19 +29,7 @@ pub fn save_state(cpu: &cpu::Cpu, filename: &str) -> Result<(), String> {
.map_err(|_| "couldn't write serialized data to file".to_string())
}
pub fn load_state(cpu: &mut cpu::Cpu, filename: &str) -> Result<(), String> {
// load file, deserialize to cpudata, set cpu fields to data fields
let path = match Path::new(&filename).parent() {
Some(p) => p,
None => return Err("couldn't convert filename to path".to_string()),
};
let stem = match Path::new(&filename).file_stem() {
Some(s) => s,
None => return Err("couldn't get file stem".to_string()),
};
let mut save_file = path.join(stem);
save_file.set_extension("dat");
pub fn load_state(cpu: &mut cpu::Cpu, save_file: PathBuf) -> Result<(), String> {
if Path::new(&save_file).exists() {
let mut f = File::open(save_file.clone())
.map_err(|e| e.to_string())?;
@ -71,3 +49,11 @@ pub fn load_state(cpu: &mut cpu::Cpu, filename: &str) -> Result<(), String> {
Err(format!("no save state file at {:?}", save_file))
}
}
pub fn change_file_extension(filename: &str, extension: &str) -> Option<PathBuf> {
let path = Path::new(filename).parent()?;
let stem = Path::new(&filename).file_stem()?;
let mut save_file = path.join(stem);
save_file.set_extension(extension);
Some(save_file)
}