Update the ISaveRam interface for clarity. We no longer constrain the sram data length, because it already wasn't true for all cores. Cores that require a specific size should throw if they get the wrong size.

This commit is contained in:
SuuperW 2025-07-16 22:13:45 -05:00
parent 0f07e102a8
commit bb80c1f013
42 changed files with 178 additions and 77 deletions

View File

@ -133,7 +133,6 @@ namespace BizHawk.Client.Common
foreach (var (k, v) in old.HeaderEntries) tas.HeaderEntries[k] = v;
tas.StartsFromSaveRam = true;
tas.SyncSettingsJson = old.SyncSettingsJson;
foreach (string comment in old.Comments)

View File

@ -49,26 +49,7 @@ namespace BizHawk.Client.Common
}
}
public bool StartsFromSaveRam
{
// ReSharper disable SimplifyConditionalTernaryExpression
get => Header.TryGetValue(HeaderKeys.StartsFromSaveram, out var s) ? bool.Parse(s) : false;
// ReSharper restore SimplifyConditionalTernaryExpression
set
{
if (value)
{
if (!Header.ContainsKey(HeaderKeys.StartsFromSaveram))
{
Header.Add(HeaderKeys.StartsFromSaveram, "True");
}
}
else
{
Header.Remove(HeaderKeys.StartsFromSaveram);
}
}
}
public bool StartsFromSaveRam => SaveRam != null;
public override string GameName
{

View File

@ -65,7 +65,7 @@ namespace BizHawk.Client.Common
byte[] SaveRam { get; set; }
bool StartsFromSavestate { get; set; }
bool StartsFromSaveRam { get; set; }
bool StartsFromSaveRam { get; }
string LogKey { get; set; }

View File

@ -26,12 +26,8 @@ using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores;
using BizHawk.Emulation.Cores.Computers.AppleII;
using BizHawk.Emulation.Cores.Computers.Commodore64;
using BizHawk.Emulation.Cores.Computers.DOS;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
using BizHawk.Emulation.Cores.Consoles.SNK;
using BizHawk.Emulation.Cores.Nintendo.GBA;
using BizHawk.Emulation.Cores.Nintendo.NES;
using BizHawk.Emulation.Cores.Nintendo.SNES;
@ -2057,36 +2053,24 @@ namespace BizHawk.Client.EmuHawk
return;
}
byte[] sram = null;
try
{
byte[] sram;
// some cores might not know how big the saveram ought to be, so just send it the whole file
if (Emulator is AppleII or C64 or DOSBox or MGBAHawk or NeoGeoPort or NES { BoardName: "FDS" })
{
sram = File.ReadAllBytes(saveramToLoad.FullName);
}
else
{
var oldRam = Emulator.AsSaveRam().CloneSaveRam();
if (oldRam is null)
{
// we have a SaveRAM file, but the current core does not have save ram.
// just skip loading the saveram file in that case
return;
}
// why do we silently truncate\pad here instead of warning\erroring?
sram = new byte[oldRam.Length];
using var fs = saveramToLoad.OpenRead();
_ = fs.Read(sram, 0, sram.Length);
}
Emulator.AsSaveRam().StoreSaveRam(sram);
sram = File.ReadAllBytes(saveramToLoad.FullName);
}
catch (IOException e)
catch (Exception e)
{
AddOnScreenMessage("An error occurred while loading Sram");
AddOnScreenMessage("An IO error occurred while loading Sram");
Console.Error.WriteLine(e);
}
try
{
if (sram != null) Emulator.AsSaveRam().StoreSaveRam(sram);
}
catch (Exception e)
{
AddOnScreenMessage("The core threw an error while loading Sram");
Console.Error.WriteLine(e);
}
}
@ -2112,9 +2096,7 @@ namespace BizHawk.Client.EmuHawk
var backupPath = $"{path}.bak";
var backupFile = new FileInfo(backupPath);
var saveram = Emulator.AsSaveRam().CloneSaveRam();
if (saveram == null)
return true;
var saveram = Emulator.AsSaveRam().CloneSaveRam()!;
try
{

View File

@ -102,7 +102,7 @@ namespace BizHawk.Client.EmuHawk
MaxDropDownItems = 32,
Size = new(152, 21),
};
if (_emulator.HasSaveRam() && _emulator.AsSaveRam().CloneSaveRam(clearDirty: false) is not null) StartFromCombo.Items.Add(START_FROM_SAVERAM);
if (_emulator.HasSaveRam()) StartFromCombo.Items.Add(START_FROM_SAVERAM);
if (_emulator.HasSavestates()) StartFromCombo.Items.Add(START_FROM_SAVESTATE);
DefaultAuthorCheckBox = new()
@ -242,7 +242,6 @@ namespace BizHawk.Client.EmuHawk
else if (selectedStartFromValue is START_FROM_SAVERAM && _emulator.HasSaveRam())
{
var core = _emulator.AsSaveRam();
movieToRecord.StartsFromSaveRam = true;
movieToRecord.SaveRam = core.CloneSaveRam(clearDirty: false);
}

View File

@ -36,7 +36,7 @@ namespace BizHawk.Client.EmuHawk
NewFromCurrentSaveRamMenuItem.Enabled =
CurrentTasMovie.InputLogLength > 0
&& SaveRamEmulator != null;
&& SaveRamEmulator?.SupportsSaveRam == true;
}
private void StartNewProjectFromNowMenuItem_Click(object sender, EventArgs e)
@ -59,7 +59,7 @@ namespace BizHawk.Client.EmuHawk
{
if (AskSaveChanges())
{
var saveRam = SaveRamEmulator?.CloneSaveRam(clearDirty: false) ?? throw new Exception("No SaveRam");
var saveRam = SaveRamEmulator?.CloneSaveRam(clearDirty: false) ?? throw new Exception("No SaveRam; this button should have been disabled.");
GoToFrame(TasView.AnyRowsSelected ? TasView.FirstSelectedRowIndex : 0);
var result = CurrentTasMovie.ConvertToSaveRamAnchoredMovie(saveRam);
DisplayMessageIfFailed(() => result, "Failed to create movie.");
@ -1341,7 +1341,7 @@ namespace BizHawk.Client.EmuHawk
StartANewProjectFromSaveRamMenuItem.Visible =
selectionIsSingleRow
&& SaveRamEmulator != null
&& SaveRamEmulator?.SupportsSaveRam == true
&& !CurrentTasMovie.StartsFromSavestate;
StartFromNowSeparator.Visible = StartNewProjectFromNowMenuItem.Visible || StartANewProjectFromSaveRamMenuItem.Visible;

View File

@ -55,13 +55,17 @@ namespace BizHawk.Emulation.Common
int pos = 0;
for (int i = 0; i < _numCores; i++)
{
var toCopy = _linkedCores[i].AsSaveRam().CloneSaveRam(); // wait CloneSaveRam is already a copy, why are we copying it again
if (toCopy is null) continue;
var b = new byte[toCopy.Length];
var numberBytesToCopy = _linkedCores[i].AsSaveRam().CloneSaveRam()?.Length;
if (numberBytesToCopy is null) continue;
var b = new byte[numberBytesToCopy.Value];
Buffer.BlockCopy(data, pos, b, 0, b.Length);
pos += b.Length;
_linkedCores[i].AsSaveRam().StoreSaveRam(b);
}
if (data.Length != pos) throw new InvalidOperationException("Incorrect sram size.");
}
public bool SupportsSaveRam => true;
}
}

View File

@ -134,7 +134,7 @@ namespace BizHawk.Emulation.Common
public static bool HasSaveRam(this IEmulator core)
{
return core != null && core.ServiceProvider.HasService<ISaveRam>();
return core != null && core.ServiceProvider.HasService<ISaveRam>() && core.AsSaveRam()!.SupportsSaveRam;
}
public static ISaveRam AsSaveRam(this IEmulator core)

View File

@ -10,16 +10,16 @@
{
/// <summary>
/// Returns a copy of the SaveRAM. Editing it won't do you any good unless you later call StoreSaveRam()
/// This IS allowed to return null.
/// Unfortunately, the core may think differently of a nonexisting (null) saveram vs a 0 size saveram.
/// Frontend users of the ISaveRam should treat null as nonexisting (and thus not even write the file, so that the "does not exist" condition can be roundtripped and not confused with an empty file)
/// This method must return null if and only if <see cref="SupportsSaveRam"/> is false.
/// </summary>
/// <param name="clearDirty">Whether the saveram should be considered in a clean state after this call for purposes of <see cref="SaveRamModified"/></param>
byte[]? CloneSaveRam(bool clearDirty = true);
/// <summary>
/// store new SaveRAM to the emu core. the data should be the same size as the return from ReadSaveRam()
/// Store new SaveRAM to the emu core.
/// The core must ignore calls to this method if <see cref="SupportsSaveRam"/> is false.
/// </summary>
/// <exception cref="Exception">The core may throw an exception if the given data is invalid.</exception>
void StoreSaveRam(byte[] data);
/// <summary>
@ -28,5 +28,10 @@
/// This value should be considered a hint more than an absolute truth.
/// </summary>
bool SaveRamModified { get; }
/// <summary>
/// Certain cores may support SaveRam only in certain situations, for example only for certain games.
/// </summary>
bool SupportsSaveRam { get; }
}
}

View File

@ -18,6 +18,8 @@ namespace BizHawk.Emulation.Cores.Arcades.MAME
public bool SaveRamModified => _nvramFilenames.Count > 0;
public bool SupportsSaveRam => _nvramFilenames.Count != 0;
public byte[] CloneSaveRam(bool clearDirty)
{
if (_nvramFilenames.Count == 0)

View File

@ -15,6 +15,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
}
public bool SaveRamModified => true;
public bool SupportsSaveRam => true;
public byte[] CloneSaveRam(bool clearDirty)
{

View File

@ -302,11 +302,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge
var deltaBSize = reader.ReadInt32();
_deltaB = reader.ReadBytes(deltaBSize);
if (reader.BaseStream.Position != deltaASize + deltaBSize + 8) throw new InvalidOperationException("Incorrect sram size.");
DeltaSerializer.ApplyDelta(_originalMediaA, _chipA.Data, _deltaA);
DeltaSerializer.ApplyDelta(_originalMediaB, _chipB.Data, _deltaB);
_saveRamDirty = false;
}
public bool SaveRamModified => _saveRamDirty;
public bool SupportsSaveRam => true;
}
}

View File

@ -39,6 +39,8 @@ public sealed partial class Drive1541 : ISaveRam
public bool SaveRamModified { get; private set; } = false;
public bool SupportsSaveRam => true;
public byte[] CloneSaveRam(bool clearDirty)
{
SaveDeltas();

View File

@ -13,12 +13,15 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
{
if (SaveRAM != null)
{
if (data.Length != SaveRAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Array.Copy(data, SaveRAM, data.Length);
}
}
public bool SaveRamModified { get; private set; }
public bool SupportsSaveRam => SaveRAM != null;
public byte[] SaveRAM;
private byte SaveRamBank;
}

View File

@ -11,9 +11,12 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
public void StoreSaveRam(byte[] data)
{
if (data.Length != _hsram.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, _hsram, 0, data.Length);
}
public bool SaveRamModified => (_hsbios != null);
public bool SupportsSaveRam => true;
}
}

View File

@ -8,6 +8,8 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public new bool SaveRamModified => _saveRamSize > 0 && _core.SaveRamIsDirty();
public new bool SupportsSaveRam => _saveRamSize != 0;
public new byte[] CloneSaveRam(bool clearDirty)
{
if (_saveRamSize == 0)

View File

@ -22,7 +22,7 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx
{
if (!LibLynx.GetSaveRamPtr(Core, out var size, out var data))
{
throw new InvalidOperationException();
return;
}
if (srcData.Length != size) throw new ArgumentException(message: "buffer too small", paramName: nameof(srcData));
@ -31,5 +31,7 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx
}
public bool SaveRamModified => LibLynx.GetSaveRamPtr(Core, out int unused, out IntPtr unused2);
public bool SupportsSaveRam => LibLynx.GetSaveRamPtr(Core, out int _, out IntPtr _);
}
}

View File

@ -15,5 +15,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Vectrex
}
public bool SaveRamModified => false;
public bool SupportsSaveRam => false;
}
}

View File

@ -11,13 +11,16 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public void StoreSaveRam(byte[] data)
{
if (_syncSettings.Use_SRAM)
if (cart_RAM != null && _syncSettings.Use_SRAM)
{
if (data.Length != cart_RAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, cart_RAM, 0, data.Length);
Console.WriteLine("loading SRAM here");
}
}
public bool SaveRamModified => has_bat & _syncSettings.Use_SRAM;
public bool SupportsSaveRam => cart_RAM != null; // The Use_SRAM setting implements behavior not officially supported by BizHawk.
}
}

View File

@ -11,6 +11,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
public bool SaveRamModified => _saveRamSize != 0;
public bool SupportsSaveRam => _saveRamSize != 0;
public byte[] CloneSaveRam(bool clearDirty)
{
if (_saveRamSize == 0) return null;

View File

@ -33,6 +33,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
public void StoreSaveRam(byte[] data)
{
if (!SupportsSaveRam) return;
if (data.AsSpan().Slice(0, 8).SequenceEqual(_legacyHeader))
{
data = LegacyFix(data);
@ -43,6 +45,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
public bool SaveRamModified => LibmGBA.BizGetSaveRam(Core, _saveScratch, _saveScratch.Length) > 0;
public bool SupportsSaveRam
{
get
{
// Is all of this necessary? Someone who knows how the core works might know.
int len = LibmGBA.BizGetSaveRam(Core, _saveScratch, _saveScratch.Length);
len = TruncateRTCIfUsingDeterministicTime(len);
return len != 0;
}
}
private static byte[] LegacyFix(byte[] saveram)
{
// at one point vbanext-hawk had a special saveram format which we want to load.

View File

@ -11,13 +11,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public void StoreSaveRam(byte[] data)
{
if (_syncSettings.Use_SRAM)
if (cart_RAM != null && _syncSettings.Use_SRAM)
{
if (data.Length != cart_RAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, cart_RAM, 0, data.Length);
Console.WriteLine("loading SRAM here");
}
}
public bool SaveRamModified => has_bat & _syncSettings.Use_SRAM;
public bool SupportsSaveRam => cart_RAM != null; // The Use_SRAM setting implements behavior not officially supported by BizHawk.
}
}

View File

@ -54,14 +54,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink
{
if (L.cart_RAM != null && R.cart_RAM == null)
{
if (data.Length != L.cart_RAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, L.cart_RAM, 0, L.cart_RAM.Length);
}
else if (R.cart_RAM != null && L.cart_RAM == null)
{
if (data.Length != R.cart_RAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, R.cart_RAM, 0, R.cart_RAM.Length);
}
else if (R.cart_RAM != null && L.cart_RAM != null)
{
if (data.Length != L.cart_RAM.Length + R.cart_RAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, L.cart_RAM, 0, L.cart_RAM.Length);
Buffer.BlockCopy(data, L.cart_RAM.Length, R.cart_RAM, 0, R.cart_RAM.Length);
}
@ -71,5 +74,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink
}
public bool SaveRamModified => (L.has_bat || R.has_bat) & linkSyncSettings.Use_SRAM;
public bool SupportsSaveRam => L.cart_RAM != null || R.cart_RAM != null; // The Use_SRAM setting implements behavior not officially supported by BizHawk.
}
}

View File

@ -84,12 +84,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x
if (R.cart_RAM != null)
{
Buffer.BlockCopy(data, temp, R.cart_RAM, 0, R.cart_RAM.Length);
temp += R.cart_RAM.Length;
}
if (data.Length != temp) throw new InvalidOperationException("Incorrect sram size.");
Console.WriteLine("loading SRAM here");
}
}
public bool SaveRamModified => (L.has_bat || C.has_bat || R.has_bat) & Link3xSyncSettings.Use_SRAM;
public bool SupportsSaveRam => L.cart_RAM != null || C.cart_RAM != null || R.cart_RAM != null; // The Use_SRAM setting implements behavior not officially supported by BizHawk.
}
}

View File

@ -105,12 +105,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
if (D.cart_RAM != null)
{
Buffer.BlockCopy(data, temp, D.cart_RAM, 0, D.cart_RAM.Length);
temp += D.cart_RAM.Length;
}
if (data.Length != temp) throw new InvalidOperationException("Incorrect sram size.");
Console.WriteLine("loading SRAM here");
}
}
public bool SaveRamModified => (A.has_bat || B.has_bat || C.has_bat || D.has_bat) & Link4xSyncSettings.Use_SRAM;
public bool SupportsSaveRam => A.cart_RAM != null || B.cart_RAM != null || C.cart_RAM != null || D.cart_RAM != null; // The Use_SRAM setting implements behavior not officially supported by BizHawk.
}
}

View File

@ -7,6 +7,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
// need to wire more stuff into the core to actually know this
public bool SaveRamModified => LibGambatte.gambatte_getsavedatalength(GambatteState) != 0;
public bool SupportsSaveRam => LibGambatte.gambatte_getsavedatalength(GambatteState) != 0;
public byte[] CloneSaveRam(bool clearDirty)
{
var length = LibGambatte.gambatte_getsavedatalength(GambatteState);
@ -24,6 +26,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public void StoreSaveRam(byte[] data)
{
var expected = LibGambatte.gambatte_getsavedatalength(GambatteState);
if (expected == 0) return;
if (data.Length != expected) throw new ArgumentException(message: "Size of saveram data does not match expected!", paramName: nameof(data));
LibGambatte.gambatte_loadsavedata(GambatteState, data);

View File

@ -15,5 +15,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
public bool SaveRamModified => true;
public bool SupportsSaveRam => true;
}
}

View File

@ -9,6 +9,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public new bool SaveRamModified => IsDSiWare ? DSiWareSaveLength != 0 : _core.SaveRamIsDirty();
public new bool SupportsSaveRam => IsDSiWare ? DSiWareSaveLength != 0 : _core.GetSaveRamLength(_console) != 0;
public new byte[] CloneSaveRam(bool clearDirty)
{
if (IsDSiWare)
@ -54,6 +56,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
if (IsDSiWare)
{
if (DSiWareSaveLength == 0) return;
if (data.Length == DSiWareSaveLength)
{
if (PublicSavSize > 0) _exe.AddReadonlyFile(data.AsSpan().Slice(0, PublicSavSize).ToArray(), "public.sav");
@ -66,6 +69,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
if (PrivateSavSize > 0) _exe.RemoveReadonlyFile("private.sav");
if (BannerSavSize > 0) _exe.RemoveReadonlyFile("banner.sav");
}
else
{
throw new InvalidOperationException("Incorrect sram size.");
}
}
else if (data.Length > 0)
{

View File

@ -15,6 +15,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
}
public bool SupportsSaveRam
{
get
{
if (Board == null) return false;
if (Board is FDS) return true;
if (Board.SaveRam == null) return false;
return true;
}
}
public byte[] CloneSaveRam(bool clearDirty)
{
if (Board is FDS fds)
@ -38,6 +49,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
return;
}
if (data.Length != Board.SaveRam.Length) throw new InvalidOperationException("Incorrect sram size.");
Array.Copy(data, Board.SaveRam, data.Length);
}
}

View File

@ -21,6 +21,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
public bool SaveRamModified => QN.qn_has_battery_ram(Context);
public bool SupportsSaveRam => QN.qn_has_battery_ram(Context);
private byte[] _saveRamBuff;
private void InitSaveRamBuff()

View File

@ -11,6 +11,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != 0
|| Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM) != 0;
public bool SupportsSaveRam =>
Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != null
|| Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM) != null;
public byte[] CloneSaveRam(bool clearDirty)
{
using (Api.EnterExit())
@ -46,7 +50,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
}
if (size == 0)
if (size == 0 || buf == null)
{
return;
}

View File

@ -6,6 +6,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
{
public bool SaveRamModified => LibSameboy.sameboy_sramlen(SameboyState) != 0;
public bool SupportsSaveRam => LibSameboy.sameboy_sramlen(SameboyState) != 0;
public byte[] CloneSaveRam(bool clearDirty)
{
int length = LibSameboy.sameboy_sramlen(SameboyState);
@ -23,12 +25,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
public void StoreSaveRam(byte[] data)
{
int expected = LibSameboy.sameboy_sramlen(SameboyState);
if (expected == 0) return;
if (data.Length != expected) throw new ArgumentException(message: "Size of saveram data does not match expected!", paramName: nameof(data));
if (expected > 0)
{
LibSameboy.sameboy_loadsram(SameboyState, data, data.Length);
}
LibSameboy.sameboy_loadsram(SameboyState, data, data.Length);
}
}
}

View File

@ -6,6 +6,8 @@ namespace BizHawk.Emulation.Cores.PCEngine
{
public bool SaveRamModified { get; private set; }
public bool SupportsSaveRam => BRAM != null;
public byte[] CloneSaveRam(bool clearDirty)
{
if (clearDirty) SaveRamModified = false;
@ -16,6 +18,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
{
if (BRAM != null)
{
if (data.Length != BRAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Array.Copy(data, BRAM, data.Length);
}

View File

@ -51,6 +51,8 @@ namespace BizHawk.Emulation.Cores.Consoles.SNK
}
}
public new bool SupportsSaveRam => true;
public new byte[] CloneSaveRam(bool clearDirty)
{
_exe.AddTransientFile(new byte[0], "SAV:flash");

View File

@ -52,14 +52,17 @@ namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
if (L.SaveRAM != null && R.SaveRAM == null)
{
if (data.Length != L.SaveRAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, L.SaveRAM, 0, L.SaveRAM.Length);
}
else if (R.SaveRAM != null && L.SaveRAM == null)
{
if (data.Length != R.SaveRAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, R.SaveRAM, 0, R.SaveRAM.Length);
}
else if (R.SaveRAM != null && L.SaveRAM != null)
{
if (data.Length != L.SaveRAM.Length + R.SaveRAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Buffer.BlockCopy(data, 0, L.SaveRAM, 0, L.SaveRAM.Length);
Buffer.BlockCopy(data, L.SaveRAM.Length, R.SaveRAM, 0, R.SaveRAM.Length);
}
@ -68,5 +71,7 @@ namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
}
public bool SaveRamModified => linkSyncSettings.Use_SRAM;
public bool SupportsSaveRam => L.SaveRAM != null || R.SaveRAM != null; // The Use_SRAM setting implements behavior not officially supported by BizHawk.
}
}

View File

@ -14,6 +14,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
if (SaveRAM != null)
{
if (data.Length != SaveRAM.Length) throw new InvalidOperationException("Incorrect sram size.");
Array.Copy(data, SaveRAM, data.Length);
}
@ -22,6 +23,8 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
public bool SaveRamModified { get; private set; }
public bool SupportsSaveRam => SaveRAM != null;
public byte[] SaveRAM;
private byte SaveRamBank;
}

View File

@ -118,6 +118,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn
public new bool SaveRamModified => true;
public new bool SupportsSaveRam => true;
public new byte[] CloneSaveRam(bool clearDirty)
{
var data = new byte[_saturnus.GetSaveRamLength()];
@ -126,7 +128,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.Saturn
}
public new void StoreSaveRam(byte[] data)
=> _saturnus.PutSaveRam(data, data.Length);
{
_saturnus.PutSaveRam(data, data.Length);
}
public bool IsSTV => _isArcade;
}

View File

@ -33,6 +33,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
return;
}
if (!SupportsSaveRam)
{
return;
}
if (!Core.gpgx_put_sram(data, data.Length))
{
throw new Exception("Core rejected saveram");
@ -48,5 +53,15 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
return size > 0 && area != IntPtr.Zero;
}
}
public bool SupportsSaveRam
{
get
{
var size = 0;
var area = Core.gpgx_get_sram(ref size);
return size == 0 || area == IntPtr.Zero;
}
}
}
}

View File

@ -986,6 +986,8 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
}
}
public bool SupportsSaveRam => true;
//THIS IS STILL AWFUL

View File

@ -30,5 +30,7 @@ namespace BizHawk.Emulation.Cores.WonderSwan
}
public bool SaveRamModified => BizSwan.bizswan_saveramsize(Core) > 0;
public bool SupportsSaveRam => true;
}
}

View File

@ -12,6 +12,8 @@ namespace BizHawk.Emulation.Cores.Libretro
public bool SaveRamModified => _saveramSize > 0;
public bool SupportsSaveRam => _saveramSize > 0;
public byte[] CloneSaveRam(bool clearDirty)
{
if (_saveramSize > 0)
@ -39,6 +41,8 @@ namespace BizHawk.Emulation.Cores.Libretro
Marshal.Copy(data, index, m.Data, (int)m.Size);
index += (int)m.Size;
}
if (data.Length != index) throw new InvalidOperationException("Incorrect sram size.");
}
}
}

View File

@ -158,6 +158,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
public bool SupportsSaveRam => _saveramSize != 0;
public byte[] CloneSaveRam(bool clearDirty)
{
if (_saveramSize == 0)
@ -176,6 +178,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
public void StoreSaveRam(byte[] data)
{
if (!SupportsSaveRam) return;
// Checking if the size of the SaveRAM provided coincides with that expected. This is important for cores whose SaveRAM size can vary depending on their configuration.
if (data.Length != _saveramSize)
{