diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index ae8fc8d064..2bb78d5d0a 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -289,55 +289,54 @@ namespace BizHawk.Client.Common /// /// Deletes/moves states to follow the state storage size limits. /// Used after changing the settings too. + /// TODO: don't miss states on region borders /// public void LimitStateCount() { - if (Used + _expectedStateSize > Settings.Cap - || DiskUsed > (ulong)Settings.DiskCapacitymb * 1024 * 1024 - || DateTime.Now.Ticks > _stateCleanupTime) + if (Used + _expectedStateSize > Settings.Cap || DiskUsed > (ulong)Settings.DiskCapacitymb * 1024 * 1024) { - // rely on frames, because relying on indexes while changing the collection is ugly - List framesToClear = new List(); - - // we have 5 greenzone regions, the last one we do not touch + // we have 5 greenzone regions (by powers of 2), the closest one we do not touch int regionSize = _maxStates / 5; + int lastClearedFrame = 1; - // drop states from previous regions, increasing the gap for each one, make sure to ignore state 0 - for (int gap = 2, last = _states.ToList().Count; gap <= 16; gap <<= 1) + // iterate through regions (region frame sizes increase by 2) + for (int borderIndex = GetStateIndexByFrame(Global.Emulator.Frame) - regionSize, mult = 2; borderIndex > 0; mult *= 2) { - // last egion index - last -= regionSize; - if (last < 1) - { - return; - } + int nextBorderIndex = borderIndex - regionSize * mult; + if (nextBorderIndex <= 0) + nextBorderIndex = 1; // reached greenzone end - // first region index - int first = last - regionSize; - if (first < 1) + // iterate through states. i > 0 because nextBorderIndex > 0 + for (int i = borderIndex; i > nextBorderIndex; i--) { - first = 1; - } + int minStep = mult * StateFrequency; + int curFrame = GetStateFrameByIndex(i); + int nextFrame = GetStateFrameByIndex(i - 1); - // iterate through the region and record states' frames - for (int i = last; i >= first; i--) - { - if ((i & (gap - 1)) > 0) + if (curFrame - nextFrame < minStep) { - framesToClear.Add(_states.ElementAt(i).Key); + RemoveState(nextFrame); + lastClearedFrame = nextFrame; } + else + continue; } + + if (nextBorderIndex == 1) + return; + + borderIndex = nextBorderIndex; } - if (framesToClear.Any()) + // finish off whatever we've missed + List> leftoverStates = _states + .Where(s => s.Key > 0 && s.Key < lastClearedFrame) + .ToList(); + + foreach (var state in leftoverStates) { - foreach (var frame in framesToClear) - { - RemoveState(frame); - } + RemoveState(state.Key); } - - _stateCleanupTime = DateTime.Now.Ticks + _stateCleanupPeriod; } } @@ -489,6 +488,26 @@ namespace BizHawk.Client.Common return this[s.Key]; } + /// + /// Returns index of the state right above the given frame + /// + /// + /// + public int GetStateIndexByFrame(int frame) + { + return _states.IndexOfKey(GetStateClosestToFrame(frame).Key); + } + + /// + /// Returns frame of the state at the given index + /// + /// + /// + public int GetStateFrameByIndex(int index) + { + return _states.ElementAt(index).Key; + } + private ulong _used; private ulong Used {