BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs

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;
}
}