diff --git a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs
index 6a0177dbbf..c8c018be70 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs
@@ -422,6 +422,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
LibsnesDll.snes_power();
SetupMemoryDomains(romData);
+
+ // disallow any future modifications to the DeterministicEmulation parameter, and set initial deterministic savestate
+ _DeterministicEmulationProtected = true;
+ if (DeterministicEmulation)
+ CoreSaveStateInternal(true);
}
//must keep references to these so that they wont get garbage collected
@@ -571,6 +576,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
if (IsLagFrame)
LagCount++;
+
+ if (DeterministicEmulation)
+ {
+ // save the one internal savestate for this frame now
+ CoreSaveStateInternal(true);
+ }
}
//video provider
@@ -611,7 +622,22 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public int LagCount { get; set; }
public bool IsLagFrame { get; private set; }
public string SystemId { get; private set; }
- public bool DeterministicEmulation { get; set; }
+
+
+ bool _DeterministicEmulation = false;
+ bool _DeterministicEmulationProtected = false;
+ public bool DeterministicEmulation
+ {
+ get { return _DeterministicEmulation; }
+ set
+ {
+ if (_DeterministicEmulationProtected)
+ throw new Exception("snes: DeterministicEmulation must be set before load!");
+ _DeterministicEmulation = value;
+ }
+ }
+
+
public bool SaveRamModified
{
set { }
@@ -669,10 +695,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public void SaveStateBinary(BinaryWriter writer)
{
- int size = LibsnesDll.snes_serialize_size();
- byte[] buf = new byte[size];
- fixed (byte* pbuf = &buf[0])
- LibsnesDll.snes_serialize(new IntPtr(pbuf), size);
+ byte[] buf = CoreSaveState();
writer.Write(buf);
// other variables
@@ -685,10 +708,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public void LoadStateBinary(BinaryReader reader)
{
int size = LibsnesDll.snes_serialize_size();
-
byte[] buf = reader.ReadBytes(size);
- fixed (byte* pbuf = &buf[0])
- LibsnesDll.snes_unserialize(new IntPtr(pbuf), size);
+ CoreLoadState(buf);
// other variables
IsLagFrame = reader.ReadBoolean();
@@ -704,6 +725,52 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
return ms.ToArray();
}
+ ///
+ /// handle the unmanaged part of loadstating
+ ///
+ void CoreLoadState(byte[] data)
+ {
+ int size = LibsnesDll.snes_serialize_size();
+ if (data.Length != size)
+ throw new Exception("Libsnes internal savestate size mismatch!");
+ fixed (byte* pbuf = &data[0])
+ LibsnesDll.snes_unserialize(new IntPtr(pbuf), size);
+ }
+ ///
+ /// handle the unmanaged part of savestating
+ ///
+ byte[] CoreSaveState()
+ {
+ if (!DeterministicEmulation)
+ return CoreSaveStateInternal(false);
+ else
+ return savestatebuff;
+ }
+
+ ///
+ /// most recent internal savestate, for deterministic mode
+ ///
+ byte[] savestatebuff;
+
+ ///
+ /// internal function handling savestate
+ /// this can cause determinism problems if called improperly!
+ ///
+ byte[] CoreSaveStateInternal(bool store)
+ {
+ int size = LibsnesDll.snes_serialize_size();
+ byte[] buf = new byte[size];
+ fixed (byte* pbuf = &buf[0])
+ LibsnesDll.snes_serialize(new IntPtr(pbuf), size);
+ if (store)
+ {
+ savestatebuff = buf;
+ return null;
+ }
+ else
+ return buf;
+ }
+
// Arbitrary extensible core comm mechanism
public CoreInputComm CoreInputComm { get; set; }
public CoreOutputComm CoreOutputComm { get { return _CoreOutputComm; } }