diff --git a/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs b/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs index 945a216c7a8..ba02feca63c 100644 --- a/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs +++ b/src/BizHawk.Client.Common/movie/MovieConversionExtensions.cs @@ -133,7 +133,6 @@ public static ITasMovie ConvertToSaveRamAnchoredMovie(this ITasMovie old, byte[] 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 a05ff5a67b0..0e532b1064f 100644 --- a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs +++ b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs @@ -51,26 +51,7 @@ public virtual bool StartsFromSavestate } } - 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 c6b9d68c80a..5c67294609b 100644 --- a/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs +++ b/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs @@ -67,7 +67,7 @@ public interface IMovie : IBasicMovieInfo 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 13adb58e103..743676574b7 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.cs @@ -27,12 +27,8 @@ 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; @@ -1921,36 +1917,24 @@ private void LoadSaveRam() 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); - } + sram = File.ReadAllBytes(saveramToLoad.FullName); + } + catch (Exception e) + { + AddOnScreenMessage("An IO error occurred while loading Sram"); + Console.Error.WriteLine(e); + } - Emulator.AsSaveRam().StoreSaveRam(sram); + try + { + if (sram != null) Emulator.AsSaveRam().StoreSaveRam(sram); } - catch (IOException e) + catch (Exception e) { - AddOnScreenMessage("An error occurred while loading Sram"); + AddOnScreenMessage("The core threw an error while loading Sram"); Console.Error.WriteLine(e); } } @@ -1976,9 +1960,7 @@ public bool FlushSaveRAM(bool autosave = false) 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 716d39e679d..5e6fd433722 100644 --- a/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs +++ b/src/BizHawk.Client.EmuHawk/movie/RecordMovie.cs @@ -103,7 +103,7 @@ public RecordMovie( 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 @@ private void Ok_Click(object sender, EventArgs e) 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 604324a6199..cb01d9129f8 100644 --- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs +++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs @@ -55,7 +55,7 @@ private void StartANewProjectFromSaveRamMenuItem_Click(object sender, EventArgs { 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 newProject = CurrentTasMovie.ConvertToSaveRamAnchoredMovie(saveRam); MainForm.PauseEmulator(); diff --git a/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs b/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs index cd2ea92225e..0f493dac07a 100644 --- a/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs +++ b/src/BizHawk.Emulation.Common/Base Implementations/LinkedSaveRam.cs @@ -23,7 +23,7 @@ private bool LinkedSaveRamModified() { for (int i = 0; i < _numCores; i++) { - if (_linkedCores[i].AsSaveRam().SaveRamModified) + if (_linkedCores[i].AsSaveRam()?.SaveRamModified == true) { return true; } @@ -37,7 +37,7 @@ public byte[] CloneSaveRam(bool clearDirty) int len = 0; for (int i = 0; i < _numCores; i++) { - linkedBuffers.Add(_linkedCores[i].AsSaveRam().CloneSaveRam(clearDirty) ?? Array.Empty()); + linkedBuffers.Add(_linkedCores[i].AsSaveRam()?.CloneSaveRam(clearDirty) ?? Array.Empty()); len += linkedBuffers[i].Length; } byte[] ret = new byte[len]; @@ -55,13 +55,15 @@ public void StoreSaveRam(byte[] data) 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."); } } } diff --git a/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs b/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs index d0fa8d67e8f..df132420d87 100644 --- a/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs +++ b/src/BizHawk.Emulation.Common/Interfaces/Services/ISaveRam.cs @@ -10,16 +10,14 @@ public interface ISaveRam : IEmulatorService { /// /// 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) /// /// Whether the saveram should be considered in a clean state after this call for purposes of - byte[]? CloneSaveRam(bool clearDirty = true); + 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 may throw an exception if the given data is invalid. void StoreSaveRam(byte[] data); /// diff --git a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.IEmulator.cs b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.IEmulator.cs index 7ad918af801..7c6997a9c08 100644 --- a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.IEmulator.cs @@ -8,7 +8,10 @@ public partial class MAME : IEmulator public string SystemId => VSystemID.Raw.Arcade; public bool DeterministicEmulation { get; } public int Frame { get; private set; } - public IEmulatorServiceProvider ServiceProvider { get; } + + private BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; + public ControllerDefinition ControllerDefinition => MAMEController; /// diff --git a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs index 7bd53d6ca4d..a28dfb6b06c 100644 --- a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.ISaveRam.cs @@ -22,7 +22,7 @@ public byte[] CloneSaveRam(bool clearDirty) { if (_nvramFilenames.Count == 0) { - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } for (int i = 0; i < _nvramFilenames.Count; i++) @@ -53,7 +53,7 @@ public void StoreSaveRam(byte[] data) { if (_nvramFilenames.Count == 0) { - return; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } using var ms = new MemoryStream(data, false); diff --git a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.MemoryDomains.cs b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.MemoryDomains.cs index 5ab82b5fb20..13fee69ca90 100644 --- a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.MemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.MemoryDomains.cs @@ -107,7 +107,7 @@ private void InitMemoryDomains() _memoryDomains.SystemBus = _memoryDomains[deviceName + " : System Bus"]!; } - ((BasicServiceProvider)ServiceProvider).Register(_memoryDomains); + _serviceProvider.Register(_memoryDomains); } } } diff --git a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.cs b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.cs index ddb93811425..c13c0f69e8a 100644 --- a/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.cs +++ b/src/BizHawk.Emulation.Cores/Arcades/MAME/MAME.cs @@ -25,13 +25,17 @@ public MAME(CoreLoadParameters lp) _gameFileName = Path.GetFileName(lp.Roms[0].RomPath.SubstringAfter('|')).ToLowerInvariant(); _syncSettings = lp.SyncSettings ?? new(); - ServiceProvider = new BasicServiceProvider(this); + _serviceProvider = new BasicServiceProvider(this); + _serviceProvider.Unregister(); DeterministicEmulation = !_syncSettings.RTCSettings.UseRealTime || lp.DeterministicEmulationRequested; _logCallback = MAMELogCallback; _baseTimeCallback = MAMEBaseTimeCallback; _inputPollCallback = InputCallbacks.Call; - _filenameCallback = name => _nvramFilenames.Add(name); + _filenameCallback = name => { + _nvramFilenames.Add(name); + _serviceProvider.Register(this); + }; _infoCallback = info => { var text = info.Replace(". ", "\n").Replace("\n\n", "\n"); diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs index 469f1ddfc4c..5b2ba5a4f59 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Cartridge/Mapper0020.cs @@ -302,6 +302,8 @@ public void StoreSaveRam(byte[] data) 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; diff --git a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IEmulator.cs b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IEmulator.cs index 52a37185e48..4723327bec7 100644 --- a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IEmulator.cs @@ -4,7 +4,8 @@ namespace BizHawk.Emulation.Cores.Computers.MSX { public partial class MSX : IEmulator, ISoundProvider, IVideoProvider { - public IEmulatorServiceProvider ServiceProvider { get; } + private BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition => current_controller; diff --git a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IMemoryDomains.cs index b448c6f4cb7..a0b9fb623e7 100644 --- a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.IMemoryDomains.cs @@ -49,7 +49,7 @@ private void SetupMemoryDomains() SyncAllByteArrayDomains(); MemoryDomains = new MemoryDomainList(_byteArrayDomains.Values.Concat(domains).ToList()); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + _serviceProvider.Register(MemoryDomains); _memoryDomainsInit = true; } diff --git a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs index 7b226140c15..f40e2df6340 100644 --- a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.ISaveRam.cs @@ -6,20 +6,26 @@ public partial class MSX : ISaveRam { public byte[] CloneSaveRam(bool clearDirty) { - return (byte[]) SaveRAM?.Clone(); + if (SaveRAM == null) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else + return (byte[]) SaveRAM.Clone(); } public void StoreSaveRam(byte[] data) { - if (SaveRAM != null) + if (SaveRAM == null) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else { + if (data.Length != SaveRAM.Length) throw new InvalidOperationException("Incorrect sram size."); Array.Copy(data, SaveRAM, data.Length); } } public bool SaveRamModified { get; private set; } - public byte[] SaveRAM; + private byte[] SaveRAM; private byte SaveRamBank; } } diff --git a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs index b4c2997b738..154c7eba465 100644 --- a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs +++ b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs @@ -14,7 +14,8 @@ public partial class MSX : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, [CoreConstructor(VSystemID.Raw.MSX)] public MSX(CoreLoadParameters lp) { - ServiceProvider = new BasicServiceProvider(this); + _serviceProvider = new BasicServiceProvider(this); + _serviceProvider.Unregister(); Settings = lp.Settings ?? new MSXSettings(); SyncSettings = lp.SyncSettings ?? new MSXSyncSettings(); @@ -141,7 +142,7 @@ public MSX(CoreLoadParameters lp) blip.SetRates(3579545, 44100); - (ServiceProvider as BasicServiceProvider).Register(this); + _serviceProvider.Register(this); SetupMemoryDomains(); @@ -156,9 +157,8 @@ public MSX(CoreLoadParameters lp) Tracer = new TraceBuffer(newHeader.ToString()); - var serviceProvider = ServiceProvider as BasicServiceProvider; - serviceProvider.Register(Tracer); - serviceProvider.Register(new StateSerializer(SyncState)); + _serviceProvider.Register(Tracer); + _serviceProvider.Register(new StateSerializer(SyncState)); current_controller = SyncSettings.Contr_Setting == MSXSyncSettings.ContrType.Keyboard ? MSXControllerKB : MSXControllerJS; } 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 9e8d938d526..ddd45dbd575 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISaveRam.cs @@ -11,6 +11,7 @@ public byte[] CloneSaveRam(bool clearDirty) public void StoreSaveRam(byte[] data) { + if (data.Length != _hsram.Length) throw new InvalidOperationException("Incorrect sram size."); Buffer.BlockCopy(data, 0, _hsram, 0, data.Length); } 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 3992a453c1f..ffcb0cdb091 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ISaveRam.cs @@ -12,7 +12,7 @@ public partial class VirtualJaguar : ISaveRam { if (_saveRamSize == 0) { - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } byte[] ret = new byte[_saveRamSize]; @@ -22,7 +22,9 @@ public partial class VirtualJaguar : ISaveRam public new void StoreSaveRam(byte[] data) { - if (_saveRamSize > 0) + if (_saveRamSize == 0) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else { if (data.Length != _saveRamSize) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs index e25351c5370..2075629a508 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs @@ -113,6 +113,8 @@ public VirtualJaguar(CoreLoadParameters(); _core.SetCdCallbacks(null, null); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IMemoryDomains.cs index 97963fc3807..9672c6cec72 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IMemoryDomains.cs @@ -23,6 +23,10 @@ private void SetupMemoryDomains() { mms.Add(new MemoryDomainIntPtr("Save RAM", MemoryDomain.Endian.Little, p, s, true, 2)); } + else + { + _serviceProvider.Unregister(); + } LibLynx.GetReadOnlyCartPtrs(Core, out var s0, out var p0, out var s1, out var p1); if (s0 > 0 && p0 != IntPtr.Zero) 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 086e37f98df..aa7a36081ad 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.ISaveRam.cs @@ -10,7 +10,7 @@ public byte[] CloneSaveRam(bool clearDirty) { if (!LibLynx.GetSaveRamPtr(Core, out var size, out var data)) { - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } byte[] ret = new byte[size]; @@ -22,7 +22,7 @@ public void StoreSaveRam(byte[] srcData) { if (!LibLynx.GetSaveRamPtr(Core, out var size, out var data)) { - throw new InvalidOperationException(); + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } if (srcData.Length != size) throw new ArgumentException(message: "buffer too small", paramName: nameof(srcData)); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs index 54ef1c01dfc..e88b4ea4c7b 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs @@ -28,7 +28,7 @@ static Lynx() [CoreConstructor(VSystemID.Raw.Lynx)] public Lynx(byte[] file, GameInfo game, CoreComm comm) { - ServiceProvider = new BasicServiceProvider(this); + _serviceProvider = new BasicServiceProvider(this); var bios = comm.CoreFileProvider.GetFirmwareOrThrow(new("Lynx", "Boot"), "Boot rom is required"); if (bios.Length != 512) @@ -131,7 +131,8 @@ public Lynx(byte[] file, GameInfo game, CoreComm comm) private IntPtr Core; - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public bool FrameAdvance(IController controller, bool render, bool rendersound = true) { 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 d93b0732874..2805a112aaa 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISaveRam.cs @@ -1,8 +1,8 @@ -using BizHawk.Emulation.Common; +//using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Consoles.Vectrex { - public partial class VectrexHawk : ISaveRam + public partial class VectrexHawk// : ISaveRam { public byte[] CloneSaveRam(bool clearDirty) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs index 9c24f98d831..dbcafbe8fcb 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs @@ -4,7 +4,8 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk { public partial class O2Hawk : IEmulator, IVideoProvider { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; 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 4950fc66785..9a14bd5342f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs @@ -6,13 +6,18 @@ public partial class O2Hawk : ISaveRam { public byte[] CloneSaveRam(bool clearDirty) { - return (byte[])cart_RAM?.Clone(); + if (cart_RAM == null) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + return (byte[])cart_RAM.Clone(); } public void StoreSaveRam(byte[] data) { - if (_syncSettings.Use_SRAM) + if (cart_RAM == null) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else if (_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"); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs b/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs index fa0d8850d6e..2ac43a3c980 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs @@ -22,7 +22,7 @@ public partial class O2Hawk : IEmulator, ISaveRam, IDebuggable, IInputPollable, public readonly byte[] _rom; public readonly byte[] header = new byte[0x50]; - public byte[] cart_RAM; + private byte[] cart_RAM; public bool has_bat; public int _frame = 0; @@ -43,6 +43,7 @@ public partial class O2Hawk : IEmulator, ISaveRam, IDebuggable, IInputPollable, public O2Hawk(CoreComm comm, GameInfo game, byte[] rom, O2Settings settings, O2SyncSettings syncSettings) { var ser = new BasicServiceProvider(this); + ser.Unregister(); cpu = new I8048 { @@ -76,7 +77,7 @@ public O2Hawk(CoreComm comm, GameInfo game, byte[] rom, O2Settings settings, O2S _frameHz = 60; ser.Register(this); - ServiceProvider = ser; + _serviceProvider = ser; _tracer = new TraceBuffer(cpu.TraceHeader); ser.Register(_tracer); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IEmulator.cs index fb6c1119731..a740bedc8a1 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IEmulator.cs @@ -6,7 +6,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES { public partial class BsnesCore : IEmulator { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition => _controllers.Definition; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IMemoryDomains.cs index 0e4804416c0..20401b2fbc8 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.IMemoryDomains.cs @@ -59,10 +59,13 @@ private void SetMemoryDomains() mm.Add(Api.exe.GetPagesDomain()); _memoryDomains = new(mm); - ((BasicServiceProvider) ServiceProvider).Register(_memoryDomains); + _serviceProvider.Register(_memoryDomains); _memoryDomains.MainMemory = _memoryDomains[_isSGB ? "SGB WRAM" : "WRAM"]; _memoryDomains.SystemBus = _memoryDomains[_isSGB ? "SGB System Bus" : "System Bus"]; + + if (_saveRamSize == 0) + _serviceProvider.Unregister(); } } } 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 54fc08a59fa..3c9382fe3a1 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.ISaveRam.cs @@ -13,7 +13,8 @@ public partial class BsnesCore : ISaveRam public byte[] CloneSaveRam(bool clearDirty) { - if (_saveRamSize == 0) return null; + if (_saveRamSize == 0) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); byte[] saveRamCopy = new byte[_saveRamSize]; using (Api.exe.EnterExit()) @@ -33,7 +34,8 @@ public byte[] CloneSaveRam(bool clearDirty) public void StoreSaveRam(byte[] data) { - if (_saveRamSize == 0) return; + if (_saveRamSize == 0) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); if (data.Length != _saveRamSize) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs index 7f9e925656d..bd065368f71 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/BsnesCore.cs @@ -22,8 +22,7 @@ public partial class BsnesCore : IEmulator, IDebuggable, IVideoProvider, ISaveRa public BsnesCore(CoreLoadParameters loadParameters) : this(loadParameters, false) { } public BsnesCore(CoreLoadParameters loadParameters, bool subframe = false) { - var ser = new BasicServiceProvider(this); - ServiceProvider = ser; + _serviceProvider = new BasicServiceProvider(this); this._romPath = Path.ChangeExtension(loadParameters.Roms[0].RomPath.SubstringBefore('|'), null); CoreComm = loadParameters.Comm; @@ -90,7 +89,7 @@ public BsnesCore(CoreLoadParameters loadParamete // start up audio resampler InitAudio(); - ser.Register(_soundProvider); + _serviceProvider.Register(_soundProvider); if (_isSGB) { @@ -141,8 +140,8 @@ public BsnesCore(CoreLoadParameters loadParamete const string TRACE_HEADER = "65816: PC, mnemonic, operands, registers (A, X, Y, S, D, B, flags (NVMXDIZC), V, H)"; _tracer = new TraceBuffer(TRACE_HEADER); - ser.Register(new W65816_DisassemblerService()); - ser.Register(_tracer); + _serviceProvider.Register(new W65816_DisassemblerService()); + _serviceProvider.Register(_tracer); Api.Seal(); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/SubBsnesCore.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/SubBsnesCore.cs index 20f8bb4dc8b..3bd47cb5fba 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/SubBsnesCore.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES/SubBsnesCore.cs @@ -25,7 +25,8 @@ public SubBsnesCore(CoreLoadParameters()); ser.Register(_bsnesCore.ServiceProvider.GetService()); - ser.Register(_bsnesCore.ServiceProvider.GetService()); + if (_bsnesCore.ServiceProvider.HasService()) + ser.Register(_bsnesCore.ServiceProvider.GetService()); ser.Register(_bsnesCore.ServiceProvider.GetService()); ser.Register(_bsnesCore.ServiceProvider.GetService()); ser.Register(_bsnesCore.ServiceProvider.GetService()); 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 76afb36a476..6380eac9293 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.ISaveRam.cs @@ -21,7 +21,10 @@ public byte[] CloneSaveRam(bool clearDirty) if (len == 0) { - return null; + // mGBA does not know a game's save type (and as a result actual savedata size) on startup. + // Consequently, it cannot conditionally provide the ISaveRam service. + // Unfortunately this means the frontend will create empty save files for games with no savedata. + return Array.Empty(); } var ret = new byte[len]; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 383afc7d0c9..921f9b0be90 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -5,7 +5,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public partial class GBHawk : IEmulator, IVideoProvider { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs index ec26712605d..b40e0093a38 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs @@ -68,7 +68,7 @@ public void SetupMemoryDomains() } MemoryDomains = new MemoryDomainList(domains); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + _serviceProvider.Register(MemoryDomains); } private byte PeekRAM(long addr) 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 9843e303d13..6adf3ebdd46 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs @@ -6,13 +6,18 @@ public partial class GBHawk : ISaveRam { public byte[] CloneSaveRam(bool clearDirty) { - return (byte[])cart_RAM?.Clone(); + if (cart_RAM == null) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + return (byte[])cart_RAM.Clone(); } public void StoreSaveRam(byte[] data) { - if (_syncSettings.Use_SRAM) + if (cart_RAM == null) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else if (_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"); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 51ca93e79d8..7ca856f5d2f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -130,7 +130,7 @@ internal static class RomChecksums [CoreConstructor(VSystemID.Raw.GBC)] public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBSettings settings, GBSyncSettings syncSettings, bool subframe = false) { - var ser = new BasicServiceProvider(this); + _serviceProvider = new BasicServiceProvider(this); cpu = new LR35902 { @@ -204,17 +204,16 @@ public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ GBS ppu.Core = this; serialport.Core = this; - ser.Register(this); - ser.Register(audio); - ServiceProvider = ser; + _serviceProvider.Register(this); + _serviceProvider.Register(audio); _ = PutSettings(settings ?? new GBSettings()); _syncSettings = syncSettings ?? new GBSyncSettings(); _tracer = new TraceBuffer(cpu.TraceHeader); - ser.Register(_tracer); - ser.Register(new StateSerializer(SyncState)); - ser.Register(_disassembler); + _serviceProvider.Register(_tracer); + _serviceProvider.Register(new StateSerializer(SyncState)); + _serviceProvider.Register(_disassembler); SetupMemoryDomains(); cpu.SetCallbacks(ReadMemory, PeekMemory, PeekMemory, WriteMemory); HardReset(); @@ -662,6 +661,11 @@ public string Setup_Mapper(string romHashMD5, string romHashSHA1) } } + if (cart_RAM == null) + { + _serviceProvider.Unregister(); + } + // Extra RTC initialization for mbc3, HuC3, and TAMA5 if (mppr == "MBC3") { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IEmulator.cs index 99892fa4c34..225a539a578 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IEmulator.cs @@ -4,7 +4,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink { public partial class GBHawkLink : IEmulator, IVideoProvider, ISoundProvider { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IMemoryDomains.cs index 19dfb15e990..38d8caf4a7f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IMemoryDomains.cs @@ -96,8 +96,13 @@ public void SetupMemoryDomains() domains.Add(cartRamR); } + if (L.cart_RAM == null && R.cart_RAM == null) + { + _serviceProvider.Unregister(); + } + MemoryDomains = new MemoryDomainList(domains); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + _serviceProvider.Register(MemoryDomains); } private byte PeekSystemBusL(long addr) 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 f5e69eb7085..f5c97ae2319 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs @@ -45,7 +45,7 @@ public byte[] CloneSaveRam(bool clearDirty) return temp; } - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } public void StoreSaveRam(byte[] data) @@ -54,17 +54,24 @@ public void StoreSaveRam(byte[] data) { 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); } + else + { + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + } Console.WriteLine("loading SRAM here"); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs index fd77e0ba821..d7594f3b7be 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs @@ -37,8 +37,7 @@ public GBHawkLink(CoreLoadParameters(this); - ser.Register(this); + _serviceProvider.Register(this); + _serviceProvider.Register(this); _tracer = new TraceBuffer(L.cpu.TraceHeader); - ser.Register(_tracer); + _serviceProvider.Register(_tracer); _lStates = L.ServiceProvider.GetService(); _rStates = R.ServiceProvider.GetService(); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs index 875b4b3e2c1..ab88e2a87cb 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs @@ -4,7 +4,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x { public partial class GBHawkLink3x : IEmulator, IVideoProvider, ISoundProvider { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs index 7441f030971..9e7ca59f62c 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs @@ -137,8 +137,13 @@ public void SetupMemoryDomains() domains.Add(cartRamR); } + if (L.cart_RAM == null && C.cart_RAM == null && R.cart_RAM == null) + { + _serviceProvider.Unregister(); + } + MemoryDomains = new MemoryDomainList(domains); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + _serviceProvider.Register(MemoryDomains); } private byte PeekSystemBusL(long addr) 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 b4241b47301..fd7d782f80c 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs @@ -60,7 +60,7 @@ public byte[] CloneSaveRam(bool clearDirty) return temp; } - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } public void StoreSaveRam(byte[] data) @@ -84,8 +84,12 @@ public void StoreSaveRam(byte[] data) if (R.cart_RAM != null) { Buffer.BlockCopy(data, temp, R.cart_RAM, 0, R.cart_RAM.Length); + temp += R.cart_RAM.Length; } + if (temp == 0) throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + if (data.Length != temp) throw new InvalidOperationException("Incorrect sram size."); + Console.WriteLine("loading SRAM here"); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs index 6d5ddbff7dd..633acd89729 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs @@ -37,8 +37,7 @@ public GBHawkLink3x(CoreLoadParameters l if (lp.Roms.Count != 3) throw new InvalidOperationException("Wrong number of roms"); - var ser = new BasicServiceProvider(this); - ServiceProvider = ser; + _serviceProvider = new BasicServiceProvider(this); Link3xSettings = lp.Settings ?? new GBLink3xSettings(); Link3xSyncSettings = lp.SyncSettings ?? new GBLink3xSyncSettings(); @@ -74,11 +73,11 @@ public GBHawkLink3x(CoreLoadParameters l C = new GBHawk.GBHawk(lp.Comm, lp.Roms[1].Game, lp.Roms[1].RomData, tempSetC, tempSyncC); R = new GBHawk.GBHawk(lp.Comm, lp.Roms[2].Game, lp.Roms[2].RomData, tempSetR, tempSyncR); - ser.Register(this); - ser.Register(this); + _serviceProvider.Register(this); + _serviceProvider.Register(this); _tracer = new TraceBuffer(L.cpu.TraceHeader); - ser.Register(_tracer); + _serviceProvider.Register(_tracer); _lStates = L.ServiceProvider.GetService(); _cStates = C.ServiceProvider.GetService(); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IEmulator.cs index 209bbe20bfa..0aacd3daf25 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IEmulator.cs @@ -5,7 +5,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x { public partial class GBHawkLink4x : IEmulator, IVideoProvider, ISoundProvider { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IMemoryDomains.cs index b0eb4dda3cd..ba92de7748a 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.IMemoryDomains.cs @@ -178,8 +178,13 @@ public void SetupMemoryDomains() domains.Add(cartRamD); } + if (A.cart_RAM == null && B.cart_RAM == null && C.cart_RAM == null && D.cart_RAM == null) + { + _serviceProvider.Unregister(); + } + MemoryDomains = new MemoryDomainList(domains); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + _serviceProvider.Register(MemoryDomains); } private byte PeekSystemBusA(long addr) 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 e6fbe4a945f..ceb73f177ce 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISaveRam.cs @@ -75,7 +75,7 @@ public byte[] CloneSaveRam(bool clearDirty) return temp; } - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } public void StoreSaveRam(byte[] data) @@ -105,8 +105,12 @@ public void StoreSaveRam(byte[] data) if (D.cart_RAM != null) { Buffer.BlockCopy(data, temp, D.cart_RAM, 0, D.cart_RAM.Length); + temp += D.cart_RAM.Length; } + if (temp == 0) throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + if (data.Length != temp) throw new InvalidOperationException("Incorrect sram size."); + Console.WriteLine("loading SRAM here"); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.cs index 5df68b05217..b71f128a483 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.cs @@ -58,7 +58,7 @@ public GBHawkLink4x(CoreLoadParameters l if (lp.Roms.Count != 4) throw new InvalidOperationException("Wrong number of roms"); - var ser = new BasicServiceProvider(this); + _serviceProvider = new BasicServiceProvider(this); Link4xSettings = lp.Settings ?? new GBLink4xSettings(); Link4xSyncSettings = lp.SyncSettings ?? new GBLink4xSyncSettings(); @@ -102,13 +102,11 @@ public GBHawkLink4x(CoreLoadParameters l C = new GBHawk.GBHawk(lp.Comm, lp.Roms[2].Game, lp.Roms[2].RomData, tempSetC, tempSyncC); D = new GBHawk.GBHawk(lp.Comm, lp.Roms[3].Game, lp.Roms[3].RomData, tempSetD, tempSyncD); - ser.Register(this); - ser.Register(this); + _serviceProvider.Register(this); + _serviceProvider.Register(this); _tracer = new TraceBuffer(A.cpu.TraceHeader); - ser.Register(_tracer); - - ServiceProvider = ser; + _serviceProvider.Register(_tracer); _aStates = A.ServiceProvider.GetService(); _bStates = B.ServiceProvider.GetService(); 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 213ccd0d9d1..630e467a4b6 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs @@ -18,12 +18,13 @@ public byte[] CloneSaveRam(bool clearDirty) return ret; } - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } public void StoreSaveRam(byte[] data) { var expected = LibGambatte.gambatte_getsavedatalength(GambatteState); + if (expected == 0) throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); 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/Gameboy/Gambatte.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 170202ad6a2..b0e7a43a318 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -251,6 +251,11 @@ bool ForceBios() remote: _syncSettings.EnableRemote); NewSaveCoreSetBuff(); + + if (LibGambatte.gambatte_getsavedatalength(GambatteState) == 0) + { + _serviceProvider.Unregister(); + } } catch { 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 478b52fa846..cf0734c1db3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs @@ -15,7 +15,7 @@ public partial class NDS : ISaveRam { if (DSiWareSaveLength == 0) { - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } _exe.AddTransientFile([ ], "public.sav"); @@ -47,13 +47,14 @@ public partial class NDS : ISaveRam return ret; } - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } public new void StoreSaveRam(byte[] data) { if (IsDSiWare) { + if (DSiWareSaveLength == 0) throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); if (data.Length == DSiWareSaveLength) { if (PublicSavSize > 0) _exe.AddReadonlyFile(data.AsSpan().Slice(0, PublicSavSize).ToArray(), "public.sav"); @@ -66,6 +67,10 @@ public partial class NDS : ISaveRam 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/NDS/MelonDS.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs index b0f83d58f7a..dcc28be792b 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs @@ -618,6 +618,12 @@ static void InitIv(Span iv, ReadOnlySpan nonce) _serviceProvider.Register(_glTextureProvider); RefreshScreenSettings(_settings); } + + bool supportsSaveRam = IsDSiWare ? DSiWareSaveLength != 0 : _core.GetSaveRamLength(_console) != 0; + if (!supportsSaveRam) + { + _serviceProvider.Unregister(); + } } catch { 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 dccf2c15a6c..2d7c8615715 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,14 @@ public bool SaveRamModified } } + private bool HasSaveRam() + { + 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) @@ -22,7 +30,8 @@ public byte[] CloneSaveRam(bool clearDirty) return fds.ReadSaveRam(); } - return (byte[]) Board?.SaveRam?.Clone(); + byte[]/*?*/ sram = (byte[])Board?.SaveRam?.Clone(); + return sram ?? throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } public void StoreSaveRam(byte[] data) @@ -30,14 +39,15 @@ public void StoreSaveRam(byte[] data) if (Board is FDS fds) { fds.StoreSaveRam(data); - return; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } if (Board?.SaveRam == null) { - return; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } + 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/NES/NES.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs index a8df5332b68..ecd03c05fdb 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs @@ -77,6 +77,11 @@ public NES(CoreComm comm, GameInfo game, byte[] rom, NESSettings settings, NESSy } ResetControllerDefinition(subframe); + + if (!HasSaveRam()) + { + ser.Unregister(); + } } private static readonly bool USE_DATABASE = true; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs index e99d5f2867e..900ab982b99 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IMemoryDomains.cs @@ -43,7 +43,7 @@ private void InitMemoryDomains() }, 1)); _memoryDomains = new MemoryDomainList(mm); - ((BasicServiceProvider) ServiceProvider).Register(_memoryDomains); + _serviceProvider.Register(_memoryDomains); } private IMemoryDomains _memoryDomains; 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 cde23166ed1..7b079237636 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISaveRam.cs @@ -6,7 +6,8 @@ public sealed partial class QuickNES : ISaveRam { public byte[] CloneSaveRam(bool clearDirty) { - if (!QN.qn_has_battery_ram(Context)) return null; + if (!QN.qn_has_battery_ram(Context)) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); LibQuickNES.ThrowStringError(QN.qn_battery_ram_save(Context, _saveRamBuff, _saveRamBuff.Length)); return (byte[])_saveRamBuff.Clone(); @@ -14,7 +15,8 @@ public byte[] CloneSaveRam(bool clearDirty) public void StoreSaveRam(byte[] data) { - if (!QN.qn_has_battery_ram(Context)) return; + if (!QN.qn_has_battery_ram(Context)) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); LibQuickNES.ThrowStringError(QN.qn_battery_ram_load(Context, data, data.Length)); } @@ -28,6 +30,9 @@ private void InitSaveRamBuff() int size = 0; LibQuickNES.ThrowStringError(QN.qn_battery_ram_size(Context, ref size)); _saveRamBuff = new byte[size]; + + if (!QN.qn_has_battery_ram(Context)) + _serviceProvider.Unregister(); } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ITraceable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ITraceable.cs index 9f4f37b856b..4eb0f275a50 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ITraceable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ITraceable.cs @@ -40,7 +40,7 @@ private void MakeTrace(IntPtr data) private void ConnectTracer() { Tracer = new TraceBuffer(TraceHeader); - ((BasicServiceProvider) ServiceProvider).Register(Tracer); + _serviceProvider.Register(Tracer); _traceCb = MakeTrace; } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs index b025cd98581..3741190b9eb 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs @@ -30,7 +30,7 @@ static QuickNES() [CoreConstructor(VSystemID.Raw.NES)] public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syncSettings) { - ServiceProvider = new BasicServiceProvider(this); + _serviceProvider = new BasicServiceProvider(this); Context = QN.qn_new(); if (Context == IntPtr.Zero) { @@ -70,7 +70,9 @@ public QuickNES(byte[] file, QuickNESSettings settings, QuickNESSyncSettings syn private static readonly LibQuickNES QN; - public IEmulatorServiceProvider ServiceProvider { get; } + + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; int IVideoLogicalOffsets.ScreenX => _settings.ClipLeftAndRight ? 8 : 0; 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 bae5d8c2a66..17006368064 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs @@ -25,7 +25,7 @@ public byte[] CloneSaveRam(bool clearDirty) if (buf == null) { - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } var ret = new byte[size]; @@ -46,9 +46,9 @@ public void StoreSaveRam(byte[] data) size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); } - if (size == 0) + if (size == 0 || buf == null) { - return; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } if (size != data.Length) diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs index cceb0160d1b..6fb6296f1e9 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs @@ -193,6 +193,13 @@ public LibsnesCore(GameInfo game, byte[] romData, byte[] xmlData, string baseRom Api.QUERY_set_audio_sample(_soundcb); Api.Seal(); RefreshPalette(); + + bool hasSaveRam = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != null + || Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM) != null; + if (!hasSaveRam) + { + ser.Unregister(); + } } private readonly LibsnesApi.snes_video_refresh_t _videocb; 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 c1d2c369544..a20ff99014c 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISaveRam.cs @@ -10,25 +10,23 @@ public byte[] CloneSaveRam(bool clearDirty) { int length = LibSameboy.sameboy_sramlen(SameboyState); - if (length > 0) + if (length == 0) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else { byte[] ret = new byte[length]; LibSameboy.sameboy_savesram(SameboyState, ret); return ret; } - - return null; } public void StoreSaveRam(byte[] data) { int expected = LibSameboy.sameboy_sramlen(SameboyState); + if (expected == 0) throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); 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/Nintendo/SameBoy/SameBoy.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.cs index 8e56d0b2035..2811def2fb2 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.cs @@ -163,6 +163,10 @@ private Sameboy( InitMemoryDomains(); InitMemoryCallbacks(); + if (LibSameboy.sameboy_sramlen(SameboyState) == 0) + { + _serviceProvider.Unregister(); + } _inputcb = InputCallback; _rumblecb = RumbleCallback; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubGBHawk/SubGBHawk.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubGBHawk/SubGBHawk.cs index 5dcb92f32dd..972b7fdc7fe 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubGBHawk/SubGBHawk.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubGBHawk/SubGBHawk.cs @@ -33,7 +33,8 @@ public SubGBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ ser.Register(_GBCore.ServiceProvider.GetService()); ser.Register(_GBCore.ServiceProvider.GetService()); ser.Register(_GBCore.ServiceProvider.GetService()); - ser.Register(_GBCore.ServiceProvider.GetService()); + if (_GBCore.ServiceProvider.HasService()) + ser.Register(_GBCore.ServiceProvider.GetService()); ser.Register(_GBCore.ServiceProvider.GetService()); ser.Register(_GBCore.ServiceProvider.GetService()); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.cs index 78c848eaa8a..d9b07e2e98a 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.cs @@ -32,7 +32,8 @@ public SubNESHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ ser.Register(_nesCore.ServiceProvider.GetService()); ser.Register(_nesCore.ServiceProvider.GetService()); ser.Register(_nesCore.ServiceProvider.GetService()); - ser.Register(_nesCore.ServiceProvider.GetService()); + if (_nesCore.ServiceProvider.HasService()) + ser.Register(_nesCore.ServiceProvider.GetService()); ser.Register(_nesCore.ServiceProvider.GetService()); ser.Register(_nesCore.ServiceProvider.GetService()); ser.Register(_nesCore.ServiceProvider.GetService()); 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 0dd85e1a644..406255d3151 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISaveRam.cs @@ -8,16 +8,22 @@ public sealed partial class PCEngine : ISaveRam public byte[] CloneSaveRam(bool clearDirty) { + if (BRAM == null) throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); if (clearDirty) SaveRamModified = false; - return (byte[]) BRAM?.Clone(); + return (byte[]) BRAM.Clone(); } public void StoreSaveRam(byte[] data) { if (BRAM != null) { + if (data.Length != BRAM.Length) throw new InvalidOperationException("Incorrect sram size."); Array.Copy(data, BRAM, data.Length); } + else + { + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + } SaveRamModified = false; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs b/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs index 24b6bb7c2a7..39785728bdc 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs @@ -323,6 +323,8 @@ private void Init(GameInfo game, byte[] rom) ser.Register(_soundProvider); ser.Register(PSG); ser.Register(new StateSerializer(SyncState)); + if (BRAM == null) + ser.Unregister(); SetupMemoryDomains(); } 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 30967d77975..8ae8697c251 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.ISaveRam.cs @@ -45,24 +45,31 @@ public byte[] CloneSaveRam(bool clearDirty) return temp; } - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } public void StoreSaveRam(byte[] data) { 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); } + else + { + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + } Console.WriteLine("loading SRAM here"); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.cs index 2ca77759c64..dc990235060 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/GGHawkLink/GGHawkLink.cs @@ -46,6 +46,8 @@ public GGHawkLink(CoreLoadParameters lp) ser.Register(L); ser.Register(this); ser.Register(this); + if (L.SaveRAM == null && R.SaveRAM == null) + ser.Unregister(); _tracer = new TraceBuffer(L.Cpu.TraceHeader); ser.Register(_tracer); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs index 1c044015e4f..f4c51ff2869 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs @@ -4,7 +4,8 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem { public partial class SMS : IEmulator { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; // not savestated variables private int s_L, s_R; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IMemoryDomains.cs index a3d9ec5f256..ff92b7b85b8 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IMemoryDomains.cs @@ -35,11 +35,15 @@ private void SetupMemoryDomains() (addr, value) => { SaveRAM[addr] = value; SaveRamModified = true; }, 1); domains.Add(saveRamDomain); } + else + { + _serviceProvider.Unregister(); + } SyncAllByteArrayDomains(); MemoryDomains = new MemoryDomainList(_byteArrayDomains.Values.Concat(domains).ToList()); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + _serviceProvider.Register(MemoryDomains); _memoryDomainsInit = true; } 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 936a037a54c..6755a9f39ab 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.ISaveRam.cs @@ -6,16 +6,22 @@ public partial class SMS : ISaveRam { public byte[] CloneSaveRam(bool clearDirty) { + if (SaveRAM == null) throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); if (clearDirty) SaveRamModified = false; - return (byte[]) SaveRAM?.Clone(); + return (byte[]) SaveRAM.Clone(); } public void StoreSaveRam(byte[] data) { if (SaveRAM != null) { + if (data.Length != SaveRAM.Length) throw new InvalidOperationException("Incorrect sram size."); Array.Copy(data, SaveRAM, data.Length); } + else + { + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + } SaveRamModified = false; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs index 8b09cbb1901..0a8c681917f 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs @@ -21,8 +21,7 @@ public partial class SMS : IEmulator, ISoundProvider, ISaveRam, IInputPollable, [CoreConstructor(VSystemID.Raw.GG)] public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSyncSettings syncSettings) { - var ser = new BasicServiceProvider(this); - ServiceProvider = ser; + _serviceProvider = new BasicServiceProvider(this); Settings = settings ?? new SmsSettings(); SyncSettings = syncSettings ?? new SmsSyncSettings(); @@ -94,7 +93,7 @@ public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSy SystemId = game.System; Vdp = new VDP(this, Cpu, IsGameGear ? VdpMode.GameGear : VdpMode.SMS, Region, sms_reg_compat); - ser.Register(Vdp); + _serviceProvider.Register(Vdp); PSG = new SN76489sms(); YM2413 = new YM2413(); //SoundMixer = new SoundMixer(YM2413, PSG); @@ -106,7 +105,7 @@ public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSy BlipL.SetRates(3579545, 44100); BlipR.SetRates(3579545, 44100); - ser.Register(this); + _serviceProvider.Register(this); SystemRam = new byte[0x2000]; @@ -194,9 +193,9 @@ public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSy Tracer = new TraceBuffer(Cpu.TraceHeader); - ser.Register(Tracer); - ser.Register(Cpu); - ser.Register(new StateSerializer(SyncState)); + _serviceProvider.Register(Tracer); + _serviceProvider.Register(Cpu); + _serviceProvider.Register(new StateSerializer(SyncState)); Vdp.ProcessOverscan(); // Z80 SP initialization @@ -206,7 +205,7 @@ public SMS(CoreComm comm, GameInfo game, byte[] rom, SmsSettings settings, SmsSy if (!IsSG1000) { - ser.Register(new SmsGpuView(Vdp)); + _serviceProvider.Register(new SmsGpuView(Vdp)); } _controllerDeck = new SMSControllerDeck(SyncSettings.Port1, SyncSettings.Port2, IsGameGear_C, SyncSettings.UseKeyboard); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs index 1ec620a4580..6c85d7ebd31 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs @@ -4,7 +4,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { public partial class GPGX : IEmulator, ISoundProvider { - public IEmulatorServiceProvider ServiceProvider { get; } + private readonly BasicServiceProvider _serviceProvider; + public IEmulatorServiceProvider ServiceProvider => _serviceProvider; public ControllerDefinition ControllerDefinition { get; private set; } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IMemoryDomains.cs index 16ad217c1df..9374a9c32ff 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IMemoryDomains.cs @@ -198,7 +198,7 @@ private unsafe void SetMemoryDomains() mm.Add(_elf.GetPagesDomain()); _memoryDomains = new MemoryDomainList(mm) { SystemBus = systemBus }; - ((BasicServiceProvider) ServiceProvider).Register(_memoryDomains); + _serviceProvider.Register(_memoryDomains); } } } 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 37ad0602406..250c949103e 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs @@ -13,7 +13,7 @@ public byte[] CloneSaveRam(bool clearDirty) var area = Core.gpgx_get_sram(ref size); if (size == 0 || area == IntPtr.Zero) { - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); } var ret = new byte[size]; @@ -33,6 +33,13 @@ public void StoreSaveRam(byte[] data) return; } + var size = 0; + var area = Core.gpgx_get_sram(ref size); + if (size == 0 || area == IntPtr.Zero) + { + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + } + if (!Core.gpgx_put_sram(data, data.Length)) { throw new Exception("Core rejected saveram"); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs index 030a4a03a4b..17fe1aafbd8 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs @@ -30,7 +30,7 @@ public GPGX(CoreLoadParameters lp) CDCallback = CDCallbackProc; CDReadCallback = CDRead; - ServiceProvider = new BasicServiceProvider(this); + _serviceProvider = new BasicServiceProvider(this); // this can influence some things internally (autodetect romtype, etc) // Determining system ID from the rom. If no rom provided, assume Genesis (Sega CD) @@ -49,7 +49,7 @@ public GPGX(CoreLoadParameters lp) }; } PCRegisterName = SystemId is VSystemID.Raw.GEN ? "M68K PC" : "Z80 pc"; - if (SystemId is not VSystemID.Raw.GEN) ((BasicServiceProvider) ServiceProvider).Unregister(); + if (SystemId is not VSystemID.Raw.GEN) _serviceProvider.Unregister(); // three or six button? // http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds @@ -141,6 +141,12 @@ public GPGX(CoreLoadParameters lp) UpdateVideo(); SetMemoryDomains(); + var size = 0; + var area = Core.gpgx_get_sram(ref size); + if (size == 0 || area == IntPtr.Zero) + { + _serviceProvider.Unregister(); + } Core.gpgx_set_input_callback(_inputCallback); @@ -152,7 +158,7 @@ public GPGX(CoreLoadParameters lp) if (SystemId == VSystemID.Raw.GEN) { _tracer = new GPGXTraceBuffer(this, _memoryDomains, this); - ((BasicServiceProvider)ServiceProvider).Register(_tracer); + _serviceProvider.Register(_tracer); } } diff --git a/src/BizHawk.Emulation.Cores/Libretro/Libretro.IMemoryDomains.cs b/src/BizHawk.Emulation.Cores/Libretro/Libretro.IMemoryDomains.cs index 8d4960954b5..8e2a85f9b73 100644 --- a/src/BizHawk.Emulation.Cores/Libretro/Libretro.IMemoryDomains.cs +++ b/src/BizHawk.Emulation.Cores/Libretro/Libretro.IMemoryDomains.cs @@ -38,6 +38,9 @@ private void InitMemoryDomains() } } + if (_saveramSize == 0) + _serviceProvider.Unregister(); + // no domains to register... if (md.Count == 0) { diff --git a/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs index 06d03f776fe..9698c10d1d5 100644 --- a/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Libretro/Libretro.ISaveRam.cs @@ -14,7 +14,9 @@ public partial class LibretroHost : ISaveRam public byte[] CloneSaveRam(bool clearDirty) { - if (_saveramSize > 0) + if (_saveramSize == 0) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else { var buf = new byte[_saveramSize]; int index = 0; @@ -25,13 +27,13 @@ public byte[] CloneSaveRam(bool clearDirty) } return buf; } - - return null; } public void StoreSaveRam(byte[] data) { - if (_saveramSize > 0) + if (_saveramSize == 0) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + else { int index = 0; foreach (var m in _saveramAreas) @@ -39,6 +41,8 @@ public void StoreSaveRam(byte[] data) 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 6bfdffede0c..03c3719d336 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs @@ -88,6 +88,8 @@ protected void PostInit() .Where(md => md.Definition.Flags.HasFlag(LibWaterboxCore.MemoryDomainFlags.Saverammable)) .ToArray(); _saveramSize = (int)_saveramAreas.Sum(a => a.Size); + if (_saveramSize == 0) + _serviceProvider.Unregister(); _exe.Seal(); } @@ -161,7 +163,7 @@ public unsafe bool SaveRamModified public byte[] CloneSaveRam(bool clearDirty) { if (_saveramSize == 0) - return null; + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); using (_exe.EnterExit()) { var ret = new byte[_saveramSize]; @@ -176,6 +178,9 @@ public byte[] CloneSaveRam(bool clearDirty) public void StoreSaveRam(byte[] data) { + if (_saveramSize == 0) + throw new InvalidOperationException("Core currently has no SRAM and should not be providing ISaveRam service."); + // 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) {