diff --git a/src/BizHawk.Emulation.Common/Base Implementations/MemoryDomainStream.cs b/src/BizHawk.Emulation.Common/Base Implementations/MemoryDomainStream.cs new file mode 100644 index 0000000000..123fc600d2 --- /dev/null +++ b/src/BizHawk.Emulation.Common/Base Implementations/MemoryDomainStream.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; +using BizHawk.Common; + +namespace BizHawk.Emulation.Common +{ + public class MemoryDomainStream : Stream + { + public MemoryDomainStream(MemoryDomain d) + { + _d = d; + } + private readonly MemoryDomain _d; + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => _d.Writable; + + public override long Length => _d.Size; + + public override long Position { get; set; } + + public override void Flush() + { + } + + public override int ReadByte() + { + if (Position >= Length) + return -1; + return _d.PeekByte(Position++); + } + + public override void WriteByte(byte value) + { + if (Position >= Length) + throw new IOException("Can't resize stream"); + _d.PokeByte(Position++, value); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (offset < 0 || offset + count > buffer.Length) + throw new ArgumentOutOfRangeException("offset"); + count = (int)Math.Min(count, Length - Position); + if (count == 0) + return 0; + // TODO: Memory domain doesn't have the overload we need :( + var poop = new byte[count]; + // TODO: Range has the wrong end value + _d.BulkPeekByte(new MutableRange(Position, Position + count - 1), poop); + Array.Copy(poop, 0, buffer, offset, count); + Position += count; + return count; + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (offset < 0 || offset + count > buffer.Length) + throw new ArgumentOutOfRangeException("offset"); + for (var i = offset; i < offset + count; i++) + _d.PokeByte(Position++, buffer[i]); + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + Position = offset; + break; + case SeekOrigin.Current: + Position += offset; + break; + case SeekOrigin.End: + Position = Length + offset; + break; + default: + throw new ArgumentOutOfRangeException("origin"); + } + return Position; + } + + public override void SetLength(long value) + { + throw new NotSupportedException("Stream cannot be resized"); + } + } +} diff --git a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs index a3d5fc581f..fe5c038e1b 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs @@ -64,18 +64,21 @@ namespace BizHawk.Emulation.Cores.Waterbox _core.GetMemoryAreas(areas); _memoryAreas = areas.Where(a => a.Data != IntPtr.Zero && a.Size != 0) .ToArray(); - _saveramAreas = _memoryAreas.Where(a => (a.Flags & LibWaterboxCore.MemoryDomainFlags.Saverammable) != 0) + + var memoryDomains = _memoryAreas.Select(a => WaterboxMemoryDomain.Create(a, _exe)).ToList(); + var primaryDomain = memoryDomains + .Where(md => md.Definition.Flags.HasFlag(LibWaterboxCore.MemoryDomainFlags.Primary)) + .Single(); + + var mdl = new MemoryDomainList(memoryDomains.Cast().ToList()); + mdl.MainMemory = primaryDomain; + _serviceProvider.Register(mdl); + + _saveramAreas = memoryDomains + .Where(md => md.Definition.Flags.HasFlag(LibWaterboxCore.MemoryDomainFlags.Saverammable)) .ToArray(); _saveramSize = (int)_saveramAreas.Sum(a => a.Size); - var memoryDomains = _memoryAreas.Select(a => WaterboxMemoryDomain.Create(a, _exe)); - var primaryIndex = _memoryAreas - .Select((a, i) => new { a, i }) - .Single(a => (a.a.Flags & LibWaterboxCore.MemoryDomainFlags.Primary) != 0).i; - var mdl = new MemoryDomainList(memoryDomains.Cast().ToList()); - mdl.MainMemory = mdl[primaryIndex]; - _serviceProvider.Register(mdl); - var sr = _core as ICustomSaveram; if (sr != null) _serviceProvider.Register(new CustomSaverammer(sr)); // override the default implementation @@ -110,7 +113,7 @@ namespace BizHawk.Emulation.Cores.Waterbox } } - private LibWaterboxCore.MemoryArea[] _saveramAreas; + private WaterboxMemoryDomain[] _saveramAreas; private int _saveramSize; public unsafe bool SaveRamModified @@ -119,18 +122,29 @@ namespace BizHawk.Emulation.Cores.Waterbox { if (_saveramSize == 0) return false; + var buff = new byte[4096]; using (_exe.EnterExit()) { - foreach (var area in _saveramAreas) + fixed(byte* bp = buff) { - int* p = (int*)area.Data; - int* pend = p + area.Size / sizeof(int); - int cmp = (area.Flags & LibWaterboxCore.MemoryDomainFlags.OneFilled) != 0 ? -1 : 0; - - while (p < pend) + foreach (var area in _saveramAreas) { - if (*p++ != cmp) - return true; + var stream = new MemoryDomainStream(area); + int cmp = (area.Definition.Flags & LibWaterboxCore.MemoryDomainFlags.OneFilled) != 0 ? -1 : 0; + while (true) + { + int nread = stream.Read(buff, 0, 4096); + if (nread == 0) + break; + + int* p = (int*)bp; + int* pend = p + nread / sizeof(int); + while (p < pend) + { + if (*p++ != cmp) + return true; + } + } } } } @@ -145,11 +159,10 @@ namespace BizHawk.Emulation.Cores.Waterbox using (_exe.EnterExit()) { var ret = new byte[_saveramSize]; - var offs = 0; + var dest = new MemoryStream(ret, true); foreach (var area in _saveramAreas) { - Marshal.Copy(area.Data, ret, offs, (int)area.Size); - offs += (int)area.Size; + new MemoryDomainStream(area).CopyTo(dest); } return ret; } @@ -163,11 +176,10 @@ namespace BizHawk.Emulation.Cores.Waterbox throw new InvalidOperationException("Saveram size mismatch"); using (_exe.EnterExit()) { - var offs = 0; + var source = new MemoryStream(data, false); foreach (var area in _saveramAreas) { - Marshal.Copy(data, offs, area.Data, (int)area.Size); - offs += (int)area.Size; + WaterboxUtils.CopySome(source, new MemoryDomainStream(area), area.Size); } } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs index 8c032e4426..f81fe9d164 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs @@ -15,10 +15,12 @@ namespace BizHawk.Emulation.Cores.Waterbox protected readonly IMonitor _monitor; protected readonly long _addressMangler; - public static MemoryDomain Create(MemoryArea m, IMonitor monitor) + public MemoryArea Definition { get; } + + public static WaterboxMemoryDomain Create(MemoryArea m, IMonitor monitor) { return m.Flags.HasFlag(MemoryDomainFlags.FunctionHook) - ? (MemoryDomain)new WaterboxMemoryDomainFunc(m, monitor) + ? (WaterboxMemoryDomain)new WaterboxMemoryDomainFunc(m, monitor) : new WaterboxMemoryDomainPointer(m, monitor); } @@ -48,6 +50,7 @@ namespace BizHawk.Emulation.Cores.Waterbox { _addressMangler = 0; } + Definition = m; } }