Skip to content

Commit

Permalink
make save file path and save state path configurable (#132)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsgroth committed Sep 30, 2024
1 parent ea794bc commit d1fc2b1
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 72 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
/target

/.cargo/config.toml

/jgenesis-config.toml
/saves
/states
6 changes: 6 additions & 0 deletions common/jgenesis-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ pub mod frontend;
pub mod input;
pub mod num;
pub mod timeutils;

#[inline]
#[must_use]
pub fn is_appimage_build() -> bool {
option_env!("JGENESIS_APPIMAGE_BUILD").is_some_and(|var| !var.is_empty())
}
27 changes: 26 additions & 1 deletion frontend/jgenesis-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use gb_core::api::{GbAspectRatio, GbPalette, GbcColorCorrection};
use genesis_core::{GenesisAspectRatio, GenesisControllerType, GenesisRegion};
use jgenesis_common::frontend::{EmulatorTrait, TimingMode};
use jgenesis_native_config::AppConfig;
use jgenesis_native_config::common::ConfigSavePath;
use jgenesis_native_driver::config::input::{NesControllerType, SnesControllerType};
use jgenesis_native_driver::config::{GgAspectRatio, SmsAspectRatio};
use jgenesis_native_driver::input::MappableInputs;
Expand Down Expand Up @@ -76,6 +77,22 @@ struct Args {
#[arg(long)]
hide_cursor_over_window: Option<bool>,

/// Save file path (RomFolder / EmulatorFolder / Custom)
#[arg(long)]
save_path: Option<ConfigSavePath>,

/// Custom save file path (if save_path=Custom)
#[arg(long)]
custom_save_path: Option<PathBuf>,

/// Save state path (RomFolder / EmulatorFolder / Custom)
#[arg(long)]
state_path: Option<ConfigSavePath>,

/// Custom save state path (if state_path=Custom)
#[arg(long)]
custom_state_path: Option<PathBuf>,

/// MasterSystem model (Sms2 / Sms1)
#[arg(long, help_heading = SMSGG_OPTIONS_HEADING)]
sms_model: Option<SmsModel>,
Expand Down Expand Up @@ -428,7 +445,15 @@ impl Args {
config.nes.remove_sprite_limit = remove_sprite_limit;
}

apply_overrides!(self, config.common, [hide_cursor_over_window]);
apply_overrides!(self, config.common, [hide_cursor_over_window, save_path, state_path]);

if let Some(custom_save_path) = &self.custom_save_path {
config.common.custom_save_path.clone_from(custom_save_path);
}

if let Some(custom_state_path) = &self.custom_state_path {
config.common.custom_state_path.clone_from(custom_state_path);
}
}

fn apply_smsgg_overrides(&self, config: &mut AppConfig) {
Expand Down
84 changes: 57 additions & 27 deletions frontend/jgenesis-gui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod romlist;
mod smsgg;
mod snes;

use crate::app::common::SavePathSelect;
use crate::app::input::{GenericButton, InputAppConfigExt};
use crate::app::nes::OverscanState;
use crate::app::romlist::{Console, RomListThreadHandle, RomMetadata};
Expand All @@ -16,7 +17,7 @@ use eframe::{Frame, Theme};
use egui::ahash::HashMap;
use egui::panel::TopBottomSide;
use egui::{
Align, Button, CentralPanel, Color32, Context, Key, KeyboardShortcut, Layout, Modifiers,
Align, Button, CentralPanel, Color32, Context, Grid, Key, KeyboardShortcut, Layout, Modifiers,
Response, Style, TextEdit, TopBottomPanel, Ui, Vec2, ViewportCommand, Visuals, Widget, Window,
menu,
};
Expand Down Expand Up @@ -87,6 +88,7 @@ enum OpenWindow {
NesGeneral,
SnesGeneral,
GameBoyGeneral,
Paths,
Interface,
CommonVideo,
SmsGgVideo,
Expand Down Expand Up @@ -374,6 +376,54 @@ impl App {
self.state.rom_list_refresh_needed = true;
}

fn render_path_settings(&mut self, ctx: &Context) {
let mut open = true;
Window::new("Path Settings").open(&mut open).resizable(false).show(ctx, |ui| {
ui.add(SavePathSelect::new(
"Game save file path",
&mut self.config.common.save_path,
&mut self.config.common.custom_save_path,
));

ui.add(SavePathSelect::new(
"Save state path",
&mut self.config.common.state_path,
&mut self.config.common.custom_state_path,
));

ui.add_space(10.0);

ui.group(|ui| {
ui.heading("ROM search directories");

ui.add_space(5.0);

Grid::new("rom_search_dirs").show(ui, |ui| {
for (i, rom_search_dir) in
self.config.rom_search_dirs.clone().into_iter().enumerate()
{
ui.label(&rom_search_dir);

if ui.button("Remove").clicked() {
self.config.rom_search_dirs.remove(i);
self.rom_list_thread.request_scan(self.config.rom_search_dirs.clone());
self.state.rom_list_refresh_needed = true;
}

ui.end_row();
}
});

if ui.button("Add").clicked() {
self.add_rom_search_directory();
}
});
});
if !open {
self.state.open_windows.remove(&OpenWindow::Paths);
}
}

fn render_interface_settings(&mut self, ctx: &Context) {
let mut open = true;
Window::new("UI Settings").open(&mut open).resizable(false).show(ctx, |ui| {
Expand All @@ -397,32 +447,6 @@ impl App {
ui.radio_value(&mut self.config.egui_theme, EguiTheme::Light, "Light");
});
});

ui.add_space(5.0);

ui.group(|ui| {
ui.label("ROM search directories");

ui.add_space(5.0);

for (i, rom_search_dir) in
self.config.rom_search_dirs.clone().into_iter().enumerate()
{
ui.horizontal(|ui| {
ui.label(&rom_search_dir);

if ui.button("Remove").clicked() {
self.config.rom_search_dirs.remove(i);
self.rom_list_thread.request_scan(self.config.rom_search_dirs.clone());
self.state.rom_list_refresh_needed = true;
}
});
}

if ui.button("Add").clicked() {
self.add_rom_search_directory();
}
});
});
if !open {
self.state.open_windows.remove(&OpenWindow::Interface);
Expand Down Expand Up @@ -670,6 +694,11 @@ impl App {
ui.close_menu();
}

if ui.button("Paths").clicked() {
self.state.open_windows.insert(OpenWindow::Paths);
ui.close_menu();
}

if ui.button("Interface").clicked() {
self.state.open_windows.insert(OpenWindow::Interface);
ui.close_menu();
Expand Down Expand Up @@ -1096,6 +1125,7 @@ impl eframe::App for App {
OpenWindow::NesGeneral => self.render_nes_general_settings(ctx),
OpenWindow::SnesGeneral => self.render_snes_general_settings(ctx),
OpenWindow::GameBoyGeneral => self.render_gb_general_settings(ctx),
OpenWindow::Paths => self.render_path_settings(ctx),
OpenWindow::Interface => self.render_interface_settings(ctx),
OpenWindow::CommonVideo => self.render_common_video_settings(ctx),
OpenWindow::SmsGgVideo => self.render_smsgg_video_settings(ctx),
Expand Down
49 changes: 48 additions & 1 deletion frontend/jgenesis-gui/src/app/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ mod helptext;

use crate::app::{App, NumericTextEdit, OpenWindow};
use eframe::epaint::Color32;
use egui::{Context, Slider, Window};
use egui::{Context, Response, Slider, Ui, Widget, Window};
use jgenesis_native_config::common::ConfigSavePath;
use jgenesis_renderer::config::{FilterMode, PreprocessShader, Scanlines, VSyncMode, WgpuBackend};
use rfd::FileDialog;
use std::num::NonZeroU32;
use std::path::PathBuf;

impl App {
pub(super) fn render_common_video_settings(&mut self, ctx: &Context) {
Expand Down Expand Up @@ -307,3 +310,47 @@ impl App {
}
}
}

pub struct SavePathSelect<'a> {
label: &'a str,
save_path: &'a mut ConfigSavePath,
custom_path: &'a mut PathBuf,
}

impl<'a> SavePathSelect<'a> {
pub fn new(
label: &'a str,
save_path: &'a mut ConfigSavePath,
custom_path: &'a mut PathBuf,
) -> Self {
Self { label, save_path, custom_path }
}
}

impl<'a> Widget for SavePathSelect<'a> {
fn ui(self, ui: &mut Ui) -> Response {
ui.group(|ui| {
ui.label(self.label);

ui.horizontal(|ui| {
ui.radio_value(self.save_path, ConfigSavePath::RomFolder, "Same folder as ROM");
ui.radio_value(self.save_path, ConfigSavePath::EmulatorFolder, "Emulator folder");
ui.radio_value(self.save_path, ConfigSavePath::Custom, "Custom");
});

ui.add_enabled_ui(*self.save_path == ConfigSavePath::Custom, |ui| {
ui.horizontal(|ui| {
ui.label("Custom path:");

let button_label = self.custom_path.to_string_lossy();
if ui.button(button_label).clicked() {
if let Some(path) = FileDialog::new().pick_folder() {
*self.custom_path = path;
}
}
});
});
})
.response
}
}
4 changes: 1 addition & 3 deletions frontend/jgenesis-native-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ jgenesis-proc-macros = { path = "../../common/jgenesis-proc-macros" }
jgenesis-renderer = { path = "../jgenesis-renderer" }

cfg-if = { workspace = true }
directories = { workspace = true }
log = { workspace = true }
serde = { workspace = true }
toml = { workspace = true }

[target.'cfg(target_os = "linux")'.dependencies]
directories = { workspace = true }

[lints]
workspace = true
49 changes: 48 additions & 1 deletion frontend/jgenesis-native-config/src/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
use crate::AppConfig;
use jgenesis_native_driver::config::{CommonConfig, WindowSize};
use jgenesis_native_driver::config::{CommonConfig, SavePath, WindowSize};
use jgenesis_proc_macros::{EnumDisplay, EnumFromStr};
use jgenesis_renderer::config::{
FilterMode, PreprocessShader, PrescaleFactor, PrescaleMode, RendererConfig, Scanlines,
VSyncMode, WgpuBackend,
};
use serde::{Deserialize, Serialize};
use std::num::NonZeroU32;
use std::path::{Path, PathBuf};

#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, EnumDisplay, EnumFromStr,
)]
pub enum ConfigSavePath {
#[default]
RomFolder,
EmulatorFolder,
Custom,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CommonAppConfig {
Expand All @@ -19,6 +31,14 @@ pub struct CommonAppConfig {
pub audio_sync_threshold: u32,
#[serde(default)]
pub audio_gain_db: f64,
#[serde(default)]
pub save_path: ConfigSavePath,
#[serde(default = "default_custom_save_path")]
pub custom_save_path: PathBuf,
#[serde(default)]
pub state_path: ConfigSavePath,
#[serde(default = "default_custom_state_path")]
pub custom_state_path: PathBuf,
pub window_width: Option<u32>,
pub window_height: Option<u32>,
#[serde(default)]
Expand Down Expand Up @@ -79,6 +99,23 @@ fn default_audio_sync_threshold() -> u32 {
8192
}

fn default_custom_path(subdir: &str) -> PathBuf {
let Some(base_dirs) = directories::BaseDirs::new() else {
log::error!("Unable to determine user base directories for default custom paths");
return PathBuf::default();
};

base_dirs.data_local_dir().join("jgenesis").join(subdir)
}

fn default_custom_save_path() -> PathBuf {
default_custom_path(SavePath::SAVE_SUBDIR)
}

fn default_custom_state_path() -> PathBuf {
default_custom_path(SavePath::STATE_SUBDIR)
}

fn default_prescale_factor() -> PrescaleFactor {
PrescaleFactor::from(NonZeroU32::new(3).unwrap())
}
Expand All @@ -105,6 +142,8 @@ impl AppConfig {
internal_audio_buffer_size: self.common.internal_audio_buffer_size,
audio_sync_threshold: self.common.audio_sync_threshold,
audio_gain_db: self.common.audio_gain_db,
save_path: save_path(self.common.save_path, &self.common.custom_save_path),
state_path: save_path(self.common.state_path, &self.common.custom_state_path),
window_size: self.common.window_size(),
renderer_config: RendererConfig {
wgpu_backend: self.common.wgpu_backend,
Expand All @@ -131,3 +170,11 @@ impl AppConfig {
}
}
}

fn save_path(path: ConfigSavePath, custom_path: &Path) -> SavePath {
match path {
ConfigSavePath::RomFolder => SavePath::RomFolder,
ConfigSavePath::EmulatorFolder => SavePath::EmulatorFolder,
ConfigSavePath::Custom => SavePath::Custom(custom_path.into()),
}
}
2 changes: 1 addition & 1 deletion frontend/jgenesis-native-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub fn default_config_path() -> PathBuf {

#[cfg(target_os = "linux")]
fn default_linux_config_path() -> PathBuf {
if option_env!("JGENESIS_APPIMAGE_BUILD").is_none() {
if !jgenesis_common::is_appimage_build() {
return CONFIG_FILENAME.into();
}

Expand Down
Loading

0 comments on commit d1fc2b1

Please sign in to comment.