sameboy: SAVERAMS
This commit is contained in:
parent
cf8013af4a
commit
57ca86710b
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
|
|
|
@ -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<IFileObject> _openFiles = new List<IFileObject>();
|
||||
private readonly Dictionary<string, IFileObject> _availableFiles = new Dictionary<string, IFileObject>();
|
||||
|
||||
|
@ -697,6 +759,21 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private T RemoveFileInternal<T>(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<ReadonlyFirmware>(name);
|
||||
}
|
||||
|
||||
public void AddTransientFile(byte[] data, string name)
|
||||
{
|
||||
_availableFiles.Add(name, new TransientFile(data, name));
|
||||
}
|
||||
public byte[] RemoveTransientFile(string name)
|
||||
{
|
||||
return RemoveFileInternal<TransientFile>(name).GetContents();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1090,6 +1168,24 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
_syscalls.RemoveReadonlyFile(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public void AddTransientFile(byte[] data, string name)
|
||||
{
|
||||
_syscalls.AddTransientFile(data, name); // don't need to clone data, as it's used at init only
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a file previously added by AddTransientFile
|
||||
/// </summary>
|
||||
/// <returns>The state of the file when it was removed</returns>
|
||||
public byte[] RemoveTransientFile(string name)
|
||||
{
|
||||
return _syscalls.RemoveTransientFile(name);
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(_createstamp);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue