From bb80c1f0136384624560a13e93e390ac0372865d Mon Sep 17 00:00:00 2001 From: SuuperW Date: Wed, 16 Jul 2025 22:13:45 -0500 Subject: [PATCH] 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. --- .../movie/MovieConversionExtensions.cs | 1 - .../movie/bk2/Bk2Movie.HeaderApi.cs | 21 +------- .../movie/interfaces/IMovie.cs | 2 +- src/BizHawk.Client.EmuHawk/MainForm.cs | 48 ++++++------------- .../movie/RecordMovie.cs | 3 +- .../tools/TAStudio/TAStudio.MenuItems.cs | 6 +-- .../Base Implementations/LinkedSaveRam.cs | 10 ++-- src/BizHawk.Emulation.Common/Extensions.cs | 2 +- .../Interfaces/Services/ISaveRam.cs | 13 +++-- .../Arcades/MAME/MAME.ISaveRam.cs | 2 + .../Computers/AppleII/AppleII.ISaveRam.cs | 1 + .../Commodore64/Cartridge/Mapper0020.cs | 4 ++ .../Commodore64/Serial/Drive1541.SaveRam.cs | 2 + .../Computers/MSX/MSX.ISaveRam.cs | 3 ++ .../Atari/A7800Hawk/A7800Hawk.ISaveRam.cs | 3 ++ .../Atari/jaguar/VirtualJaguar.ISaveRam.cs | 2 + .../Consoles/Atari/lynx/Lynx.ISaveRam.cs | 4 +- .../GCE/Vectrex/VectrexHawk.ISaveRam.cs | 2 + .../Magnavox/Odyssey2/O2Hawk.ISaveRam.cs | 5 +- .../Nintendo/BSNES/BsnesCore.ISaveRam.cs | 2 + .../Nintendo/GBA/MGBAHawk.ISaveRam.cs | 13 +++++ .../Nintendo/GBHawk/GBHawk.ISaveRam.cs | 5 +- .../GBHawkLink/GBHawkLink.ISaveRam.cs | 5 ++ .../GBHawkLink3x/GBHawkLink3x.ISaveRam.cs | 5 ++ .../GBHawkLink4x/GBHawkLink4x.ISaveRam.cs | 5 ++ .../Nintendo/Gameboy/Gambatte.ISaveRam.cs | 3 ++ .../Consoles/Nintendo/N64/N64.ISaveRam.cs | 2 + .../Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs | 7 +++ .../Consoles/Nintendo/NES/NES.ISaveRam.cs | 12 +++++ .../Nintendo/QuickNES/QuickNES.ISaveRam.cs | 2 + .../Nintendo/SNES/LibsnesCore.ISaveRam.cs | 6 ++- .../Nintendo/SameBoy/SameBoy.ISaveRam.cs | 8 ++-- .../Consoles/PC Engine/PCEngine.ISaveRam.cs | 3 ++ .../Consoles/SNK/NeoGeoPort.cs | 2 + .../Sega/GGHawkLink/GGHawkLink.ISaveRam.cs | 5 ++ .../Consoles/Sega/SMS/SMS.ISaveRam.cs | 3 ++ .../Consoles/Sega/Saturn/Saturnus.cs | 6 ++- .../Consoles/Sega/gpgx64/GPGX.ISaveRam.cs | 15 ++++++ .../Consoles/Sony/PSX/Octoshock.cs | 2 + .../WonderSwan/WonderSwan.ISaveRam.cs | 2 + .../Libretro/Libretro.ISaveRam.cs | 4 ++ .../Waterbox/WaterboxCore.cs | 4 ++ 42 files changed, 178 insertions(+), 77 deletions(-) diff --git a/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs b/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs index aa4c913c24..c04a1c57c0 100644 --- a/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs +++ b/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs @@ -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) diff --git a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs index afb9e7dda0..d965543a8a 100644 --- a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs +++ b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs @@ -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 { diff --git a/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs b/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs index c448fa0279..d7d38d97f0 100644 --- a/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs +++ b/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs @@ -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; } diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs index 091670c339..a0eedda0b9 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.cs @@ -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 { diff --git a/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs b/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs index 3f469ce93f..86eeb4c334 100644 --- a/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs +++ b/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs @@ -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); } diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs index 2ba751d495..a19beb7c07 100644 --- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs +++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs @@ -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; diff --git a/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs b/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs index cd2ea92225..ccc86f5dba 100644 --- a/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs +++ b/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs @@ -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; } } diff --git a/src/BizHawk.Emulation.Common/Extensions.cs b/src/BizHawk.Emulation.Common/Extensions.cs index c277ff125e..3c935d8af0 100644 --- a/src/BizHawk.Emulation.Common/Extensions.cs +++ b/src/BizHawk.Emulation.Common/Extensions.cs @@ -134,7 +134,7 @@ namespace BizHawk.Emulation.Common public static bool HasSaveRam(this IEmulator core) { - return core != null && core.ServiceProvider.HasService(); + return core != null && core.ServiceProvider.HasService() && core.AsSaveRam()!.SupportsSaveRam; } public static ISaveRam AsSaveRam(this IEmulator core) diff --git a/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs b/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs index d0fa8d67e8..e7c73e6153 100644 --- a/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs +++ b/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs @@ -10,16 +10,16 @@ { /// /// 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 is false. /// /// Whether the saveram should be considered in a clean state after this call for purposes of byte[]? CloneSaveRam(bool clearDirty = true); /// - /// 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 is false. /// + /// The core may throw an exception if the given data is invalid. void StoreSaveRam(byte[] data); /// @@ -28,5 +28,10 @@ /// This value should be considered a hint more than an absolute truth. /// bool SaveRamModified { get; } + + /// + /// Certain cores may support SaveRam only in certain situations, for example only for certain games. + /// + bool SupportsSaveRam { get; } } } diff --git a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs index 7bd53d6ca4..e87e031a27 100644 --- a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs @@ -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) diff --git a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISaveRam.cs index b686ccf80e..0df774f798 100644 --- a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISaveRam.cs @@ -15,6 +15,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII } public bool SaveRamModified => true; + public bool SupportsSaveRam => true; public byte[] CloneSaveRam(bool clearDirty) { diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs index 469f1ddfc4..b241fb41ed 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs @@ -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; } } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs index a34b18e2f4..b3a6606387 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs @@ -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(); diff --git a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs index 7b226140c1..c4869319fa 100644 --- a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs @@ -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; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISaveRam.cs index 9e8d938d52..a71c0c11fb 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISaveRam.cs @@ -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; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ISaveRam.cs index 3992a453c1..a1dbd5a578 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ISaveRam.cs @@ -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) diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.ISaveRam.cs index 086e37f98d..a81cfeb4a8 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.ISaveRam.cs @@ -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 _); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISaveRam.cs index c4b7eace2a..638ce514ae 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISaveRam.cs @@ -15,5 +15,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Vectrex } public bool SaveRamModified => false; + + public bool SupportsSaveRam => false; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs index 4950fc6678..929aac06ff 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs @@ -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. } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.ISaveRam.cs index 54fc08a59f..c0a2fcdd24 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.ISaveRam.cs @@ -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; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.ISaveRam.cs index 76afb36a47..b445a3a9d9 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.ISaveRam.cs @@ -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. diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs index 9843e303d1..518f190a81 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs @@ -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. } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs index f5e69eb708..cf25dc0e83 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs @@ -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. } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs index b4241b4730..6b9bd590da 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs @@ -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. } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISaveRam.cs index e6fbe4a945..aac9d7009b 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISaveRam.cs @@ -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. } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs index 213ccd0d9d..5df9066091 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs @@ -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); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.ISaveRam.cs index 974f913376..02649f83b1 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.ISaveRam.cs @@ -15,5 +15,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 } public bool SaveRamModified => true; + + public bool SupportsSaveRam => true; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs index 0364b53905..1acb9e86f7 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs @@ -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) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISaveRam.cs index 67a9563a02..a07014ebb6 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISaveRam.cs @@ -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); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs index b7fdd5b966..797a0dc188 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs @@ -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() diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs index bae5d8c2a6..5629c00247 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs @@ -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; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISaveRam.cs index c1d2c36954..d8eba45f1c 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISaveRam.cs @@ -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); } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISaveRam.cs index 0dd85e1a64..041ad68f81 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISaveRam.cs @@ -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); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs b/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs index 87975014f8..f81cde30b6 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/SNK/NeoGeoPort.cs @@ -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"); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.ISaveRam.cs index 30967d7797..d7b93f3132 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.ISaveRam.cs @@ -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. } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.ISaveRam.cs index 936a037a54..811c2ed2b3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.ISaveRam.cs @@ -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; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs index b8ddca7175..3ed83d569b 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Saturnus.cs @@ -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; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs index 37ad060240..db5db5cf4f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs @@ -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; + } + } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs b/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs index 5a6d547160..309f668b0b 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs @@ -986,6 +986,8 @@ namespace BizHawk.Emulation.Cores.Sony.PSX } } + public bool SupportsSaveRam => true; + //THIS IS STILL AWFUL diff --git a/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.ISaveRam.cs index e338da7986..d1fdeb601d 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.ISaveRam.cs @@ -30,5 +30,7 @@ namespace BizHawk.Emulation.Cores.WonderSwan } public bool SaveRamModified => BizSwan.bizswan_saveramsize(Core) > 0; + + public bool SupportsSaveRam => true; } } diff --git a/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs index 06d03f776f..95ac053ba0 100644 --- a/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs @@ -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."); } } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs index 9cbc2981b5..1022c808a3 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs @@ -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) {