133 lines
3.7 KiB
C#
133 lines
3.7 KiB
C#
using System;
|
|
using System.IO;
|
|
|
|
using BizHawk.Common.BufferExtensions;
|
|
using BizHawk.Emulation.Common;
|
|
|
|
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|
{
|
|
public unsafe partial class LibsnesCore : IStatable
|
|
{
|
|
public bool BinarySaveStatesPreferred => true;
|
|
|
|
public void SaveStateText(TextWriter writer)
|
|
{
|
|
var temp = SaveStateBinary();
|
|
temp.SaveAsHexFast(writer);
|
|
writer.WriteLine("Frame {0}", Frame); // we don't parse this, it's only for the client to use
|
|
writer.WriteLine("Profile {0}", CurrentProfile);
|
|
}
|
|
|
|
public void LoadStateText(TextReader reader)
|
|
{
|
|
string hex = reader.ReadLine();
|
|
byte[] state = new byte[hex.Length / 2];
|
|
state.ReadFromHexFast(hex);
|
|
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
|
reader.ReadLine(); // Frame #
|
|
var profile = reader.ReadLine().Split(' ')[1];
|
|
ValidateLoadstateProfile(profile);
|
|
}
|
|
|
|
public void SaveStateBinary(BinaryWriter writer)
|
|
{
|
|
writer.Write(DeterministicEmulation ? _savestatebuff : CoreSaveState());
|
|
|
|
// other variables
|
|
writer.Write(IsLagFrame);
|
|
writer.Write(LagCount);
|
|
writer.Write(Frame);
|
|
writer.Write(CurrentProfile);
|
|
|
|
writer.Flush();
|
|
}
|
|
|
|
public void LoadStateBinary(BinaryReader reader)
|
|
{
|
|
int size = Api.QUERY_serialize_size();
|
|
byte[] buf = reader.ReadBytes(size);
|
|
CoreLoadState(buf);
|
|
|
|
if (DeterministicEmulation) // deserialize controller and fast-foward now
|
|
{
|
|
// reconstruct savestatebuff at the same time to avoid a costly core serialize
|
|
var ms = new MemoryStream();
|
|
var bw = new BinaryWriter(ms);
|
|
bw.Write(buf);
|
|
bool framezero = reader.ReadBoolean();
|
|
bw.Write(framezero);
|
|
if (!framezero)
|
|
{
|
|
var ssc = new SaveController(ControllerDefinition);
|
|
ssc.DeSerialize(reader);
|
|
IController tmp = _controller;
|
|
_controller = ssc;
|
|
_nocallbacks = true;
|
|
FrameAdvance(ssc, false, false);
|
|
_nocallbacks = false;
|
|
_controller = tmp;
|
|
ssc.Serialize(bw);
|
|
}
|
|
|
|
bw.Close();
|
|
_savestatebuff = ms.ToArray();
|
|
}
|
|
|
|
// other variables
|
|
IsLagFrame = reader.ReadBoolean();
|
|
LagCount = reader.ReadInt32();
|
|
Frame = reader.ReadInt32();
|
|
var profile = reader.ReadString();
|
|
ValidateLoadstateProfile(profile);
|
|
}
|
|
|
|
public byte[] SaveStateBinary()
|
|
{
|
|
var ms = new MemoryStream();
|
|
var bw = new BinaryWriter(ms);
|
|
SaveStateBinary(bw);
|
|
bw.Flush();
|
|
return ms.ToArray();
|
|
}
|
|
|
|
// handle the unmanaged part of loadstating
|
|
private void CoreLoadState(byte[] data)
|
|
{
|
|
int size = Api.QUERY_serialize_size();
|
|
if (data.Length != size)
|
|
{
|
|
throw new Exception("Libsnes internal savestate size mismatch!");
|
|
}
|
|
|
|
Api.CMD_init();
|
|
|
|
// zero 01-sep-2014 - this approach isn't being used anymore, it's too slow!
|
|
// LoadCurrent(); //need to make sure chip roms are reloaded
|
|
fixed (byte* pbuf = &data[0])
|
|
Api.CMD_unserialize(new IntPtr(pbuf), size);
|
|
}
|
|
|
|
|
|
// handle the unmanaged part of savestating
|
|
private byte[] CoreSaveState()
|
|
{
|
|
int size = Api.QUERY_serialize_size();
|
|
byte[] buf = new byte[size];
|
|
fixed (byte* pbuf = &buf[0])
|
|
Api.CMD_serialize(new IntPtr(pbuf), size);
|
|
return buf;
|
|
}
|
|
|
|
private void ValidateLoadstateProfile(string profile)
|
|
{
|
|
if (profile != CurrentProfile)
|
|
{
|
|
throw new InvalidOperationException($"You've attempted to load a savestate made using a different SNES profile ({profile}) than your current configuration ({CurrentProfile}). We COULD automatically switch for you, but we havent done that yet. This error is to make sure you know that this isnt going to work right now.");
|
|
}
|
|
}
|
|
|
|
// most recent internal savestate, for deterministic mode ONLY
|
|
private byte[] _savestatebuff;
|
|
}
|
|
}
|