228 lines
5.8 KiB
C#
228 lines
5.8 KiB
C#
using System.IO;
|
|
|
|
namespace BizHawk.MultiClient
|
|
{
|
|
public partial class MainForm
|
|
{
|
|
MruStack<MemoryStream> RewindBuf = new MruStack<MemoryStream>(15000);
|
|
byte[] LastState;
|
|
bool RewindImpossible = false;
|
|
|
|
void CaptureRewindState()
|
|
{
|
|
if (RewindImpossible)
|
|
return;
|
|
|
|
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();
|
|
if (LastState.Length > 0x100000)
|
|
{
|
|
RewindImpossible = true;
|
|
LastState = null;
|
|
Global.OSD.AddMessage("Rewind Disabled: State too large.");
|
|
Global.OSD.AddMessage("See 'Arcade Card Rewind Hack' in Emulation->PC Engine options.");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Otherwise, it's not the first frame, so build a delta.
|
|
if (LastState.Length <= 0x10000)
|
|
CaptureRewindState64K();
|
|
else
|
|
CaptureRewindStateLarge();
|
|
}
|
|
|
|
// Builds a delta for states that are <= 64K in size.
|
|
void CaptureRewindState64K()
|
|
{
|
|
byte[] CurrentState = Global.Emulator.SaveStateBinary();
|
|
int beginChangeSequence = -1;
|
|
bool inChangeSequence = false;
|
|
var ms = new MemoryStream();
|
|
var writer = new BinaryWriter(ms);
|
|
if (CurrentState.Length != LastState.Length)
|
|
{
|
|
writer.Write(true); // full state
|
|
writer.Write(LastState);
|
|
}
|
|
else
|
|
{
|
|
writer.Write(false); // delta state
|
|
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);
|
|
}
|
|
|
|
// Builds a delta for states that are > 64K in size.
|
|
void CaptureRewindStateLarge()
|
|
{
|
|
byte[] CurrentState = Global.Emulator.SaveStateBinary();
|
|
int beginChangeSequence = -1;
|
|
bool inChangeSequence = false;
|
|
var ms = new MemoryStream();
|
|
var writer = new BinaryWriter(ms);
|
|
if (CurrentState.Length != LastState.Length)
|
|
{
|
|
writer.Write(true); // full state
|
|
writer.Write(LastState);
|
|
}
|
|
else
|
|
{
|
|
writer.Write(false); // delta state
|
|
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);
|
|
}
|
|
|
|
void Rewind64K()
|
|
{
|
|
var ms = RewindBuf.Pop();
|
|
var reader = new BinaryReader(ms);
|
|
bool fullstate = reader.ReadBoolean();
|
|
if (fullstate)
|
|
{
|
|
Global.Emulator.LoadStateBinary(reader);
|
|
}
|
|
else
|
|
{
|
|
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));
|
|
}
|
|
}
|
|
|
|
void RewindLarge()
|
|
{
|
|
var ms = RewindBuf.Pop();
|
|
var reader = new BinaryReader(ms);
|
|
bool fullstate = reader.ReadBoolean();
|
|
if (fullstate)
|
|
{
|
|
Global.Emulator.LoadStateBinary(reader);
|
|
}
|
|
else
|
|
{
|
|
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));
|
|
}
|
|
}
|
|
|
|
public void Rewind(int frames)
|
|
{
|
|
for (int i = 0; i < frames; i++)
|
|
{
|
|
if (RewindBuf.Count == 0 || (true == Global.MovieSession.Movie.Loaded && 0 == Global.MovieSession.Movie.Frames))
|
|
{
|
|
return;
|
|
}
|
|
if (LastState.Length < 0x10000)
|
|
{
|
|
Rewind64K();
|
|
}
|
|
else
|
|
{
|
|
RewindLarge();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ResetRewindBuffer()
|
|
{
|
|
RewindBuf.Clear();
|
|
RewindImpossible = false;
|
|
LastState = null;
|
|
}
|
|
|
|
public int RewindBufferCount()
|
|
{
|
|
return RewindBuf.Count;
|
|
}
|
|
}
|
|
}
|