reorg TasStateManager file

This commit is contained in:
adelikat 2019-06-15 16:13:16 -05:00
parent eaef336492
commit e89bb6c276
2 changed files with 142 additions and 140 deletions

View File

@ -7,16 +7,29 @@ namespace BizHawk.Client.Common
public interface IStateManager
{
// byte[] this[int frame] { get; } // TODO: I had it refactored to this back in the day
/// <summary>
/// Retrieves the savestate for the given frame,
/// If this frame does not have a state currently, will return an empty array
/// </summary>
/// <returns>A savestate for the given frame or an empty array if there isn't one</returns>
KeyValuePair<int, byte[]> this[int frame] { get; }
TasStateManagerSettings Settings { get; set; }
Action<int> InvalidateCallback { set; }
/// <summary>
/// Requests that the current emulator state be captured
/// Unless force is true, the state may or may not be captured depending on the logic employed by "green-zone" management
/// </summary>
void Capture(bool force = false);
bool HasState(int frame);
/// <summary>
/// Clears out all savestates after the given frame number
/// </summary>
bool Invalidate(int frame);
void Clear();
@ -37,8 +50,14 @@ namespace BizHawk.Client.Common
void UpdateStateFrequency();
/// <summary>
/// Returns index of the state right above the given frame
/// </summary>
int GetStateIndexByFrame(int frame);
/// <summary>
/// Returns frame of the state at the given index
/// </summary>
int GetStateFrameByIndex(int index);
bool Remove(int frame);

View File

@ -15,6 +15,9 @@ namespace BizHawk.Client.Common
/// </summary>
public class TasStateManager : IStateManager
{
private const int MinFrequency = 1;
private const int MaxFrequency = 16;
// TODO: pass this in, and find a solution to a stale reference (this is instantiated BEFORE a new core instance is made, making this one stale if it is simply set in the constructor
private IStatable Core => Global.Emulator.AsStatable();
private readonly StateManagerDecay _decay;
@ -23,9 +26,9 @@ namespace BizHawk.Client.Common
private readonly SortedList<int, StateManagerState> _states;
private readonly ulong _expectedStateSize;
private ulong _used;
private int _stateFrequency;
private readonly int _minFrequency = 1;
private readonly int _maxFrequency = 16;
private int MaxStates => (int)(Settings.Cap / _expectedStateSize) +
(int)((ulong)Settings.DiskCapacitymb * 1024 * 1024 / _expectedStateSize);
private int FileStateGap => 1 << Settings.FileStateGap;
@ -55,22 +58,8 @@ namespace BizHawk.Client.Common
public Action<int> InvalidateCallback { get; set; }
public void UpdateStateFrequency()
{
_stateFrequency = ((int)_expectedStateSize / Settings.MemStateGapDivider / 1024)
.Clamp(_minFrequency, _maxFrequency);
_decay.UpdateSettings(MaxStates, _stateFrequency, 4);
LimitStateCount();
}
public TasStateManagerSettings Settings { get; set; }
/// <summary>
/// Retrieves the savestate for the given frame,
/// If this frame does not have a state currently, will return an empty array
/// </summary>
/// <returns>A savestate for the given frame or an empty array if there isn't one</returns>
public KeyValuePair<int, byte[]> this[int frame]
{
get
@ -89,6 +78,12 @@ namespace BizHawk.Client.Common
}
}
public int Count => _states.Count;
public int Last => _states.Count > 0
? _states.Last().Key
: 0;
private byte[] InitialState
{
get
@ -102,10 +97,25 @@ namespace BizHawk.Client.Common
}
}
/// <summary>
/// Requests that the current emulator state be captured
/// Unless force is true, the state may or may not be captured depending on the logic employed by "green-zone" management
/// </summary>
public bool Any()
{
if (_movie.StartsFromSavestate)
{
return _states.Count > 0;
}
return _states.Count > 1;
}
public void UpdateStateFrequency()
{
_stateFrequency = ((int)_expectedStateSize / Settings.MemStateGapDivider / 1024)
.Clamp(MinFrequency, MaxFrequency);
_decay.UpdateSettings(MaxStates, _stateFrequency, 4);
LimitStateCount();
}
public void Capture(bool force = false)
{
bool shouldCapture;
@ -138,21 +148,18 @@ namespace BizHawk.Client.Common
}
}
internal void SetState(int frame, byte[] state, bool skipRemoval = true)
public void Clear()
{
if (!skipRemoval) // skipRemoval: false only when capturing new states
if (_states.Any())
{
LimitStateCount(); // Remove before adding so this state won't be removed.
}
if (_states.ContainsKey(frame))
{
_states[frame].State = state;
}
else
{
_used += (ulong)state.Length;
_states.Add(frame, new StateManagerState(state, frame));
var tempState = _states.Values;
var power = tempState[0].Frame == 0
? _states.Values.First(s => s.Frame == 0)
: _states.Values[0];
_states.Clear();
SetState(0, power.State);
_used = (ulong)power.State.Length;
}
}
@ -166,9 +173,6 @@ namespace BizHawk.Client.Common
return _states.ContainsKey(frame);
}
/// <summary>
/// Clears out all savestates after the given frame number
/// </summary>
public bool Invalidate(int frame)
{
bool anyInvalidated = false;
@ -222,6 +226,89 @@ namespace BizHawk.Client.Common
return true;
}
// Map:
// 4 bytes - total savestate count
// [Foreach state]
// 4 bytes - frame
// 4 bytes - length of savestate
// 0 - n savestate
public void Save(BinaryWriter bw)
{
List<int> noSave = ExcludeStates();
bw.Write(_states.Count - noSave.Count);
for (int i = 0; i < _states.Count; i++)
{
if (noSave.Contains(i))
{
continue;
}
bw.Write(_states.Keys[i]);
bw.Write(_states.Values[i].Length);
bw.Write(_states.Values[i].State);
}
}
public void Load(BinaryReader br)
{
_states.Clear();
try
{
int nstates = br.ReadInt32();
for (int i = 0; i < nstates; i++)
{
int frame = br.ReadInt32();
int len = br.ReadInt32();
byte[] data = br.ReadBytes(len);
// whether we should allow state removal check here is an interesting question
// nothing was edited yet, so it might make sense to show the project untouched first
SetState(frame, data);
}
}
catch (EndOfStreamException)
{
}
}
public KeyValuePair<int, byte[]> GetStateClosestToFrame(int frame)
{
var s = _states.LastOrDefault(state => state.Key < frame);
return this[s.Key];
}
public int GetStateIndexByFrame(int frame)
{
return _states.IndexOfKey(GetStateClosestToFrame(frame).Key);
}
public int GetStateFrameByIndex(int index)
{
return _states.Keys[index];
}
private void SetState(int frame, byte[] state, bool skipRemoval = true)
{
if (!skipRemoval) // skipRemoval: false only when capturing new states
{
LimitStateCount(); // Remove before adding so this state won't be removed.
}
if (_states.ContainsKey(frame))
{
_states[frame].State = state;
}
else
{
_used += (ulong)state.Length;
_states.Add(frame, new StateManagerState(state, frame));
}
}
// Deletes states to follow the state storage size limits.
// Used after changing the settings too.
private void LimitStateCount()
@ -290,109 +377,5 @@ namespace BizHawk.Client.Common
return ret;
}
public void Clear()
{
if (_states.Any())
{
var tempState = _states.Values;
var power = tempState[0].Frame == 0
? _states.Values.First(s => s.Frame == 0)
: _states.Values[0];
_states.Clear();
SetState(0, power.State);
_used = (ulong)power.State.Length;
}
}
// Map:
// 4 bytes - total savestate count
// [Foreach state]
// 4 bytes - frame
// 4 bytes - length of savestate
// 0 - n savestate
public void Save(BinaryWriter bw)
{
List<int> noSave = ExcludeStates();
bw.Write(_states.Count - noSave.Count);
for (int i = 0; i < _states.Count; i++)
{
if (noSave.Contains(i))
{
continue;
}
bw.Write(_states.Keys[i]);
bw.Write(_states.Values[i].Length);
bw.Write(_states.Values[i].State);
}
}
public void Load(BinaryReader br)
{
_states.Clear();
try
{
int nstates = br.ReadInt32();
for (int i = 0; i < nstates; i++)
{
int frame = br.ReadInt32();
int len = br.ReadInt32();
byte[] data = br.ReadBytes(len);
// whether we should allow state removal check here is an interesting question
// nothing was edited yet, so it might make sense to show the project untouched first
SetState(frame, data);
}
}
catch (EndOfStreamException)
{
}
}
public KeyValuePair<int, byte[]> GetStateClosestToFrame(int frame)
{
var s = _states.LastOrDefault(state => state.Key < frame);
return this[s.Key];
}
/// <summary>
/// Returns index of the state right above the given frame
/// </summary>
public int GetStateIndexByFrame(int frame)
{
return _states.IndexOfKey(GetStateClosestToFrame(frame).Key);
}
/// <summary>
/// Returns frame of the state at the given index
/// </summary>
public int GetStateFrameByIndex(int index)
{
return _states.Keys[index];
}
private ulong _used;
public int Count => _states.Count;
public bool Any()
{
if (_movie.StartsFromSavestate)
{
return _states.Count > 0;
}
return _states.Count > 1;
}
public int Last => _states.Count > 0
? _states.Last().Key
: 0;
}
}