From 57ca86710b8796b54547f37920d978c4e16daf68 Mon Sep 17 00:00:00 2001 From: nattthebear Date: Wed, 19 Jul 2017 19:49:23 -0400 Subject: [PATCH] sameboy: SAVERAMS --- .../Consoles/Nintendo/Gameboy/LibSameboy.cs | 9 ++ .../Consoles/Nintendo/Gameboy/Sameboy.cs | 22 +++- BizHawk.Emulation.Cores/Waterbox/PeRunner.cs | 114 ++++++++++++++++-- waterbox/sameboy/bizhawk.cpp | 25 +++- 4 files changed, 157 insertions(+), 13 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs index aba870b558..405635404c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs @@ -42,5 +42,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy [BizImport(CC)] public abstract byte GetIoReg(byte port); + + [BizImport(CC)] + public abstract void PutSaveRam(); + + [BizImport(CC)] + public abstract void GetSaveRam(); + + [BizImport(CC)] + public abstract bool HasSaveRam(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs index aaa1a1a006..928ab1bd62 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs @@ -13,7 +13,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy { [Core("SameBoy", "LIJI32", true, false, "efc11783c7fb6da66e1dd084e41ba6a85c0bd17e", "https://sameboy.github.io/", false)] - public class Sameboy : WaterboxCore, IGameboyCommon + public class Sameboy : WaterboxCore, IGameboyCommon, ISaveRam { /// /// the nominal length of one frame @@ -147,6 +147,26 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy #endregion + #region ISaveram + + public new bool SaveRamModified => _core.HasSaveRam(); + + public new byte[] CloneSaveRam() + { + _exe.AddTransientFile(null, "save.ram"); + _core.GetSaveRam(); + return _exe.RemoveTransientFile("save.ram"); + } + + public new void StoreSaveRam(byte[] data) + { + _exe.AddReadonlyFile(data, "save.ram"); + _core.PutSaveRam(); + _exe.RemoveReadonlyFile("save.ram"); + } + + #endregion + protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) { return new LibSameboy.FrameInfo diff --git a/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs index 1db3950b0a..080b91e4b3 100644 --- a/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs +++ b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs @@ -308,6 +308,68 @@ namespace BizHawk.Emulation.Cores.Waterbox } } + private class TransientFile : IFileObject + { + private bool _inUse = false; + public string Name { get; } + public Stream Stream { get; } + public bool Close() + { + if (_inUse) + { + _inUse = false; + return true; + } + else + { + return false; + } + } + + public bool Open(FileAccess access) + { + if (_inUse) + { + return false; + } + else + { + // TODO: if access != RW, the resultant handle lets you do those all anyway + _inUse = true; + Stream.Position = 0; + return true; + } + } + + public void LoadStateBinary(BinaryReader br) + { + throw new InvalidOperationException("Internal savestate error!"); + } + + public void SaveStateBinary(BinaryWriter bw) + { + throw new InvalidOperationException("Transient files cannot be savestated!"); + } + + public TransientFile(byte[] data, string name) + { + Stream = new MemoryStream(); + Name = name; + if (data != null) + { + Stream.Write(data, 0, data.Length); + Stream.Position = 0; + } + } + + public byte[] GetContents() + { + if (_inUse) + throw new InvalidOperationException(); + return ((MemoryStream)Stream).ToArray(); + } + } + private readonly List _openFiles = new List(); private readonly Dictionary _availableFiles = new Dictionary(); @@ -697,6 +759,21 @@ namespace BizHawk.Emulation.Cores.Waterbox } } + + private T RemoveFileInternal(string name) + where T : IFileObject + { + IFileObject o; + if (!_availableFiles.TryGetValue(name, out o)) + throw new InvalidOperationException("File was never registered!"); + if (o.GetType() != typeof(T)) + throw new InvalidOperationException("Object was not a the right kind of file"); + if (_openFiles.Contains(o)) + throw new InvalidOperationException("Core never closed the file!"); + _availableFiles.Remove(name); + return (T)o; + } + public void AddReadonlyFile(byte[] data, string name) { _availableFiles.Add(name, new ReadonlyFirmware(data, name)); @@ -704,15 +781,16 @@ namespace BizHawk.Emulation.Cores.Waterbox public void RemoveReadonlyFile(string name) { - IFileObject o; - if (!_availableFiles.TryGetValue(name, out o)) - throw new InvalidOperationException("Firmware was never registered!"); - var f = o as ReadonlyFirmware; - if (f == null) - throw new InvalidOperationException("Object was not a firmware!"); - if (_openFiles.Contains(o)) - throw new InvalidOperationException("Core never closed the firmware!"); - _availableFiles.Remove(name); + RemoveFileInternal(name); + } + + public void AddTransientFile(byte[] data, string name) + { + _availableFiles.Add(name, new TransientFile(data, name)); + } + public byte[] RemoveTransientFile(string name) + { + return RemoveFileInternal(name).GetContents(); } } @@ -1090,6 +1168,24 @@ namespace BizHawk.Emulation.Cores.Waterbox _syscalls.RemoveReadonlyFile(name); } + /// + /// Add a transient file that will appear to the waterbox core's libc. The file will be readable + /// and writable. Any attempt to save state while the file is loaded will fail. + /// + public void AddTransientFile(byte[] data, string name) + { + _syscalls.AddTransientFile(data, name); // don't need to clone data, as it's used at init only + } + + /// + /// Remove a file previously added by AddTransientFile + /// + /// The state of the file when it was removed + public byte[] RemoveTransientFile(string name) + { + return _syscalls.RemoveTransientFile(name); + } + public void SaveStateBinary(BinaryWriter bw) { bw.Write(_createstamp); diff --git a/waterbox/sameboy/bizhawk.cpp b/waterbox/sameboy/bizhawk.cpp index 15db12b9d6..f37c60a0eb 100644 --- a/waterbox/sameboy/bizhawk.cpp +++ b/waterbox/sameboy/bizhawk.cpp @@ -15,7 +15,7 @@ extern "C" { static GB_gameboy_t GB; static uint32_t GBPixels[160 * 144]; -static uint32_t* CurrentFramebuffer; +static uint32_t *CurrentFramebuffer; static bool sgb; static void VBlankCallback(GB_gameboy_t *gb) { @@ -94,7 +94,7 @@ static void SgbSampleCallback(int16_t sl, int16_t sr, uint64_t clock) sample_sgb.right = sr; } -ECL_EXPORT bool Init(bool cgb, const uint8_t* spc, int spclen) +ECL_EXPORT bool Init(bool cgb, const uint8_t *spc, int spclen) { if (spc) { @@ -145,7 +145,7 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo &f) { if (sgb) { - sgb_set_controller_data((uint8_t*)&f.Keys); + sgb_set_controller_data((uint8_t *)&f.Keys); } else { @@ -225,6 +225,25 @@ ECL_EXPORT uint8_t GetIoReg(uint8_t port) return GB.io_registers[port]; } +ECL_EXPORT void PutSaveRam() +{ + GB_load_battery(&GB, "save.ram"); +} + +ECL_EXPORT void GetSaveRam() +{ + GB_save_battery(&GB, "save.ram"); +} + +ECL_EXPORT bool HasSaveRam() +{ + if (!GB.cartridge_type->has_battery) + return false; // Nothing to save. + if (GB.mbc_ram_size == 0 && !GB.cartridge_type->has_rtc) + return false; /* Claims to have battery, but has no RAM or RTC */ + return true; +} + int main() { return 0;