Add support for multiple state manager types.
This commit is contained in:
parent
62ddf36289
commit
15d93f6eb8
|
@ -8,7 +8,7 @@
|
|||
public int MovieCompressionLevel { get; }
|
||||
public bool VBAStyleMovieLoadState { get; }
|
||||
public bool PlaySoundOnMovieEnd { get; set; }
|
||||
PagedStateManager.PagedSettings DefaultTasStateManagerSettings { get; }
|
||||
IStateManagerSettings DefaultTasStateManagerSettings { get; }
|
||||
}
|
||||
|
||||
public class MovieConfig : IMovieConfig
|
||||
|
@ -20,6 +20,6 @@
|
|||
public bool VBAStyleMovieLoadState { get; set; }
|
||||
public bool PlaySoundOnMovieEnd { get; set; }
|
||||
|
||||
public PagedStateManager.PagedSettings DefaultTasStateManagerSettings { get; set; } = new();
|
||||
public IStateManagerSettings DefaultTasStateManagerSettings { get; set; } = new PagedStateManager.PagedSettings();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace BizHawk.Client.Common
|
|||
bool BindMarkersToInput { get; set; }
|
||||
|
||||
IMovieChangeLog ChangeLog { get; }
|
||||
IStateManager<PagedStateManager.PagedSettings> TasStateManager { get; set; }
|
||||
IStateManager TasStateManager { get; set; }
|
||||
Func<string> InputRollSettingsForSave { get; set; }
|
||||
string InputRollSettings { get; }
|
||||
ITasMovieRecord this[int index] { get; }
|
||||
|
|
|
@ -4,9 +4,9 @@ using BizHawk.Emulation.Common;
|
|||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public interface IStateManager<TSettings> : IDisposable
|
||||
public interface IStateManager : IDisposable
|
||||
{
|
||||
TSettings Settings { get; }
|
||||
IStateManagerSettings Settings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Requests that the current emulator state be captured
|
||||
|
@ -44,8 +44,9 @@ namespace BizHawk.Client.Common
|
|||
|
||||
/// <summary>
|
||||
/// Updates the internal state saving logic settings
|
||||
/// May create a new state manager
|
||||
/// </summary>
|
||||
IStateManager<TSettings> UpdateSettings(TSettings settings, bool keepOldStates = false);
|
||||
IStateManager UpdateSettings(IStateManagerSettings settings, bool keepOldStates = false);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the current state of the instance for persisting to disk
|
||||
|
@ -58,7 +59,7 @@ namespace BizHawk.Client.Common
|
|||
void LoadStateHistory(BinaryReader br);
|
||||
|
||||
/// <summary>
|
||||
/// Enables the instance to be used. An instance of <see cref="IStateManager{T}"/> should not
|
||||
/// Enables the instance to be used. An instance of <see cref="IStateManager"/> should not
|
||||
/// be useable until this method is called
|
||||
/// </summary>
|
||||
void Engage(byte[] frameZeroState);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public interface IStateManagerSettings
|
||||
{
|
||||
IStateManager CreateManager(Func<int, bool> reserveCallback);
|
||||
|
||||
IStateManagerSettings Clone();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.IO;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
internal class StatableStream : IStatable
|
||||
{
|
||||
public bool AvoidRewind => false;
|
||||
public void LoadStateBinary(BinaryReader reader) => throw new NotImplementedException();
|
||||
|
||||
private Stream _stream;
|
||||
private int _length;
|
||||
public StatableStream(Stream stream, int length)
|
||||
{
|
||||
_stream = stream;
|
||||
_length = length;
|
||||
}
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
int copied = 0;
|
||||
const int bufferSize = 81920; // It's the default of CopyTo's buffer size
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
while (copied < _length - bufferSize)
|
||||
{
|
||||
if (_stream.Read(buffer, 0, bufferSize) != bufferSize)
|
||||
throw new Exception("Unexpected end of stream.");
|
||||
writer.Write(buffer);
|
||||
copied += bufferSize;
|
||||
}
|
||||
int remaining = _length - copied;
|
||||
if (_stream.Read(buffer, 0, remaining) != remaining)
|
||||
throw new Exception("Unexpected end of stream.");
|
||||
writer.Write(buffer, 0, remaining);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -135,17 +135,20 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
});
|
||||
|
||||
PagedStateManager.PagedSettings settings = new();
|
||||
IStateManagerSettings settings = Session.Settings.DefaultTasStateManagerSettings;
|
||||
bl.GetLump(BinaryStateLump.StateHistorySettings, abort: false, tr =>
|
||||
{
|
||||
var json = tr.ReadToEnd();
|
||||
try
|
||||
{
|
||||
settings = JsonConvert.DeserializeObject<PagedStateManager.PagedSettings>(json);
|
||||
settings = JsonConvert.DeserializeObject<IStateManagerSettings>(json, new JsonSerializerSettings()
|
||||
{
|
||||
TypeNameHandling = TypeNameHandling.Objects,
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing, and use default settings instead
|
||||
settings = Session.Settings.DefaultTasStateManagerSettings;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -155,7 +158,7 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
try
|
||||
{
|
||||
TasStateManager = new PagedStateManager(settings, IsReserved);
|
||||
TasStateManager = settings.CreateManager(IsReserved);
|
||||
TasStateManager.LoadStateHistory(br);
|
||||
}
|
||||
catch
|
||||
|
@ -172,13 +175,11 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
try
|
||||
{
|
||||
TasStateManager = new PagedStateManager(settings, IsReserved);
|
||||
TasStateManager = settings.CreateManager(IsReserved);
|
||||
}
|
||||
catch
|
||||
{
|
||||
TasStateManager = new PagedStateManager(
|
||||
Session.Settings.DefaultTasStateManagerSettings,
|
||||
IsReserved);
|
||||
TasStateManager = Session.Settings.DefaultTasStateManagerSettings.CreateManager(IsReserved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
_inputPollable = emulator.AsInputPollable();
|
||||
|
||||
TasStateManager ??= new PagedStateManager(Session.Settings.DefaultTasStateManagerSettings, IsReserved);
|
||||
TasStateManager ??= Session.Settings.DefaultTasStateManagerSettings.CreateManager(IsReserved);
|
||||
if (StartsFromSavestate)
|
||||
{
|
||||
TasStateManager.Engage(BinarySavestate);
|
||||
|
@ -81,7 +81,7 @@ namespace BizHawk.Client.Common
|
|||
public TasLagLog LagLog { get; } = new TasLagLog();
|
||||
|
||||
public override string PreferredExtension => Extension;
|
||||
public IStateManager<PagedStateManager.PagedSettings> TasStateManager { get; set; }
|
||||
public IStateManager TasStateManager { get; set; }
|
||||
|
||||
public Action<int> GreenzoneInvalidated { get; set; }
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ using BizHawk.Emulation.Common;
|
|||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class ZwinderStateManager : IStateManager<ZwinderStateManagerSettings>, IDisposable
|
||||
public class ZwinderStateManager : IStateManager, IDisposable
|
||||
{
|
||||
private static readonly byte[] NonState = Array.Empty<byte>();
|
||||
|
||||
|
@ -36,12 +36,6 @@ namespace BizHawk.Client.Common
|
|||
_reserveCallback = reserveCallback;
|
||||
}
|
||||
|
||||
/// <param name="reserveCallback">Called when deciding to evict a state for the given frame, if true is returned, the state will be reserved</param>
|
||||
public ZwinderStateManager(Func<int, bool> reserveCallback)
|
||||
: this(new ZwinderStateManagerSettings(), reserveCallback)
|
||||
{
|
||||
}
|
||||
|
||||
public void Engage(byte[] frameZeroState)
|
||||
{
|
||||
if (!_reserved.ContainsKey(0))
|
||||
|
@ -64,20 +58,37 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
|
||||
public ZwinderStateManagerSettings Settings { get; private set; }
|
||||
IStateManagerSettings IStateManager.Settings => Settings;
|
||||
|
||||
public ZwinderStateManager UpdateSettings(ZwinderStateManagerSettings settings, bool keepOldStates = false)
|
||||
public IStateManager UpdateSettings(IStateManagerSettings settings, bool keepOldStates = false)
|
||||
{
|
||||
bool makeNewReserved = Settings?.AncientStoreType != settings.AncientStoreType;
|
||||
Settings = settings;
|
||||
if (settings is not ZwinderStateManagerSettings zSettings)
|
||||
{
|
||||
IStateManager newManager = settings.CreateManager(_reserveCallback);
|
||||
newManager.Engage(GetStateClosestToFrame(0).Value.ReadAllBytes());
|
||||
if (keepOldStates)
|
||||
{
|
||||
foreach (int frame in StateCache)
|
||||
{
|
||||
Stream ss = GetStateClosestToFrame(frame).Value;
|
||||
newManager.Capture(frame, new StatableStream(ss, (int)ss.Length));
|
||||
}
|
||||
}
|
||||
Dispose();
|
||||
return newManager;
|
||||
}
|
||||
|
||||
_current = UpdateBuffer(_current, settings.Current(), keepOldStates);
|
||||
_recent = UpdateBuffer(_recent, settings.Recent(), keepOldStates);
|
||||
_gapFiller = UpdateBuffer(_gapFiller, settings.GapFiller(), keepOldStates);
|
||||
bool makeNewReserved = Settings?.AncientStoreType != zSettings.AncientStoreType;
|
||||
Settings = zSettings;
|
||||
|
||||
_current = UpdateBuffer(_current, zSettings.Current(), keepOldStates);
|
||||
_recent = UpdateBuffer(_recent, zSettings.Recent(), keepOldStates);
|
||||
_gapFiller = UpdateBuffer(_gapFiller, zSettings.GapFiller(), keepOldStates);
|
||||
|
||||
if (keepOldStates)
|
||||
{
|
||||
// For ancients, let's throw out states if doing so still satisfies the ancient state interval.
|
||||
if (settings.AncientStateInterval > _ancientInterval)
|
||||
if (zSettings.AncientStateInterval > _ancientInterval)
|
||||
{
|
||||
List<int> reservedFrames = _reserved.Keys.ToList();
|
||||
reservedFrames.Sort();
|
||||
|
@ -86,7 +97,7 @@ namespace BizHawk.Client.Common
|
|||
if (_reserveCallback(reservedFrames[i]))
|
||||
continue;
|
||||
|
||||
if (reservedFrames[i + 1] - reservedFrames[i - 1] <= settings.AncientStateInterval)
|
||||
if (reservedFrames[i + 1] - reservedFrames[i - 1] <= zSettings.AncientStateInterval)
|
||||
{
|
||||
EvictReserved(reservedFrames[i]);
|
||||
reservedFrames.RemoveAt(i);
|
||||
|
@ -113,12 +124,11 @@ namespace BizHawk.Client.Common
|
|||
if (makeNewReserved)
|
||||
RebuildReserved();
|
||||
|
||||
_ancientInterval = settings.AncientStateInterval;
|
||||
_ancientInterval = zSettings.AncientStateInterval;
|
||||
RebuildStateCache();
|
||||
|
||||
return this;
|
||||
}
|
||||
IStateManager<ZwinderStateManagerSettings> IStateManager<ZwinderStateManagerSettings>.UpdateSettings(ZwinderStateManagerSettings settings, bool keepOldStates) => UpdateSettings(settings, keepOldStates);
|
||||
|
||||
private void RebuildReserved()
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ using BizHawk.Common;
|
|||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class ZwinderStateManagerSettings
|
||||
public class ZwinderStateManagerSettings : IStateManagerSettings
|
||||
{
|
||||
public ZwinderStateManagerSettings() { }
|
||||
|
||||
|
@ -109,6 +109,13 @@ namespace BizHawk.Client.Common
|
|||
[Description("Where to keep the reserved states.")]
|
||||
public IRewindSettings.BackingStoreType AncientStoreType { get; set; } = IRewindSettings.BackingStoreType.Memory;
|
||||
|
||||
public IStateManager CreateManager(Func<int, bool> reserveCallback)
|
||||
{
|
||||
return new ZwinderStateManager(this, reserveCallback);
|
||||
}
|
||||
|
||||
public IStateManagerSettings Clone() => new ZwinderStateManagerSettings(this);
|
||||
|
||||
// Just to simplify some other code.
|
||||
public RewindConfig Current()
|
||||
{
|
||||
|
|
|
@ -26,10 +26,11 @@ namespace BizHawk.Client.Common
|
|||
/// 1. No copies, ever. States are deposited directly to, and read directly from, one giant buffer. (assuming no TempFile storage which is not currently implemented)
|
||||
/// 2. Support for arbitrary and changeable state sizes.
|
||||
/// </summary>
|
||||
public class PagedStateManager : IStateManager<PagedStateManager.PagedSettings>, IDisposable
|
||||
public class PagedStateManager : IStateManager, IDisposable
|
||||
{
|
||||
public PagedSettings Settings { get; private set; }
|
||||
public class PagedSettings
|
||||
IStateManagerSettings IStateManager.Settings => Settings;
|
||||
public class PagedSettings : IStateManagerSettings
|
||||
{
|
||||
// Instead of the user giving a set of memory limits, the user will give just one.
|
||||
// That will be the limit for ALL managed states combined.
|
||||
|
@ -95,6 +96,13 @@ namespace BizHawk.Client.Common
|
|||
|
||||
ForceSaveMarkerStates = other.ForceSaveMarkerStates;
|
||||
}
|
||||
|
||||
public IStateManager CreateManager(Func<int, bool> reserveCallback)
|
||||
{
|
||||
return new PagedStateManager(this, reserveCallback);
|
||||
}
|
||||
|
||||
public IStateManagerSettings Clone() => new PagedSettings(this);
|
||||
}
|
||||
|
||||
public int Count => _states.Count;
|
||||
|
@ -553,25 +561,25 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public PagedStateManager UpdateSettings(PagedSettings settings, bool keepOldStates = false)
|
||||
public IStateManager UpdateSettings(IStateManagerSettings settings, bool keepOldStates = false)
|
||||
{
|
||||
if (settings.TotalMemoryLimitMB != this.Settings.TotalMemoryLimitMB)
|
||||
if (settings is not PagedSettings pSettings || pSettings.TotalMemoryLimitMB != this.Settings.TotalMemoryLimitMB)
|
||||
{
|
||||
PagedStateManager newManager = new(settings, _reserveCallback);
|
||||
IStateManager newManager = settings.CreateManager(_reserveCallback);
|
||||
newManager.Engage(GetStateClosestToFrame(0).Value.ReadAllBytes());
|
||||
if (keepOldStates) foreach (StateInfo state in _states)
|
||||
{
|
||||
Stream s = GetStateClosestToFrame(state.Frame).Value;
|
||||
newManager.Capture(state.Frame, new StatableStream(s, (int) s.Length));
|
||||
}
|
||||
{
|
||||
Stream s = GetStateClosestToFrame(state.Frame).Value;
|
||||
newManager.Capture(state.Frame, new StatableStream(s, (int)s.Length));
|
||||
}
|
||||
|
||||
Dispose();
|
||||
return newManager;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool recaptureOld = settings.FramesBetweenOldStates > this.Settings.FramesBetweenOldStates;
|
||||
this.Settings = settings;
|
||||
bool recaptureOld = pSettings.FramesBetweenOldStates > this.Settings.FramesBetweenOldStates;
|
||||
this.Settings = pSettings;
|
||||
if (recaptureOld)
|
||||
{
|
||||
foreach (StateInfo state in _states)
|
||||
|
@ -586,7 +594,6 @@ namespace BizHawk.Client.Common
|
|||
return this;
|
||||
}
|
||||
}
|
||||
IStateManager<PagedSettings> IStateManager<PagedSettings>.UpdateSettings(PagedSettings settings, bool keepOldStates) => UpdateSettings(settings, keepOldStates);
|
||||
public void SaveStateHistory(BinaryWriter bw)
|
||||
{
|
||||
bw.Write((byte)2); // version
|
||||
|
@ -654,36 +661,5 @@ namespace BizHawk.Client.Common
|
|||
public StatableArray(byte[] array) => _array = array;
|
||||
public void SaveStateBinary(BinaryWriter writer) => writer.Write(_array);
|
||||
}
|
||||
|
||||
private class StatableStream: IStatable
|
||||
{
|
||||
public bool AvoidRewind => false;
|
||||
public void LoadStateBinary(BinaryReader reader) => throw new NotImplementedException();
|
||||
|
||||
private Stream _stream;
|
||||
private int _length;
|
||||
public StatableStream(Stream stream, int length)
|
||||
{
|
||||
_stream = stream;
|
||||
_length = length;
|
||||
}
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
int copied = 0;
|
||||
const int bufferSize = 81920; // It's the default of CopyTo's buffer size
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
while (copied < _length - bufferSize)
|
||||
{
|
||||
if (_stream.Read(buffer, 0, bufferSize) != bufferSize)
|
||||
throw new Exception("Unexpected end of stream.");
|
||||
writer.Write(buffer);
|
||||
copied += bufferSize;
|
||||
}
|
||||
int remaining = _length - copied;
|
||||
if (_stream.Read(buffer, 0, remaining) != remaining)
|
||||
throw new Exception("Unexpected end of stream.");
|
||||
writer.Write(buffer, 0, remaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
public partial class GreenzoneSettings : Form, IDialogParent
|
||||
{
|
||||
private readonly Action<PagedStateManager.PagedSettings, bool> _saveSettings;
|
||||
private PagedStateManager.PagedSettings _settings;
|
||||
private readonly Action<IStateManagerSettings, bool> _saveSettings;
|
||||
private IStateManagerSettings _settings;
|
||||
private readonly bool _isDefault;
|
||||
|
||||
public IDialogController DialogController { get; }
|
||||
|
||||
public GreenzoneSettings(IDialogController dialogController, PagedStateManager.PagedSettings settings, Action<PagedStateManager.PagedSettings, bool> saveSettings, bool isDefault)
|
||||
public GreenzoneSettings(IDialogController dialogController, IStateManagerSettings settings, Action<IStateManagerSettings, bool> saveSettings, bool isDefault)
|
||||
{
|
||||
DialogController = dialogController;
|
||||
InitializeComponent();
|
||||
|
|
|
@ -915,7 +915,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
using GreenzoneSettings form = new(
|
||||
DialogController,
|
||||
new PagedStateManager.PagedSettings(CurrentTasMovie.TasStateManager.Settings),
|
||||
CurrentTasMovie.TasStateManager.Settings.Clone(),
|
||||
(s, k) => { CurrentTasMovie.TasStateManager = CurrentTasMovie.TasStateManager.UpdateSettings(s, k); },
|
||||
false)
|
||||
{
|
||||
|
@ -955,7 +955,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
using GreenzoneSettings form = new(
|
||||
DialogController,
|
||||
new PagedStateManager.PagedSettings(Config.Movies.DefaultTasStateManagerSettings),
|
||||
Config.Movies.DefaultTasStateManagerSettings.Clone(),
|
||||
(s, k) => { Config.Movies.DefaultTasStateManagerSettings = s; },
|
||||
true)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue