diff --git a/src/BizHawk.Client.Common/config/RewindConfig.cs b/src/BizHawk.Client.Common/config/RewindConfig.cs index 017c6ac350..cf41109a75 100644 --- a/src/BizHawk.Client.Common/config/RewindConfig.cs +++ b/src/BizHawk.Client.Common/config/RewindConfig.cs @@ -2,26 +2,20 @@ { public interface IRewindSettings { - /// - /// Gets a value indicating whether or not to enable rewinding - /// - public bool Enabled { get; } - /// /// Gets a value indicating whether or not to compress savestates before storing them /// - public bool UseCompression { get; } - + bool UseCompression { get; } /// /// Max amount of buffer space to use in MB /// - public int BufferSize { get; } + int BufferSize { get; } /// /// Desired frame length (number of emulated frames you can go back before running out of buffer) /// - public int TargetFrameLength { get; } + int TargetFrameLength { get; } } public class RewindConfig : IRewindSettings diff --git a/src/BizHawk.Client.Common/rewind/Zwinder.cs b/src/BizHawk.Client.Common/rewind/Zwinder.cs index 4e615c8475..d073481ca6 100644 --- a/src/BizHawk.Client.Common/rewind/Zwinder.cs +++ b/src/BizHawk.Client.Common/rewind/Zwinder.cs @@ -130,11 +130,19 @@ namespace BizHawk.Client.Common return; var start = (_states[HeadStateIndex].Start + _states[HeadStateIndex].Size) & _sizeMask; - - var stream = new SaveStateStream( - _buffer, - start, - Size, _sizeMask); + var initialMaxSize = Count > 0 + ? (_states[_firstStateIndex].Start - start) & _sizeMask + : Size; + Func notifySizeReached = () => + { + if (Count == 0) + throw new IOException("A single state must not be larger than the buffer"); + _firstStateIndex = (_firstStateIndex + 1) & StateMask; + return Count > 0 + ? (_states[_firstStateIndex].Start - start) & _sizeMask + : Size; + }; + var stream = new SaveStateStream(_buffer, start, _sizeMask, initialMaxSize, notifySizeReached); if (_useCompression) { @@ -146,14 +154,9 @@ namespace BizHawk.Client.Common _stateSource.SaveStateBinary(new BinaryWriter(stream)); } - // invalidate states if we're at the state ringbuffer size limit, or if they were overridden in the byte buffer - var length = stream.Length; - while (Count == StateMask || Count > 0 && ((_states[_firstStateIndex].Start - start) & _sizeMask) < length) - _firstStateIndex = (_firstStateIndex + 1) & StateMask; - _states[_nextStateIndex].Frame = frame; _states[_nextStateIndex].Start = start; - _states[_nextStateIndex].Size = (int)length; + _states[_nextStateIndex].Size = (int)stream.Length; _nextStateIndex = (_nextStateIndex + 1) & StateMask; Console.WriteLine($"Size: {Size >> 20}MiB, Used: {Used >> 20}MiB, States: {Count}"); @@ -201,19 +204,35 @@ namespace BizHawk.Client.Common private class SaveStateStream : Stream { - public SaveStateStream(byte[] buffer, long offset, long maxSize, long mask) + /// + /// + /// + /// The ringbuffer to write into + /// Offset into the buffer to start writing (and treat as position 0 in the stream) + /// Buffer size mask, used to wrap values in the ringbuffer correctly + /// + /// If the stream will exceed this size, notifySizeReached must be called before clobbering any data + /// + /// + /// The callback that will be called when notifySize is about to be exceeded. Can either return a new larger notifySize, + /// or abort processing with an IOException. This must fail if size is going to exceed buffer.Length, as nothing else + /// is preventing that case. + /// + public SaveStateStream(byte[] buffer, long offset, long mask, long notifySize, Func notifySizeReached) { _buffer = buffer; _offset = offset; - _maxSize = maxSize; _mask = mask; + _notifySize = notifySize; + _notifySizeReached = notifySizeReached; } private readonly byte[] _buffer; private readonly long _offset; - private readonly long _maxSize; private readonly long _mask; private long _position; + private long _notifySize; + private readonly Func _notifySizeReached; public override bool CanRead => false; public override bool CanSeek => false; @@ -230,9 +249,10 @@ namespace BizHawk.Client.Common public override void Write(byte[] buffer, int offset, int count) { - long n = Math.Min(_maxSize - _position, count); - if (n != count) - throw new IOException("A single state cannot be bigger than the buffer!"); + long requestedSize = _position + count; + while (requestedSize > _notifySize) + _notifySize = _notifySizeReached(); + long n = count; if (n > 0) { var start = (_position + _offset) & _mask; @@ -256,14 +276,10 @@ namespace BizHawk.Client.Common public override void WriteByte(byte value) { - if (_position < _maxSize) - { - _buffer[(_position++ + _offset) & _mask] = value; - } - else - { - throw new IOException("A single state cannot be bigger than the buffer!"); - } + long requestedSize = _position + 1; + while (requestedSize > _notifySize) + _notifySize = _notifySizeReached(); + _buffer[(_position++ + _offset) & _mask] = value; } }