BizHawk/BizHawk.MultiClient/Rewind.cs

175 lines
6.1 KiB
C#

using System.IO;
namespace BizHawk.MultiClient
{
public partial class MainForm
{
private MruStack<MemoryStream> RewindBuf = new MruStack<MemoryStream>(15000);
private byte[] LastState;
private void CaptureRewindState()
{
if (LastState == null)
{
// This is the first frame. Capture the state, and put it in LastState for future deltas to be compared against.
LastState = Global.Emulator.SaveStateBinary();
//System.Console.WriteLine(LastState.Length);
return;
}
// Otherwise, it's not the first frame, so build a delta.
if (LastState.Length <= 0x10000)
CaptureRewindState64K();
else
CaptureRewindStateLarge();
}
/// <summary>
/// Builds a delta for states that are <= 64K in size.
/// </summary>
private void CaptureRewindState64K()
{
byte[] CurrentState = Global.Emulator.SaveStateBinary();
int beginChangeSequence = -1;
bool inChangeSequence = false;
var ms = new MemoryStream();
var writer = new BinaryWriter(ms);
for (int i = 0; i < CurrentState.Length; i++)
{
if (inChangeSequence == false)
{
if (i >= LastState.Length)
continue;
if (CurrentState[i] == LastState[i])
continue;
inChangeSequence = true;
beginChangeSequence = i;
continue;
}
if (i - beginChangeSequence == 254 || i == CurrentState.Length - 1)
{
writer.Write((byte)(i - beginChangeSequence + 1));
writer.Write((ushort)beginChangeSequence);
writer.Write(LastState, beginChangeSequence, i - beginChangeSequence + 1);
inChangeSequence = false;
continue;
}
if (CurrentState[i] == LastState[i])
{
writer.Write((byte)(i - beginChangeSequence));
writer.Write((ushort)beginChangeSequence);
writer.Write(LastState, beginChangeSequence, i - beginChangeSequence);
inChangeSequence = false;
}
}
LastState = CurrentState;
ms.Position = 0;
RewindBuf.Push(ms);
}
/// <summary>
/// Builds a delta for states that are > 64K in size.
/// </summary>
private void CaptureRewindStateLarge()
{
byte[] CurrentState = Global.Emulator.SaveStateBinary();
int beginChangeSequence = -1;
bool inChangeSequence = false;
var ms = new MemoryStream();
var writer = new BinaryWriter(ms);
for (int i = 0; i < CurrentState.Length; i++)
{
if (inChangeSequence == false)
{
if (i >= LastState.Length)
continue;
if (CurrentState[i] == LastState[i])
continue;
inChangeSequence = true;
beginChangeSequence = i;
continue;
}
if (i - beginChangeSequence == 254 || i == CurrentState.Length - 1)
{
writer.Write((byte)(i - beginChangeSequence + 1));
writer.Write(beginChangeSequence);
writer.Write(LastState, beginChangeSequence, i - beginChangeSequence + 1);
inChangeSequence = false;
continue;
}
if (CurrentState[i] == LastState[i])
{
writer.Write((byte)(i - beginChangeSequence));
writer.Write(beginChangeSequence);
writer.Write(LastState, beginChangeSequence, i - beginChangeSequence);
inChangeSequence = false;
}
}
LastState = CurrentState;
ms.Position = 0;
RewindBuf.Push(ms);
}
private void Rewind64K()
{
var ms = RewindBuf.Pop();
var reader = new BinaryReader(ms);
var output = new MemoryStream(LastState);
while (ms.Position < ms.Length - 1)
{
byte len = reader.ReadByte();
ushort offset = reader.ReadUInt16();
output.Position = offset;
output.Write(ms.GetBuffer(), (int)ms.Position, len);
ms.Position += len;
}
reader.Close();
output.Position = 0;
Global.Emulator.LoadStateBinary(new BinaryReader(output));
}
private void RewindLarge()
{
var ms = RewindBuf.Pop();
var reader = new BinaryReader(ms);
var output = new MemoryStream(LastState);
while (ms.Position < ms.Length - 1)
{
byte len = reader.ReadByte();
int offset = reader.ReadInt32();
output.Position = offset;
output.Write(ms.GetBuffer(), (int)ms.Position, len);
ms.Position += len;
}
reader.Close();
output.Position = 0;
Global.Emulator.LoadStateBinary(new BinaryReader(output));
}
private void Rewind(int frames)
{
for (int i = 0; i < frames; i++)
{
if (RewindBuf.Count == 0)
return;
if (LastState.Length < 0x10000)
Rewind64K();
else
RewindLarge();
}
}
public void ResetRewindBuffer()
{
RewindBuf.Clear();
LastState = null;
}
}
}