Waterbox: Add XOR based savestates for GPGX64
This commit is contained in:
parent
3a4b6601d9
commit
37dc9908d0
|
@ -53,6 +53,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
|
|||
VsyncNumerator = 21281370;
|
||||
VsyncDenominator = 425568;
|
||||
}
|
||||
|
||||
_exe.Seal();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -89,7 +89,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
bw.Write(Used);
|
||||
if (!Sealed)
|
||||
{
|
||||
var ms = Memory.GetStream(Memory.Start, Used, false);
|
||||
bw.Write(Memory.XorHash);
|
||||
var ms = Memory.GetXorStream(Memory.Start, Used, false);
|
||||
ms.CopyTo(bw.BaseStream);
|
||||
}
|
||||
else
|
||||
|
@ -108,9 +109,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
throw new InvalidOperationException(string.Format("Heap {0} used {1} larger than available {2}", Name, used, Memory.Size));
|
||||
if (!Sealed)
|
||||
{
|
||||
var hash = br.ReadBytes(Memory.XorHash.Length);
|
||||
if (!hash.SequenceEqual(Memory.XorHash))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom?", Name));
|
||||
}
|
||||
|
||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.None);
|
||||
Memory.Protect(Memory.Start, used, MemoryBlock.Protection.RW);
|
||||
var ms = Memory.GetStream(Memory.Start, used, true);
|
||||
var ms = Memory.GetXorStream(Memory.Start, used, true);
|
||||
WaterboxUtils.CopySome(br.BaseStream, ms, (long)used);
|
||||
Used = used;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@ using System.IO;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Waterbox
|
||||
{
|
||||
// C# is annoying: arithmetic operators for native ints are not exposed.
|
||||
// So we store them as long/ulong instead in many places, and use these helpers
|
||||
// to convert to IntPtr when needed
|
||||
|
||||
public static class Z
|
||||
{
|
||||
public static IntPtr US(ulong l)
|
||||
|
@ -47,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
/// <summary>
|
||||
/// system page size
|
||||
/// </summary>
|
||||
public static int PageSize { get; private set;}
|
||||
public static int PageSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// bitshift corresponding to PageSize
|
||||
|
@ -68,7 +72,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
PageMask = (ulong)(PageSize - 1);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// true if addr is aligned
|
||||
/// </summary>
|
||||
|
@ -121,6 +125,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
/// </summary>
|
||||
private readonly Protection[] _pageData;
|
||||
|
||||
/// <summary>
|
||||
/// snapshot for XOR buffer
|
||||
/// </summary>
|
||||
private byte[] _snapshot;
|
||||
|
||||
public byte[] XorHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// get a page index within the block
|
||||
/// </summary>
|
||||
|
@ -216,13 +227,53 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
{
|
||||
if (start < Start)
|
||||
throw new ArgumentOutOfRangeException(nameof(start));
|
||||
|
||||
if (start + length > End)
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
|
||||
return new MemoryViewStream(!writer, writer, (long)start, (long)length, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get a stream that can be used to read or write from part of the block.
|
||||
/// both reads and writes will be XORed against an earlier recorded snapshot
|
||||
/// </summary>
|
||||
public Stream GetXorStream(ulong start, ulong length, bool writer)
|
||||
{
|
||||
if (start < Start)
|
||||
throw new ArgumentOutOfRangeException(nameof(start));
|
||||
if (start + length > End)
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
if (_snapshot == null)
|
||||
throw new InvalidOperationException("No snapshot taken!");
|
||||
|
||||
return new MemoryViewXorStream(!writer, writer, (long)start, (long)length, this, _snapshot, (long)(start - Start));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// take a snapshot of the entire memory block's contents, for use in GetXorStream
|
||||
/// </summary>
|
||||
public void SaveXorSnapshot()
|
||||
{
|
||||
if (_snapshot != null)
|
||||
throw new InvalidOperationException("Snapshot already taken");
|
||||
if (!Active)
|
||||
throw new InvalidOperationException("Not active");
|
||||
|
||||
// temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
|
||||
// that to complicate things
|
||||
Kernel32.MemoryProtection old;
|
||||
if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
|
||||
throw new InvalidOperationException("VirtualProtect() returned FALSE!");
|
||||
|
||||
_snapshot = new byte[Size];
|
||||
var ds = new MemoryStream(_snapshot, true);
|
||||
var ss = GetStream(Start, Size, false);
|
||||
ss.CopyTo(ds);
|
||||
XorHash = WaterboxUtils.Hash(_snapshot);
|
||||
|
||||
ProtectAll();
|
||||
}
|
||||
|
||||
private static Kernel32.MemoryProtection GetKernelMemoryProtectionValue(Protection prot)
|
||||
{
|
||||
Kernel32.MemoryProtection p;
|
||||
|
@ -338,24 +389,25 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
public override long Length { get { return _length; } }
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _pos; } set
|
||||
{
|
||||
get { return _pos; }
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > _length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
_pos = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (!_readable)
|
||||
throw new InvalidOperationException();
|
||||
if (count < 0 || count > buffer.Length)
|
||||
if (count < 0 || count + offset > buffer.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
EnsureNotDisposed();
|
||||
count = (int)Math.Min(count, _length - _pos);
|
||||
Marshal.Copy(Z.SS(_ptr + _pos), buffer, 0, count);
|
||||
Marshal.Copy(Z.SS(_ptr + _pos), buffer, offset, count);
|
||||
_pos += count;
|
||||
return count;
|
||||
}
|
||||
|
@ -389,15 +441,73 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
{
|
||||
if (!_writable)
|
||||
throw new InvalidOperationException();
|
||||
if (count < 0 || count > buffer.Length)
|
||||
if (count < 0 || count + offset > buffer.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
if (count > _length - _pos)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
EnsureNotDisposed();
|
||||
count = (int)Math.Min(count, _length - _pos);
|
||||
Marshal.Copy(buffer, 0, Z.SS(_ptr + _pos), count);
|
||||
Marshal.Copy(buffer, offset, Z.SS(_ptr + _pos), count);
|
||||
_pos += count;
|
||||
}
|
||||
}
|
||||
|
||||
private class MemoryViewXorStream : MemoryViewStream
|
||||
{
|
||||
public MemoryViewXorStream(bool readable, bool writable, long ptr, long length, MemoryBlock owner,
|
||||
byte[] initial, long offset)
|
||||
: base(readable, writable, ptr, length, owner)
|
||||
{
|
||||
_initial = initial;
|
||||
_offset = (int)offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the initial data to XOR against for both reading and writing
|
||||
/// </summary>
|
||||
private byte[] _initial;
|
||||
/// <summary>
|
||||
/// offset into the XOR data that this stream is representing
|
||||
/// </summary>
|
||||
private int _offset;
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int pos = (int)Position;
|
||||
count = base.Read(buffer, offset, count);
|
||||
XorTransform(_initial, _offset + pos, buffer, offset, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int pos = (int)Position;
|
||||
if (count < 0 || count + offset > buffer.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
if (count > Length - pos)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
// is mutating the buffer passed to Stream.Write kosher?
|
||||
XorTransform(_initial, _offset + pos, buffer, offset, count);
|
||||
base.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
private static unsafe void XorTransform(byte[] source, int sourceOffset, byte[] dest, int destOffset, int length)
|
||||
{
|
||||
// we don't do any bounds check because MemoryViewStream.Read and MemoryViewXorStream.Write already did it
|
||||
|
||||
// TODO: C compilers can make this pretty snappy, but can the C# jitter? Or do we need intrinsics
|
||||
fixed (byte* _s = source, _d = dest)
|
||||
{
|
||||
byte* s = _s + sourceOffset;
|
||||
byte* d = _d + destOffset;
|
||||
byte* sEnd = s + length;
|
||||
while (s < sEnd)
|
||||
{
|
||||
*d++ ^= *s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Kernel32
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
|
|
|
@ -519,6 +519,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
/// </summary>
|
||||
private readonly List<PeWrapper> _modules = new List<PeWrapper>();
|
||||
|
||||
/// <summary>
|
||||
/// all loaded heaps
|
||||
/// </summary>
|
||||
private readonly List<Heap> _heaps = new List<Heap>();
|
||||
|
||||
/// <summary>
|
||||
/// anything at all that needs to be disposed on finish
|
||||
/// </summary>
|
||||
|
@ -553,6 +558,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
if (saveStated)
|
||||
_savestateComponents.Add(heap);
|
||||
_disposeList.Add(heap);
|
||||
_heaps.Add(heap);
|
||||
return heap;
|
||||
}
|
||||
|
||||
|
@ -663,6 +669,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
using (this.EnterExit())
|
||||
{
|
||||
_sealedheap.Seal();
|
||||
foreach (var h in _heaps)
|
||||
h.Memory.SaveXorSnapshot();
|
||||
foreach (var pe in _modules)
|
||||
pe.Memory.SaveXorSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -292,6 +292,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
{
|
||||
bw.Write(MAGIC);
|
||||
bw.Write(_fileHash);
|
||||
bw.Write(Memory.XorHash);
|
||||
bw.Write(Start);
|
||||
|
||||
foreach (var s in _pe.ImageSectionHeaders)
|
||||
|
@ -302,7 +303,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
ulong start = Start + s.VirtualAddress;
|
||||
ulong length = s.VirtualSize;
|
||||
|
||||
var ms = Memory.GetStream(start, length, false);
|
||||
var ms = Memory.GetXorStream(start, length, false);
|
||||
bw.Write(length);
|
||||
ms.CopyTo(bw.BaseStream);
|
||||
}
|
||||
|
@ -314,6 +315,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
throw new InvalidOperationException("Magic not magic enough!");
|
||||
if (!br.ReadBytes(_fileHash.Length).SequenceEqual(_fileHash))
|
||||
throw new InvalidOperationException("Elf changed disguise!");
|
||||
if (!br.ReadBytes(Memory.XorHash.Length).SequenceEqual(Memory.XorHash))
|
||||
throw new InvalidOperationException("Elf is super slippery!");
|
||||
if (br.ReadUInt64() != Start)
|
||||
throw new InvalidOperationException("Trickys elves moved on you!");
|
||||
|
||||
|
@ -330,7 +333,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
if (br.ReadUInt64() != length)
|
||||
throw new InvalidOperationException("Unexpected section size for " + s.Name);
|
||||
|
||||
var ms = Memory.GetStream(start, length, true);
|
||||
var ms = Memory.GetXorStream(start, length, true);
|
||||
WaterboxUtils.CopySome(br.BaseStream, ms, (long)length);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue