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;
|
VsyncNumerator = 21281370;
|
||||||
VsyncDenominator = 425568;
|
VsyncDenominator = 425568;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_exe.Seal();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
@ -89,7 +89,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
bw.Write(Used);
|
bw.Write(Used);
|
||||||
if (!Sealed)
|
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);
|
ms.CopyTo(bw.BaseStream);
|
||||||
}
|
}
|
||||||
else
|
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));
|
throw new InvalidOperationException(string.Format("Heap {0} used {1} larger than available {2}", Name, used, Memory.Size));
|
||||||
if (!Sealed)
|
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, Memory.Size, MemoryBlock.Protection.None);
|
||||||
Memory.Protect(Memory.Start, used, MemoryBlock.Protection.RW);
|
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);
|
WaterboxUtils.CopySome(br.BaseStream, ms, (long)used);
|
||||||
Used = used;
|
Used = used;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@ using System.IO;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Waterbox
|
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 class Z
|
||||||
{
|
{
|
||||||
public static IntPtr US(ulong l)
|
public static IntPtr US(ulong l)
|
||||||
|
@ -121,6 +125,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Protection[] _pageData;
|
private readonly Protection[] _pageData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// snapshot for XOR buffer
|
||||||
|
/// </summary>
|
||||||
|
private byte[] _snapshot;
|
||||||
|
|
||||||
|
public byte[] XorHash { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// get a page index within the block
|
/// get a page index within the block
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -216,13 +227,53 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
if (start < Start)
|
if (start < Start)
|
||||||
throw new ArgumentOutOfRangeException(nameof(start));
|
throw new ArgumentOutOfRangeException(nameof(start));
|
||||||
|
|
||||||
if (start + length > End)
|
if (start + length > End)
|
||||||
throw new ArgumentOutOfRangeException(nameof(length));
|
throw new ArgumentOutOfRangeException(nameof(length));
|
||||||
|
|
||||||
return new MemoryViewStream(!writer, writer, (long)start, (long)length, this);
|
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)
|
private static Kernel32.MemoryProtection GetKernelMemoryProtectionValue(Protection prot)
|
||||||
{
|
{
|
||||||
Kernel32.MemoryProtection p;
|
Kernel32.MemoryProtection p;
|
||||||
|
@ -339,7 +390,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
public override long Position
|
public override long Position
|
||||||
{
|
{
|
||||||
get { return _pos; } set
|
get { return _pos; }
|
||||||
|
set
|
||||||
{
|
{
|
||||||
if (value < 0 || value > _length)
|
if (value < 0 || value > _length)
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
@ -351,11 +403,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
if (!_readable)
|
if (!_readable)
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
if (count < 0 || count > buffer.Length)
|
if (count < 0 || count + offset > buffer.Length)
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
EnsureNotDisposed();
|
EnsureNotDisposed();
|
||||||
count = (int)Math.Min(count, _length - _pos);
|
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;
|
_pos += count;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -389,15 +441,73 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
if (!_writable)
|
if (!_writable)
|
||||||
throw new InvalidOperationException();
|
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();
|
throw new ArgumentOutOfRangeException();
|
||||||
EnsureNotDisposed();
|
EnsureNotDisposed();
|
||||||
count = (int)Math.Min(count, _length - _pos);
|
Marshal.Copy(buffer, offset, Z.SS(_ptr + _pos), count);
|
||||||
Marshal.Copy(buffer, 0, Z.SS(_ptr + _pos), count);
|
|
||||||
_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
|
private static class Kernel32
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
|
|
@ -519,6 +519,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly List<PeWrapper> _modules = new List<PeWrapper>();
|
private readonly List<PeWrapper> _modules = new List<PeWrapper>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// all loaded heaps
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<Heap> _heaps = new List<Heap>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// anything at all that needs to be disposed on finish
|
/// anything at all that needs to be disposed on finish
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -553,6 +558,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
if (saveStated)
|
if (saveStated)
|
||||||
_savestateComponents.Add(heap);
|
_savestateComponents.Add(heap);
|
||||||
_disposeList.Add(heap);
|
_disposeList.Add(heap);
|
||||||
|
_heaps.Add(heap);
|
||||||
return heap;
|
return heap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,6 +669,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
using (this.EnterExit())
|
using (this.EnterExit())
|
||||||
{
|
{
|
||||||
_sealedheap.Seal();
|
_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(MAGIC);
|
||||||
bw.Write(_fileHash);
|
bw.Write(_fileHash);
|
||||||
|
bw.Write(Memory.XorHash);
|
||||||
bw.Write(Start);
|
bw.Write(Start);
|
||||||
|
|
||||||
foreach (var s in _pe.ImageSectionHeaders)
|
foreach (var s in _pe.ImageSectionHeaders)
|
||||||
|
@ -302,7 +303,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
ulong start = Start + s.VirtualAddress;
|
ulong start = Start + s.VirtualAddress;
|
||||||
ulong length = s.VirtualSize;
|
ulong length = s.VirtualSize;
|
||||||
|
|
||||||
var ms = Memory.GetStream(start, length, false);
|
var ms = Memory.GetXorStream(start, length, false);
|
||||||
bw.Write(length);
|
bw.Write(length);
|
||||||
ms.CopyTo(bw.BaseStream);
|
ms.CopyTo(bw.BaseStream);
|
||||||
}
|
}
|
||||||
|
@ -314,6 +315,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
throw new InvalidOperationException("Magic not magic enough!");
|
throw new InvalidOperationException("Magic not magic enough!");
|
||||||
if (!br.ReadBytes(_fileHash.Length).SequenceEqual(_fileHash))
|
if (!br.ReadBytes(_fileHash.Length).SequenceEqual(_fileHash))
|
||||||
throw new InvalidOperationException("Elf changed disguise!");
|
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)
|
if (br.ReadUInt64() != Start)
|
||||||
throw new InvalidOperationException("Trickys elves moved on you!");
|
throw new InvalidOperationException("Trickys elves moved on you!");
|
||||||
|
|
||||||
|
@ -330,7 +333,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
if (br.ReadUInt64() != length)
|
if (br.ReadUInt64() != length)
|
||||||
throw new InvalidOperationException("Unexpected section size for " + s.Name);
|
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);
|
WaterboxUtils.CopySome(br.BaseStream, ms, (long)length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue