147 lines
4.2 KiB
C#
147 lines
4.2 KiB
C#
using BizHawk.Common.BizInvoke;
|
|
using BizHawk.Emulation.Common;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace BizHawk.Emulation.Cores.Waterbox
|
|
{
|
|
/// <summary>
|
|
/// a simple grow-only fixed max size heap
|
|
/// </summary>
|
|
internal sealed class Heap : IBinaryStateable, IDisposable
|
|
{
|
|
public MemoryBlock Memory { get; private set; }
|
|
/// <summary>
|
|
/// name, used in identifying errors
|
|
/// </summary>
|
|
public string Name { get; private set; }
|
|
/// <summary>
|
|
/// total number of bytes used
|
|
/// </summary>
|
|
public ulong Used { get; private set; }
|
|
|
|
/// <summary>
|
|
/// true if the heap has been sealed, preventing further changes
|
|
/// </summary>
|
|
public bool Sealed { get; private set; }
|
|
|
|
private byte[] _hash;
|
|
|
|
public Heap(ulong start, ulong size, string name)
|
|
{
|
|
Memory = new MemoryBlock(start, size);
|
|
Used = 0;
|
|
Name = name;
|
|
Console.WriteLine("Created heap `{1}` at {0:x16}:{2:x16}", start, name, start + size);
|
|
}
|
|
|
|
private ulong EnsureAlignment(int align)
|
|
{
|
|
if (align > 1)
|
|
{
|
|
ulong newused = ((Used - 1) | (ulong)(align - 1)) + 1;
|
|
if (newused > Memory.Size)
|
|
{
|
|
throw new InvalidOperationException(string.Format("Failed to meet alignment {0} on heap {1}", align, Name));
|
|
}
|
|
return newused;
|
|
}
|
|
return Used;
|
|
}
|
|
|
|
public ulong Allocate(ulong size, int align)
|
|
{
|
|
if (Sealed)
|
|
throw new InvalidOperationException(string.Format("Attempt made to allocate from sealed heap {0}", Name));
|
|
|
|
ulong allocstart = EnsureAlignment(align);
|
|
ulong newused = allocstart + size;
|
|
if (newused > Memory.Size)
|
|
{
|
|
throw new InvalidOperationException(string.Format("Failed to allocate {0} bytes from heap {1}", size, Name));
|
|
}
|
|
ulong ret = Memory.Start + allocstart;
|
|
Memory.Protect(Memory.Start + Used, newused - Used, MemoryBlock.Protection.RW);
|
|
Used = newused;
|
|
Console.WriteLine($"Allocated {size} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
|
|
return ret;
|
|
}
|
|
|
|
public void Seal()
|
|
{
|
|
if (!Sealed)
|
|
{
|
|
Memory.Protect(Memory.Start, Used, MemoryBlock.Protection.R);
|
|
_hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false));
|
|
Sealed = true;
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException(string.Format("Attempt to reseal heap {0}", Name));
|
|
}
|
|
}
|
|
|
|
public void SaveStateBinary(BinaryWriter bw)
|
|
{
|
|
bw.Write(Name);
|
|
bw.Write(Used);
|
|
if (!Sealed)
|
|
{
|
|
bw.Write(Memory.XorHash);
|
|
var ms = Memory.GetXorStream(Memory.Start, WaterboxUtils.AlignUp(Used), false);
|
|
ms.CopyTo(bw.BaseStream);
|
|
}
|
|
else
|
|
{
|
|
bw.Write(_hash);
|
|
}
|
|
}
|
|
|
|
public void LoadStateBinary(BinaryReader br)
|
|
{
|
|
var name = br.ReadString();
|
|
if (name != Name)
|
|
// probable cause: internal error
|
|
throw new InvalidOperationException(string.Format("Name did not match for heap {0}", Name));
|
|
var used = br.ReadUInt64();
|
|
if (used > Memory.Size)
|
|
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 with the same SyncSettings?", Name));
|
|
}
|
|
var usedAligned = WaterboxUtils.AlignUp(used);
|
|
|
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.None);
|
|
Memory.Protect(Memory.Start, used, MemoryBlock.Protection.RW);
|
|
var ms = Memory.GetXorStream(Memory.Start, usedAligned, true);
|
|
WaterboxUtils.CopySome(br.BaseStream, ms, (long)usedAligned);
|
|
Used = used;
|
|
}
|
|
else
|
|
{
|
|
var hash = br.ReadBytes(_hash.Length);
|
|
if (!hash.SequenceEqual(_hash))
|
|
{
|
|
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom with the same SyncSettings?", Name));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Memory != null)
|
|
{
|
|
Memory.Dispose();
|
|
Memory = null;
|
|
}
|
|
}
|
|
}
|
|
}
|