reorg TasStateManager file
This commit is contained in:
parent
eaef336492
commit
e89bb6c276
|
@ -7,16 +7,29 @@ namespace BizHawk.Client.Common
|
||||||
public interface IStateManager
|
public interface IStateManager
|
||||||
{
|
{
|
||||||
// byte[] this[int frame] { get; } // TODO: I had it refactored to this back in the day
|
// 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; }
|
KeyValuePair<int, byte[]> this[int frame] { get; }
|
||||||
|
|
||||||
TasStateManagerSettings Settings { get; set; }
|
TasStateManagerSettings Settings { get; set; }
|
||||||
|
|
||||||
Action<int> InvalidateCallback { 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);
|
void Capture(bool force = false);
|
||||||
|
|
||||||
bool HasState(int frame);
|
bool HasState(int frame);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears out all savestates after the given frame number
|
||||||
|
/// </summary>
|
||||||
bool Invalidate(int frame);
|
bool Invalidate(int frame);
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
@ -37,8 +50,14 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
void UpdateStateFrequency();
|
void UpdateStateFrequency();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns index of the state right above the given frame
|
||||||
|
/// </summary>
|
||||||
int GetStateIndexByFrame(int frame);
|
int GetStateIndexByFrame(int frame);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns frame of the state at the given index
|
||||||
|
/// </summary>
|
||||||
int GetStateFrameByIndex(int index);
|
int GetStateFrameByIndex(int index);
|
||||||
|
|
||||||
bool Remove(int frame);
|
bool Remove(int frame);
|
||||||
|
|
|
@ -15,6 +15,9 @@ namespace BizHawk.Client.Common
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TasStateManager : IStateManager
|
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
|
// 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 IStatable Core => Global.Emulator.AsStatable();
|
||||||
private readonly StateManagerDecay _decay;
|
private readonly StateManagerDecay _decay;
|
||||||
|
@ -23,9 +26,9 @@ namespace BizHawk.Client.Common
|
||||||
private readonly SortedList<int, StateManagerState> _states;
|
private readonly SortedList<int, StateManagerState> _states;
|
||||||
private readonly ulong _expectedStateSize;
|
private readonly ulong _expectedStateSize;
|
||||||
|
|
||||||
|
private ulong _used;
|
||||||
private int _stateFrequency;
|
private int _stateFrequency;
|
||||||
private readonly int _minFrequency = 1;
|
|
||||||
private readonly int _maxFrequency = 16;
|
|
||||||
private int MaxStates => (int)(Settings.Cap / _expectedStateSize) +
|
private int MaxStates => (int)(Settings.Cap / _expectedStateSize) +
|
||||||
(int)((ulong)Settings.DiskCapacitymb * 1024 * 1024 / _expectedStateSize);
|
(int)((ulong)Settings.DiskCapacitymb * 1024 * 1024 / _expectedStateSize);
|
||||||
private int FileStateGap => 1 << Settings.FileStateGap;
|
private int FileStateGap => 1 << Settings.FileStateGap;
|
||||||
|
@ -55,22 +58,8 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
public Action<int> InvalidateCallback { get; set; }
|
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; }
|
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]
|
public KeyValuePair<int, byte[]> this[int frame]
|
||||||
{
|
{
|
||||||
get
|
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
|
private byte[] InitialState
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -102,10 +97,25 @@ namespace BizHawk.Client.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public bool Any()
|
||||||
/// 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
|
if (_movie.StartsFromSavestate)
|
||||||
/// </summary>
|
{
|
||||||
|
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)
|
public void Capture(bool force = false)
|
||||||
{
|
{
|
||||||
bool shouldCapture;
|
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.
|
var tempState = _states.Values;
|
||||||
}
|
var power = tempState[0].Frame == 0
|
||||||
|
? _states.Values.First(s => s.Frame == 0)
|
||||||
if (_states.ContainsKey(frame))
|
: _states.Values[0];
|
||||||
{
|
|
||||||
_states[frame].State = state;
|
_states.Clear();
|
||||||
}
|
SetState(0, power.State);
|
||||||
else
|
_used = (ulong)power.State.Length;
|
||||||
{
|
|
||||||
_used += (ulong)state.Length;
|
|
||||||
_states.Add(frame, new StateManagerState(state, frame));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,9 +173,6 @@ namespace BizHawk.Client.Common
|
||||||
return _states.ContainsKey(frame);
|
return _states.ContainsKey(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears out all savestates after the given frame number
|
|
||||||
/// </summary>
|
|
||||||
public bool Invalidate(int frame)
|
public bool Invalidate(int frame)
|
||||||
{
|
{
|
||||||
bool anyInvalidated = false;
|
bool anyInvalidated = false;
|
||||||
|
@ -222,6 +226,89 @@ namespace BizHawk.Client.Common
|
||||||
return true;
|
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.
|
// Deletes states to follow the state storage size limits.
|
||||||
// Used after changing the settings too.
|
// Used after changing the settings too.
|
||||||
private void LimitStateCount()
|
private void LimitStateCount()
|
||||||
|
@ -290,109 +377,5 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
return ret;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue