Skip to content
2 changes: 2 additions & 0 deletions src/BizHawk.Client.Common/FilesystemFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public FilesystemFilter(

public static readonly FilesystemFilter PNGs = new FilesystemFilter("PNG Files", new[] { "png" });

public static readonly FilesystemFilter SaveRams = new FilesystemFilter("SaveRAM Files", new[] { "SaveRAM", "bin" });

public static readonly FilesystemFilter TAStudioProjects = new FilesystemFilter("TAS Project Files", new[] { MovieService.TasMovieExtension });

public static readonly FilesystemFilter TextFiles = new FilesystemFilter("Text Files", new[] { "txt" });
Expand Down
7 changes: 5 additions & 2 deletions src/BizHawk.Client.Common/LoadRomArgs.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#nullable enable

namespace BizHawk.Client.Common
{
public sealed record class LoadRomArgs(
IOpenAdvanced OpenAdvanced,
string/*?*/ ForcedSysID = null,
bool? Deterministic = null);
string? ForcedSysID = null,
bool? Deterministic = null,
string? SaveRamPath = null);
}
6 changes: 4 additions & 2 deletions src/BizHawk.Client.Common/config/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,14 @@ public void SetWindowScaleFor(string sysID, int windowScale)
/// <summary>
/// Whether to make AutoSave files at periodic intervals
/// </summary>
public bool AutosaveSaveRAM { get; set; }
public bool AutosaveSaveRAM { get; set; } = true;

/// <summary>
/// Intervals at which to make AutoSave files
/// </summary>
public int FlushSaveRamFrames { get; set; }
public int FlushSaveRamFrames { get; set; } = 5 * 60 * 60; // 5 minutes (it assumes 60 fps)

public bool WarnLoadSramReboots { get; set; } = true;

public bool TurboSeek { get; set; }

Expand Down
20 changes: 9 additions & 11 deletions src/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,20 +230,24 @@ public static string RomAbsolutePath(this PathEntryCollection collection, string
return collection.AbsolutePathFor(path.Path, systemId);
}

public static string SaveRamAbsolutePath(this PathEntryCollection collection, IGameInfo game, IMovie movie)
public static string SaveRamAbsolutePath(this PathEntryCollection collection, IGameInfo game)
{
var name = game.FilesystemSafeName();
if (movie.IsActive())
{
name += $".{Path.GetFileNameWithoutExtension(movie.Filename)}";
}

var pathEntry = collection[game.System, "Save RAM"]
?? collection[game.System, "Base"];

return $"{Path.Combine(collection.AbsolutePathFor(pathEntry.Path, game.System), name)}.SaveRAM";
}

public static string SaveRamAbsolutePath(this PathEntryCollection collection, string system)
{
var pathEntry = collection[system, "Save RAM"]
?? collection[system, "Base"];

return collection.AbsolutePathFor(pathEntry.Path, system);
}

// Shenanigans
public static string RetroSaveRamAbsolutePath(this PathEntryCollection collection, string coreName)
{
Expand All @@ -260,12 +264,6 @@ public static string RetroSystemAbsolutePath(this PathEntryCollection collection
return Path.Combine(collection.AbsolutePathFor(pathEntry.Path, VSystemID.Raw.Libretro), coreName);
}

public static string AutoSaveRamAbsolutePath(this PathEntryCollection collection, IGameInfo game, IMovie movie)
{
var path = collection.SaveRamAbsolutePath(game, movie);
return path.Insert(path.Length - 8, ".AutoSaveRAM");
}

public static string CheatsAbsolutePath(this PathEntryCollection collection, string systemId)
{
var pathEntry = collection[systemId, "Cheats"]
Expand Down
7 changes: 6 additions & 1 deletion src/BizHawk.Client.EmuHawk/CustomControls/MsgBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ internal partial class MsgBox : Form
// The min required width of the button and checkbox row. Sum of button widths + checkbox width + margins.
private int _minButtonRowWidth;

public bool UserSaysDontShowAgain => chkBx.Checked;

/// <summary>
/// Create a new instance of the dialog box with a message and title and a standard windows MessageBox icon.
/// </summary>
/// <param name="message">Message text.</param>
/// <param name="title">Dialog Box title.</param>
/// <param name="boxIcon">Standard system MessageBox icon.</param>
public MsgBox(string message, string title, MessageBoxIcon boxIcon)
/// <param name="showCheckbox">Show a checkbox letting the user say "don't show this again".</param>
public MsgBox(string message, string title, MessageBoxIcon boxIcon, bool showCheckbox = false)
{
var icon = GetMessageBoxIcon(boxIcon);
InitializeComponent();
Expand All @@ -39,6 +42,8 @@ public MsgBox(string message, string title, MessageBoxIcon boxIcon)
{
messageLbl.Location = new Point(FormXMargin, FormYMargin);
}

chkBx.Visible = showCheckbox;
}

/// <summary>
Expand Down
18 changes: 17 additions & 1 deletion src/BizHawk.Client.EmuHawk/MainForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 65 additions & 1 deletion src/BizHawk.Client.EmuHawk/MainForm.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,70 @@ private void FlushSaveRAMMenuItem_Click(object sender, EventArgs e)
ShowMessageIfError(() => FlushSaveRAM(), "Failed to flush saveram!");
}

private void SaveSramAsMenuItem_Click(object sender, EventArgs e)
{
string sramFolderPath = Config.PathEntries.SaveRamAbsolutePath(Game.System);

// Create folder if it doesn't already exist
try
{
Directory.CreateDirectory(sramFolderPath);
}
catch (IOException) { /* ignored */ }
catch (UnauthorizedAccessException) { /* ignored */ }

FilesystemFilterSet filterset = new(FilesystemFilter.SaveRams);
var shouldSaveResult = this.ShowFileSaveDialog(
initDir: sramFolderPath,
discardCWDChange: true,
fileExt: $".{filterset.Filters[0].Extensions.First()}",
filter: filterset);
if (shouldSaveResult is not null)
{
string normalPath = CurrentlyOpenRomArgs.SaveRamPath ?? Config.PathEntries.SaveRamAbsolutePath(Game);
string oldAutoPath = MakeSaveRamAutosavePath(normalPath);
CurrentlyOpenRomArgs = CurrentlyOpenRomArgs with { SaveRamPath = shouldSaveResult };
FlushSaveRAMMenuItem_Click(sender, e);
try { File.Delete(oldAutoPath); } catch { /* nothing */ }
}
}

private void LoadSramMenuItem_Click(object sender, EventArgs e)
{
if (Config.WarnLoadSramReboots)
{
MsgBox box = new(
message: "Loading Save RAM requires a core reboot. Proceed?",
title: "Reboot?",
boxIcon: MessageBoxIcon.Warning,
showCheckbox: true);
box.SetButtons([ "Yes", "No" ], [ DialogResult.Yes, DialogResult.Cancel ]);
DialogResult result = box.ShowDialog();
if (box.UserSaysDontShowAgain) Config.WarnLoadSramReboots = false;
if (result == DialogResult.Cancel) return;
}

// get the file
string sramFolderPath = Config.PathEntries.SaveRamAbsolutePath(Game.System);
// Create folder if it doesn't already exist
try
{
Directory.CreateDirectory(sramFolderPath);
}
catch (IOException) { /* ignored */ }
catch (UnauthorizedAccessException) { /* ignored */ }

FilesystemFilterSet filterset = new(FilesystemFilter.SaveRams);
string fileToLoad = this.ShowFileOpenDialog(
initDir: sramFolderPath,
discardCWDChange: true,
filter: filterset);
if (fileToLoad == null) return;

// reboot
RebootCore(fileToLoad);
}

private void ReadonlyMenuItem_Click(object sender, EventArgs e)
{
ToggleReadOnly();
Expand Down Expand Up @@ -1270,7 +1334,7 @@ private void MainFormContextMenu_Opening(object sender, System.ComponentModel.Ca

ConfigContextMenuItem.Visible = _inFullscreen;

ClearSRAMContextMenuItem.Visible = File.Exists(Config.PathEntries.SaveRamAbsolutePath(Game, MovieSession.Movie));
ClearSRAMContextMenuItem.Visible = File.Exists(Config.PathEntries.SaveRamAbsolutePath(Game));

ContextSeparator_AfterROM.Visible = OpenRomContextMenuItem.Visible || LoadLastRomContextMenuItem.Visible;

Expand Down
3 changes: 3 additions & 0 deletions src/BizHawk.Client.EmuHawk/MainForm.Movie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace BizHawk.Client.EmuHawk
{
public partial class MainForm
{
private bool _hadMovie = false;

public bool StartNewMovie(IMovie movie, bool newMovie)
{
if (movie is null) throw new ArgumentNullException(paramName: nameof(movie));
Expand Down Expand Up @@ -63,6 +65,7 @@ public bool StartNewMovie(IMovie movie, bool newMovie)
Config.RecentMovies.Add(movie.Filename);

MovieSession.RunQueuedMovie(newMovie, Emulator);
_hadMovie = true;
if (newMovie)
{
PopulateWithDefaultHeaderValues(movie);
Expand Down
Loading
Loading