From 67d95c93594fd5637d0543ae734d9a454af3b6e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Sun, 23 Jul 2023 22:58:41 +0100
Subject: [PATCH] chore: initial support for loading sav files

---
 frontends/sdl/src/main.rs | 14 ++++++++---
 src/util.rs               | 52 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs
index d0b54912..2fcab56b 100644
--- a/frontends/sdl/src/main.rs
+++ b/frontends/sdl/src/main.rs
@@ -13,6 +13,7 @@ use boytacean::{
     ppu::{PaletteInfo, PpuMode},
     rom::Cartridge,
     serial::{NullDevice, SerialDevice},
+    util::replace_ext,
 };
 use chrono::Utc;
 use clap::Parser;
@@ -202,10 +203,15 @@ impl Emulator {
     }
 
     pub fn load_rom(&mut self, path: Option<&str>) {
-        let path_res = path.unwrap_or(&self.rom_path);
+        let rom_path: &str = path.unwrap_or(&self.rom_path);
+        let ram_path = replace_ext(rom_path, "sav").unwrap_or("".to_string());
         let rom = self.system.load_rom_file(
-            path_res,
-            Some("C:/repo.other/boytacean/res/roms.prop/super_mario_2.sav"),
+            rom_path,
+            if Path::new(&ram_path).exists() {
+                Some(&ram_path)
+            } else {
+                None
+            },
         );
         println!(
             "========= Cartridge =========\n{}\n=============================",
@@ -216,7 +222,7 @@ impl Emulator {
                 .set_title(format!("{} [{}]", self.title, rom.title()).as_str())
                 .unwrap();
         }
-        self.rom_path = String::from(path_res);
+        self.rom_path = String::from(rom_path);
     }
 
     pub fn reset(&mut self) {
diff --git a/src/util.rs b/src/util.rs
index 96776eec..37393a92 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, fs::File, io::Read, rc::Rc};
+use std::{cell::RefCell, fs::File, io::Read, path::Path, rc::Rc};
 
 pub type SharedMut<T> = Rc<RefCell<T>>;
 
@@ -11,3 +11,53 @@ pub fn read_file(path: &str) -> Vec<u8> {
     file.read_to_end(&mut data).unwrap();
     data
 }
+
+/// Replaces the extension in the given path with the provided extension.
+/// This function allows for simple associated file discovery.
+pub fn replace_ext(path: &str, new_extension: &str) -> Option<String> {
+    let file_path = Path::new(path);
+    let parent_dir = file_path.parent()?;
+    let file_stem = file_path.file_stem()?;
+    let file_extension = file_path.extension()?;
+    if file_stem == file_extension {
+        return None;
+    }
+    let new_file_name = format!("{}.{}", file_stem.to_str()?, new_extension);
+    let new_file_path = parent_dir.join(new_file_name);
+    Some(String::from(new_file_path.to_str()?))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_change_extension() {
+        let new_path = replace_ext("/path/to/file.txt", "dat").unwrap();
+        assert_eq!(
+            new_path,
+            Path::new("/path/to").join("file.dat").to_str().unwrap()
+        );
+
+        let new_path = replace_ext("/path/to/file.with.multiple.dots.txt", "dat").unwrap();
+        assert_eq!(
+            new_path,
+            Path::new("/path/to")
+                .join("file.with.multiple.dots.dat")
+                .to_str()
+                .unwrap()
+        );
+
+        let new_path = replace_ext("/path/to/file.without.extension", "dat").unwrap();
+        assert_eq!(
+            new_path,
+            Path::new("/path/to")
+                .join("file.without.dat")
+                .to_str()
+                .unwrap()
+        );
+
+        let new_path = replace_ext("/path/to/directory/", "dat");
+        assert_eq!(new_path, None);
+    }
+}
-- 
GitLab