diff --git a/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs b/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs index c1a6801f47..d0f23013aa 100644 --- a/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs +++ b/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs @@ -4,24 +4,17 @@ using System.IO; namespace BizHawk.Client.Common { /// - /// Represents a savestate in the TasStateManager + /// Represents a savestate in the /// internal class StateManagerState : IDisposable { private static long _stateId = 0; - private TasStateManager _manager; + private readonly TasStateManager _manager; + private readonly long _id; private byte[] _state; - private long _id; - public int Frame { get; set; } - - public void Write(BinaryWriter w) - { - w.Write(Frame); - w.Write(_state.Length); - w.Write(_state); - } + public int Frame { get; } public static StateManagerState Read(BinaryReader r, TasStateManager m) { @@ -30,6 +23,13 @@ namespace BizHawk.Client.Common return new StateManagerState(m, data, frame); } + public void Write(BinaryWriter w) + { + w.Write(Frame); + w.Write(_state.Length); + w.Write(_state); + } + public byte[] State { get @@ -41,6 +41,7 @@ namespace BizHawk.Client.Common return _manager.ndbdatabase.FetchAll(_id.ToString()); } + set { if (_state != null) @@ -54,17 +55,11 @@ namespace BizHawk.Client.Common } } - public int Length - { - get { return State.Length; } - } + public int Length => State.Length; - public bool IsOnDisk - { - get { return _state == null; } - } + public bool IsOnDisk => _state == null; - public StateManagerState(TasStateManager manager, byte[] state, int frame) + public StateManagerState(TasStateManager manager, byte[] state, int frame) { _manager = manager; _state = state; @@ -104,7 +99,9 @@ namespace BizHawk.Client.Common public void Dispose() { if (!IsOnDisk) + { return; + } _manager.ndbdatabase.Release(_id.ToString()); } diff --git a/BizHawk.Client.Common/movie/tasproj/TasBranch.cs b/BizHawk.Client.Common/movie/tasproj/TasBranch.cs index 30db681acf..13faf2d0f8 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasBranch.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasBranch.cs @@ -34,7 +34,10 @@ namespace BizHawk.Client.Common { var currentHashes = this.Select(b => b.UniqueIdentifier.GetHashCode()).ToList(); - do item.UniqueIdentifier = Guid.NewGuid(); + do + { + item.UniqueIdentifier = Guid.NewGuid(); + } while (currentHashes.Contains(item.UniqueIdentifier.GetHashCode())); } @@ -57,9 +60,9 @@ namespace BizHawk.Client.Common // if this header needs more stuff in it, handle it sensibly tw.WriteLine(JsonConvert.SerializeObject(new { - Frame = b.Frame, - TimeStamp = b.TimeStamp, - UniqueIdentifier = b.UniqueIdentifier + b.Frame, + b.TimeStamp, + b.UniqueIdentifier })); }); @@ -72,7 +75,9 @@ namespace BizHawk.Client.Common { int todo = b.InputLog.Count; for (int i = 0; i < todo; i++) + { tw.WriteLine(b.InputLog[i]); + } }); bs.PutLump(nframebuffer, delegate(Stream s) diff --git a/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs b/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs index 9582b662d8..03514f5fa6 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs @@ -20,11 +20,14 @@ namespace BizHawk.Client.Common if (frame < LagLog.Count) { if (frame < 0) + { return null; - else - return LagLog[frame]; + } + + return LagLog[frame]; } - else if (frame == Global.Emulator.Frame && frame == LagLog.Count) + + if (frame == Global.Emulator.Frame && frame == LagLog.Count) { // LagLog[frame] = Global.Emulator.AsInputPollable().IsLagFrame; // Note: Side effects! return Global.Emulator.AsInputPollable().IsLagFrame; @@ -40,7 +43,8 @@ namespace BizHawk.Client.Common LagLog.RemoveAt(frame); return; } - else if (frame < 0) + + if (frame < 0) { return; // Nothing to do } @@ -85,6 +89,7 @@ namespace BizHawk.Client.Common LagLog.RemoveRange(frame + 1, LagLog.Count - frame - 1); return true; } + return false; } @@ -92,10 +97,15 @@ namespace BizHawk.Client.Common { WasLag.RemoveAt(frame); } + public void InsertHistoryAt(int frame, bool isLag) - { // LagLog was invalidated when the frame was inserted + { + // LagLog was invalidated when the frame was inserted if (frame <= LagLog.Count) + { LagLog.Insert(frame, isLag); + } + WasLag.Insert(frame, isLag); } @@ -109,8 +119,11 @@ namespace BizHawk.Client.Common bw.Write(LagLog[i]); bw.Write(WasLag[i]); } + for (int i = LagLog.Count; i < WasLag.Count; i++) + { bw.Write(WasLag[i]); + } } public void Load(BinaryReader br) diff --git a/BizHawk.Client.Common/movie/tasproj/TasMovie.History.cs b/BizHawk.Client.Common/movie/tasproj/TasMovie.History.cs index 9a6a73badd..498ece9dd2 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasMovie.History.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasMovie.History.cs @@ -6,7 +6,7 @@ namespace BizHawk.Client.Common { public class TasMovieChangeLog { - List> History; + private readonly List> History; public List Names; public int UndoIndex = -1; int _maxSteps = 100; @@ -173,17 +173,19 @@ namespace BizHawk.Client.Common return PreviousRedoFrame; } - public bool CanUndo { get { return UndoIndex > -1; } } - public bool CanRedo { get { return UndoIndex < History.Count - 1; } } + public bool CanUndo => UndoIndex > -1; + public bool CanRedo => UndoIndex < History.Count - 1; public string NextUndoStepName { get { if (Names.Count == 0 || UndoIndex < 0) + { return null; - else - return Names[UndoIndex]; + } + + return Names[UndoIndex]; } } @@ -192,10 +194,14 @@ namespace BizHawk.Client.Common get { if (UndoIndex == History.Count - 1) + { return Movie.InputLogLength; + } if (History[UndoIndex + 1].Count == 0) + { return Movie.InputLogLength; + } return History[UndoIndex + 1].Min(a => a.FirstFrame); } @@ -206,24 +212,32 @@ namespace BizHawk.Client.Common get { if (UndoIndex == -1) + { return Movie.InputLogLength; + } if (History[UndoIndex].Count == 0) + { return Movie.InputLogLength; + } return History[UndoIndex].Min(a => a.FirstFrame); } } - #region "Change History" + #region Change History private bool AddMovieAction(string name) { if (UndoIndex + 1 != History.Count) + { TruncateLog(UndoIndex + 1); + } if (name == "") + { name = "Undo step " + _totalSteps; + } bool ret = false; if (!RecordingBatch) @@ -234,7 +248,9 @@ namespace BizHawk.Client.Common _totalSteps += 1; if (History.Count <= MaxSteps) + { UndoIndex += 1; + } else { History.RemoveAt(0); @@ -312,10 +328,11 @@ namespace BizHawk.Client.Common History.Last().Add(new MovieActionBindInput(Movie, frame, isDelete)); } } + #endregion } - #region "Classes" + #region Classes public interface IMovieAction { @@ -328,12 +345,14 @@ namespace BizHawk.Client.Common public class MovieAction : IMovieAction { - public int FirstFrame { get; private set; } - public int LastFrame { get; private set; } + public int FirstFrame { get; } + public int LastFrame { get; } + private int undoLength; private int redoLength; - private int length - { get { return LastFrame - FirstFrame + 1; } } + + private int length => LastFrame - FirstFrame + 1; + private List oldLog; private List newLog; private bool bindMarkers; @@ -346,7 +365,9 @@ namespace BizHawk.Client.Common undoLength = Math.Min(LastFrame + 1, movie.InputLogLength) - FirstFrame; for (int i = 0; i < undoLength; i++) + { oldLog.Add(movie.GetLogEntries()[FirstFrame + i]); + } bindMarkers = movie.BindMarkersToInput; } @@ -356,7 +377,9 @@ namespace BizHawk.Client.Common redoLength = Math.Min(LastFrame + 1, movie.InputLogLength) - FirstFrame; newLog = new List(); for (int i = 0; i < redoLength; i++) + { newLog.Add(movie.GetLogEntries()[FirstFrame + i]); + } } public void Undo(TasMovie movie) @@ -400,8 +423,8 @@ namespace BizHawk.Client.Common public class MovieActionMarker : IMovieAction { - public int FirstFrame { get; private set; } - public int LastFrame { get; private set; } + public int FirstFrame { get; } + public int LastFrame { get; } private string oldMessage; private string newMessage; diff --git a/BizHawk.Client.Common/movie/tasproj/TasMovie.cs b/BizHawk.Client.Common/movie/tasproj/TasMovie.cs index dd2fc88cda..205f2c7e7a 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasMovie.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasMovie.cs @@ -30,26 +30,22 @@ namespace BizHawk.Client.Common public bool BindMarkersToInput { get; set; } public bool UseInputCache { get; set; } public int CurrentBranch { get; set; } - public int BranchCount { get { return Branches.Count; } } - public int LastValidFrame { get { return LagLog.LastValidFrame; } } - public override string PreferredExtension { get { return Extension; } } - public TasStateManager TasStateManager { get { return StateManager; } } - public TasMovieRecord this[int index] - { - get - { - return new TasMovieRecord - { - // State = StateManager[index], - HasState = StateManager.HasState(index), - LogEntry = GetInputLogEntry(index), - Lagged = LagLog[index + 1], - WasLagged = LagLog.History(index + 1) - }; - } - } - public TasMovie(string path, bool startsFromSavestate = false, BackgroundWorker progressReportWorker = null) + public int BranchCount => Branches.Count; + public int LastValidFrame => LagLog.LastValidFrame; + public override string PreferredExtension => Extension; + public TasStateManager TasStateManager => StateManager; + + public TasMovieRecord this[int index] => new TasMovieRecord + { + // State = StateManager[index], + HasState = StateManager.HasState(index), + LogEntry = GetInputLogEntry(index), + Lagged = LagLog[index + 1], + WasLagged = LagLog.History(index + 1) + }; + + public TasMovie(string path, bool startsFromSavestate = false, BackgroundWorker progressReportWorker = null) : base(path) { // TODO: how to call the default constructor AND the base(path) constructor? And is base(path) calling base() ? @@ -71,7 +67,6 @@ namespace BizHawk.Client.Common } public TasMovie(bool startsFromSavestate = false, BackgroundWorker progressReportWorker = null) - : base() { _progressReportWorker = progressReportWorker; if (!Global.Emulator.HasSavestates()) @@ -98,11 +93,11 @@ namespace BizHawk.Client.Common public void ReportProgress(double percent) { if (percent > 100d) - return; - if (_progressReportWorker != null) { - _progressReportWorker.ReportProgress((int)percent); + return; } + + _progressReportWorker?.ReportProgress((int)percent); } // TODO: use LogGenerators rather than string comparisons @@ -156,7 +151,7 @@ namespace BizHawk.Client.Common if (anyInvalidated && Global.MovieSession.Movie.IsCountingRerecords) { - base.Rerecords++; + Rerecords++; } } @@ -190,9 +185,9 @@ namespace BizHawk.Client.Common { if (adapter.Definition.BoolButtons.Contains(buttonName)) { - return adapter.IsPressed(buttonName) ? - Mnemonics[buttonName].ToString() : - ""; + return adapter.IsPressed(buttonName) + ? Mnemonics[buttonName].ToString() + : ""; } if (adapter.Definition.FloatControls.Contains(buttonName)) @@ -284,6 +279,7 @@ namespace BizHawk.Client.Common int? stateFrame = null; var newLog = new List(); + // We are in record mode so replace the movie log with the one from the savestate if (!Global.MovieSession.MultiTrack.IsActive) { @@ -303,7 +299,8 @@ namespace BizHawk.Client.Common { break; } - else if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay + + if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay { var strs = line.Split('x'); try @@ -340,6 +337,7 @@ namespace BizHawk.Client.Common { TimelineBranchFrame = counter; } + counter++; } } @@ -440,42 +438,51 @@ namespace BizHawk.Client.Common public TasBranch GetBranch(int index) { if (index >= Branches.Count || index < 0) + { return null; - else - return Branches[index]; + } + + return Branches[index]; } public TasBranch GetBranch(Guid id) { - TasBranch branch = Branches.SingleOrDefault(b => b.UniqueIdentifier == id); - if (branch != null) - return branch; - else - return null; + return Branches.SingleOrDefault(b => b.UniqueIdentifier == id); } public int BranchHashByIndex(int index) { if (index >= Branches.Count) + { return -1; - else - return Branches[index].UniqueIdentifier.GetHashCode(); + } + + return Branches[index].UniqueIdentifier.GetHashCode(); } public int BranchIndexByHash(int hash) { TasBranch branch = Branches.SingleOrDefault(b => b.UniqueIdentifier.GetHashCode() == hash); if (branch == null) + { return -1; + } + return Branches.IndexOf(branch); } public int BranchIndexByFrame(int frame) { - TasBranch branch = Branches.Where(b => b.Frame == frame) - .OrderByDescending(b => b.TimeStamp).FirstOrDefault(); + TasBranch branch = Branches + .Where(b => b.Frame == frame) + .OrderByDescending(b => b.TimeStamp) + .FirstOrDefault(); + if (branch == null) + { return -1; + } + return Branches.IndexOf(branch); } @@ -497,7 +504,7 @@ namespace BizHawk.Client.Common { int? divergentPoint = DivergentPoint(_log, branch.InputLog); - if (_log != null) _log.Dispose(); + _log?.Dispose(); _log = branch.InputLog.Clone(); ////_changes = true; @@ -509,14 +516,18 @@ namespace BizHawk.Client.Common LagLog.FromLagLog(branch.LagLog); // don't truncate LagLog if the branch's one is shorter, but input is the same } else + { StateManager.Invalidate(branch.InputLog.Count); + } StateManager.LoadBranch(Branches.IndexOf(branch)); StateManager.SetState(branch.Frame, branch.CoreData); ////ChangeLog = branch.ChangeLog; if (BindMarkersToInput) // pretty critical not to erase them + { Markers = branch.Markers; + } Changes = true; } @@ -526,7 +537,10 @@ namespace BizHawk.Client.Common int index = Branches.IndexOf(old); newBranch.UniqueIdentifier = old.UniqueIdentifier; if (newBranch.UserText == "") + { newBranch.UserText = old.UserText; + } + Branches[index] = newBranch; TasStateManager.UpdateBranch(index); Changes = true; @@ -537,7 +551,9 @@ namespace BizHawk.Client.Common TasBranch branch = Branches[b1]; if (b2 >= Branches.Count) + { b2 = Branches.Count - 1; + } Branches.Remove(branch); Branches.Insert(b2, branch); @@ -553,7 +569,11 @@ namespace BizHawk.Client.Common private bool _changes; public override bool Changes { - get { return _changes; } + get + { + return _changes; + } + protected set { if (_changes != value) @@ -567,14 +587,11 @@ namespace BizHawk.Client.Common // This event is Raised only when Changes is TOGGLED. private void OnPropertyChanged(string propertyName) { - if (PropertyChanged != null) - { - // Raising the event when FirstName or LastName property value changed - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } + // Raising the event when FirstName or LastName property value changed + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - void Markers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + private void Markers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { Changes = true; } diff --git a/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs b/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs index 7e089d298f..044ddeea54 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs @@ -28,7 +28,7 @@ namespace BizHawk.Client.Common Message = split[1]; } - public virtual int Frame { get; private set; } + public virtual int Frame { get; } public virtual string Message { get; set; } @@ -48,23 +48,24 @@ namespace BizHawk.Client.Common { return false; } - else if (obj is TasMovieMarker) + + if (obj is TasMovieMarker) { return Frame == (obj as TasMovieMarker).Frame; } - else - { - return false; - } + + return false; } public static bool operator ==(TasMovieMarker marker, int frame) { + // TODO: account for null marker return marker.Frame == frame; } public static bool operator !=(TasMovieMarker marker, int frame) { + // TODO: account for null marker return marker.Frame != frame; } } @@ -80,9 +81,11 @@ namespace BizHawk.Client.Common public TasMovieMarkerList DeepClone() { - TasMovieMarkerList ret = new TasMovieMarkerList(_movie); + var ret = new TasMovieMarkerList(_movie); for (int i = 0; i < Count; i++) + { ret.Add(new TasMovieMarker(this[i].Frame, this[i].Message)); + } return ret; } @@ -91,11 +94,8 @@ namespace BizHawk.Client.Common private void OnListChanged(NotifyCollectionChangedAction action) { - if (CollectionChanged != null) - { - // TODO Allow different types - CollectionChanged.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } + // TODO Allow different types + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public override string ToString() @@ -123,7 +123,10 @@ namespace BizHawk.Client.Common if (existingItem.Message != item.Message) { if (!fromHistory) + { _movie.ChangeLog.AddMarkerChange(item, item.Frame, existingItem.Message); + } + existingItem.Message = item.Message; OnListChanged(NotifyCollectionChangedAction.Replace); } @@ -131,7 +134,10 @@ namespace BizHawk.Client.Common else { if (!fromHistory) + { _movie.ChangeLog.AddMarkerChange(item); + } + base.Add(item); Sort((m1, m2) => m1.Frame.CompareTo(m2.Frame)); OnListChanged(NotifyCollectionChangedAction.Add); @@ -150,8 +156,11 @@ namespace BizHawk.Client.Common { Add(m); } + if (endBatch) + { _movie.ChangeLog.EndBatch(); + } } // the inherited one @@ -163,7 +172,10 @@ namespace BizHawk.Client.Common public void Insert(int index, TasMovieMarker item, bool fromHistory) { if (!fromHistory) + { _movie.ChangeLog.AddMarkerChange(item); + } + base.Insert(index, item); Sort((m1, m2) => m1.Frame.CompareTo(m2.Frame)); OnListChanged(NotifyCollectionChangedAction.Add); @@ -173,9 +185,14 @@ namespace BizHawk.Client.Common { bool endBatch = _movie.ChangeLog.BeginNewBatch("Add Markers", true); foreach (TasMovieMarker m in collection) + { _movie.ChangeLog.AddMarkerChange(m); + } + if (endBatch) + { _movie.ChangeLog.EndBatch(); + } base.InsertRange(index, collection); Sort((m1, m2) => m1.Frame.CompareTo(m2.Frame)); @@ -191,9 +208,15 @@ namespace BizHawk.Client.Common public void Remove(TasMovieMarker item, bool fromHistory) { if (item == null || item.Frame == 0) // TODO: Don't do this. + { return; + } + if (!fromHistory) + { _movie.ChangeLog.AddMarkerChange(null, item.Frame, item.Message); + } + base.Remove(item); OnListChanged(NotifyCollectionChangedAction.Remove); } @@ -204,10 +227,15 @@ namespace BizHawk.Client.Common foreach (TasMovieMarker m in this) { if (match.Invoke(m)) + { _movie.ChangeLog.AddMarkerChange(null, m.Frame, m.Message); + } } + if (endBatch) + { _movie.ChangeLog.EndBatch(); + } int removeCount = base.RemoveAll(match); if (removeCount > 0) @@ -220,10 +248,16 @@ namespace BizHawk.Client.Common public void Move(int fromFrame, int toFrame, bool fromHistory = false) { if (fromFrame == 0) // no thanks! + { return; + } + TasMovieMarker m = Get(fromFrame); if (m == null) // TODO: Don't do this. + { return; + } + _movie.ChangeLog.AddMarkerChange(m, m.Frame); Insert(0, new TasMovieMarker(toFrame, m.Message), fromHistory); Remove(m, fromHistory); @@ -243,18 +277,26 @@ namespace BizHawk.Client.Common if (this[i].Frame >= startFrame) { if (i == 0) + { continue; + } + _movie.ChangeLog.AddMarkerChange(null, this[i].Frame, this[i].Message); RemoveAt(i); deletedCount++; } } + if (endBatch) + { _movie.ChangeLog.EndBatch(); + } + if (deletedCount > 0) { OnListChanged(NotifyCollectionChangedAction.Remove); } + return deletedCount; } diff --git a/BizHawk.Client.Common/movie/tasproj/TasSession.cs b/BizHawk.Client.Common/movie/tasproj/TasSession.cs index 89ae7957e7..4e1bc173ae 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasSession.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasSession.cs @@ -5,9 +5,9 @@ namespace BizHawk.Client.Common { public class TasSession { - private TasMovie _movie; - public int CurrentFrame { get; set; } - public int CurrentBranch { get; set; } + private readonly TasMovie _movie; + public int CurrentFrame { get; private set; } + public int CurrentBranch { get; private set; } public TasSession(TasMovie movie) { @@ -23,10 +23,10 @@ namespace BizHawk.Client.Common } public override string ToString() - { - StringBuilder sb = new StringBuilder(); - - UpdateValues(); + { + UpdateValues(); + + var sb = new StringBuilder(); sb.AppendLine(CurrentFrame.ToString()); sb.AppendLine(CurrentBranch.ToString()); @@ -37,17 +37,18 @@ namespace BizHawk.Client.Common { if (!string.IsNullOrWhiteSpace(session)) { - string[] lines = session.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + string[] lines = session.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); - if (lines.Length > 0) - CurrentFrame = int.Parse(lines[0]); - else - CurrentFrame = 0; + CurrentFrame = lines.Length > 0 ? int.Parse(lines[0]) : 0; if (lines.Length > 1) + { CurrentBranch = int.Parse(lines[1]); + } else + { CurrentBranch = -1; + } } } } diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index 2aa9268846..1ac6d0dec0 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -17,25 +17,16 @@ namespace BizHawk.Client.Common public class TasStateManager : IDisposable { // 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 - { - get - { - return Global.Emulator.AsStatable(); - } - } + private IStatable Core => Global.Emulator.AsStatable(); public Action InvalidateCallback { get; set; } private void CallInvalidateCallback(int index) { - if (InvalidateCallback != null) - { - InvalidateCallback(index); - } + InvalidateCallback?.Invoke(index); } - private List lowPriorityStates = new List(); + private readonly List lowPriorityStates = new List(); internal NDBDatabase ndbdatabase; private Guid guid = Guid.NewGuid(); private SortedList States = new SortedList(); @@ -53,7 +44,7 @@ namespace BizHawk.Client.Common private readonly TasMovie _movie; private ulong _expectedStateSize = 0; - private int _minFrequency = VersionInfo.DeveloperBuild ? 2 : 1; + private readonly int _minFrequency = VersionInfo.DeveloperBuild ? 2 : 1; private const int _maxFrequency = 16; private int StateFrequency @@ -101,8 +92,7 @@ namespace BizHawk.Client.Common public void Dispose() { // States and BranchStates don't need cleaning because they would only contain an ndbdatabase entry which was demolished by the below - if (ndbdatabase != null) - ndbdatabase.Dispose(); + ndbdatabase?.Dispose(); } /// @@ -111,7 +101,9 @@ namespace BizHawk.Client.Common public void MountWriteAccess() { if (_isMountedForWrite) + { return; + } _isMountedForWrite = true; @@ -127,7 +119,10 @@ namespace BizHawk.Client.Common States = new SortedList(limit); if (_expectedStateSize > int.MaxValue) + { throw new InvalidOperationException(); + } + ndbdatabase = new NDBDatabase(statePath, Settings.DiskCapacitymb * 1024 * 1024, (int)_expectedStateSize); } @@ -157,7 +152,7 @@ namespace BizHawk.Client.Common } } - private List accessed; + private readonly List accessed; public byte[] InitialState { @@ -244,18 +239,26 @@ namespace BizHawk.Client.Common int i = 0; int markerSkips = maxStates / 2; + // lowPrioritySates (e.g. states with only lag frames between them) do { if (lowPriorityStates.Count > i) + { shouldRemove = findState(lowPriorityStates[i]); + } else + { break; + } // Keep marker states markerSkips--; if (markerSkips < 0) + { shouldRemove.X = -1; + } + i++; } while (StateIsMarker(shouldRemove.X, shouldRemove.Y) && markerSkips > -1 || shouldRemove.X == 0); @@ -267,14 +270,21 @@ namespace BizHawk.Client.Common do { if (accessed.Count > i) + { shouldRemove = findState(accessed[i]); + } else + { break; + } // Keep marker states markerSkips--; if (markerSkips < 0) + { shouldRemove.X = -1; + } + i++; } while (StateIsMarker(shouldRemove.X, shouldRemove.Y) && markerSkips > -1 || shouldRemove.X == 0); } @@ -283,7 +293,7 @@ namespace BizHawk.Client.Common { if (BranchStates.Any() && !Settings.EraseBranchStatesFirst) { - var kvp = BranchStates.Count() > 1 ? BranchStates.ElementAt(1) : BranchStates.ElementAt(0); + var kvp = BranchStates.Count > 1 ? BranchStates.ElementAt(1) : BranchStates.ElementAt(0); shouldRemove.X = kvp.Key; shouldRemove.Y = kvp.Value.Keys[0]; } @@ -301,17 +311,21 @@ namespace BizHawk.Client.Common private bool StateIsMarker(int frame, int branch) { if (frame == -1) + { return false; + } if (branch == -1) - return _movie.Markers.IsMarker(States[frame].Frame + 1); - else { - if (_movie.GetBranch(_movie.BranchIndexByHash(branch)).Markers == null) - return _movie.Markers.IsMarker(States[frame].Frame + 1); - else - return _movie.GetBranch(branch).Markers.Any(m => m.Frame + 1 == frame); + return _movie.Markers.IsMarker(States[frame].Frame + 1); } + + if (_movie.GetBranch(_movie.BranchIndexByHash(branch)).Markers == null) + { + return _movie.Markers.IsMarker(States[frame].Frame + 1); + } + + return _movie.GetBranch(branch).Markers.Any(m => m.Frame + 1 == frame); } private bool AllLag(int from, int upTo) @@ -320,13 +334,17 @@ namespace BizHawk.Client.Common { upTo = Global.Emulator.Frame - 1; if (!Global.Emulator.AsInputPollable().IsLagFrame) + { return false; + } } for (int i = from; i < upTo; i++) { if (_movie[i].Lagged == false) + { return false; + } } return true; @@ -347,12 +365,17 @@ namespace BizHawk.Client.Common internal void SetState(int frame, byte[] state, bool skipRemoval = true) { if (!skipRemoval) // skipRemoval: false only when capturing new states + { MaybeRemoveStates(); // Remove before adding so this state won't be removed. + } if (States.ContainsKey(frame)) { if (stateHasDuplicate(frame, -1) != -2) + { Used += (ulong)state.Length; + } + States[frame].State = state; } else @@ -373,9 +396,13 @@ namespace BizHawk.Client.Common private void RemoveState(int frame, int branch = -1) { if (branch == -1) + { accessed.Remove(States[frame]); + } else if (accessed.Contains(BranchStates[frame][branch]) && !Settings.EraseBranchStatesFirst) + { accessed.Remove(BranchStates[frame][branch]); + } StateManagerState state; bool hasDuplicate = stateHasDuplicate(frame, branch) != -2; @@ -383,14 +410,20 @@ namespace BizHawk.Client.Common { state = States[frame]; if (States[frame].IsOnDisk) + { States[frame].Dispose(); + } else + { Used -= (ulong)States[frame].Length; + } + States.RemoveAt(States.IndexOfKey(frame)); } else { state = BranchStates[frame][branch]; + if (BranchStates[frame][branch].IsOnDisk) BranchStates[frame][branch].Dispose(); else @@ -398,17 +431,23 @@ namespace BizHawk.Client.Common BranchStates[frame].RemoveAt(BranchStates[frame].IndexOfKey(branch)); if (BranchStates[frame].Count == 0) + { BranchStates.Remove(frame); + } } if (!hasDuplicate) + { lowPriorityStates.Remove(state); + } } private void StateAccessed(int frame) { if (frame == 0 && _movie.StartsFromSavestate) + { return; + } StateManagerState state = States[frame]; bool removed = accessed.Remove(state); @@ -417,12 +456,17 @@ namespace BizHawk.Client.Common if (States[frame].IsOnDisk) { if (!States[accessed[0].Frame].IsOnDisk) + { MoveStateToDisk(accessed[0].Frame); + } + MoveStateToMemory(frame); } if (!removed && accessed.Count > maxStates) + { accessed.RemoveAt(0); + } } public bool HasState(int frame) @@ -454,8 +498,10 @@ namespace BizHawk.Client.Common anyInvalidated = statesToRemove.Any(); - foreach (KeyValuePair state in statesToRemove) + foreach (var state in statesToRemove) + { RemoveState(state.Key); + } CallInvalidateCallback(frame); } @@ -466,13 +512,12 @@ namespace BizHawk.Client.Common /// /// Clears all state information /// - /// public void Clear() { States.Clear(); accessed.Clear(); Used = 0; - clearDiskStates(); + ClearDiskStates(); } public void ClearStateHistory() @@ -488,14 +533,13 @@ namespace BizHawk.Client.Common SetState(0, power.State); Used = (ulong)power.State.Length; - clearDiskStates(); + ClearDiskStates(); } } - private void clearDiskStates() + private void ClearDiskStates() { - if (ndbdatabase != null) - ndbdatabase.Clear(); + ndbdatabase?.Clear(); } /// @@ -518,7 +562,9 @@ namespace BizHawk.Client.Common } if (Used > Settings.Cap) + { MaybeRemoveStates(); + } } private List ExcludeStates() @@ -656,11 +702,12 @@ namespace BizHawk.Client.Common list.Add(key2, state); c2--; } + BranchStates.Add(key, list); c--; } } - catch (EndOfStreamException) { } + catch (EndOfStreamException) { } // Bad! } @@ -684,12 +731,18 @@ namespace BizHawk.Client.Common { return _used; } + set { + // TODO: Shouldn't we throw an exception? Debug.Fail only runs in debug mode? if (value > 0xf000000000000000) + { System.Diagnostics.Debug.Fail("ulong Used underfow!"); + } else + { _used = value; + } } } @@ -697,18 +750,16 @@ namespace BizHawk.Client.Common { get { - if (ndbdatabase == null) return 0; - else return (ulong)ndbdatabase.Consumed; + if (ndbdatabase == null) + { + return 0; + } + + return (ulong)ndbdatabase.Consumed; } } - public int StateCount - { - get - { - return States.Count; - } - } + public int StateCount => States.Count; public bool Any() { @@ -746,7 +797,7 @@ namespace BizHawk.Client.Common } } - #region "Branches" + #region Branches private SortedList> BranchStates = new SortedList>(); @@ -764,16 +815,24 @@ namespace BizHawk.Client.Common else { if (!BranchStates[frame].ContainsKey(branchHash)) + { return -2; + } + stateToMatch = BranchStates[frame][branchHash]; + // Check the current branch for duplicate. if (States.ContainsKey(frame) && States[frame] == stateToMatch) + { return -1; + } } // Check if there are any branch states for the given frame. if (!BranchStates.ContainsKey(frame) || BranchStates[frame] == null || branchHash == -1) + { return -2; + } // Loop through branch states for the given frame. SortedList stateList = BranchStates[frame]; @@ -781,10 +840,14 @@ namespace BizHawk.Client.Common { // Don't check the branch containing the state to match. if (i == _movie.BranchIndexByHash(branchHash)) + { continue; + } if (stateList.Values[i] == stateToMatch) + { return i; + } } return -2; // No match. @@ -801,8 +864,11 @@ namespace BizHawk.Client.Common int index = BranchStates[s.Frame].Values.IndexOf(s); ret.Y = BranchStates[s.Frame].Keys.ElementAt(index); } + if (ret.Y == -1) + { return new Point(-1, -2); + } } return ret; @@ -815,7 +881,9 @@ namespace BizHawk.Client.Common foreach (KeyValuePair kvp in States) { if (!BranchStates.ContainsKey(kvp.Key)) + { BranchStates.Add(kvp.Key, new SortedList()); + } SortedList stateList = BranchStates[kvp.Key]; @@ -824,6 +892,7 @@ namespace BizHawk.Client.Common stateList = new SortedList(); BranchStates[kvp.Key] = stateList; } + stateList.Add(branchHash, kvp.Value); // We aren't creating any new states, just adding a reference to an already-existing one; so Used doesn't need to be updated. } @@ -837,7 +906,10 @@ namespace BizHawk.Client.Common { SortedList stateList = kvp.Value; if (stateList == null) + { continue; + } + /* if (stateList.ContainsKey(branchHash)) { @@ -850,14 +922,18 @@ namespace BizHawk.Client.Common */ stateList.Remove(branchHash); if (stateList.Count == 0) + { BranchStates.Remove(kvp.Key); + } } } public void UpdateBranch(int index) { if (index == -1) // backup branch is outsider + { return; + } int branchHash = _movie.BranchHashByIndex(index); @@ -866,7 +942,10 @@ namespace BizHawk.Client.Common { SortedList stateList = kvp.Value; if (stateList == null) + { continue; + } + /* if (stateList.ContainsKey(branchHash)) { @@ -879,14 +958,18 @@ namespace BizHawk.Client.Common */ stateList.Remove(branchHash); if (stateList.Count == 0) + { BranchStates.Remove(kvp.Key); + } } // AddBranch foreach (KeyValuePair kvp in States) { if (!BranchStates.ContainsKey(kvp.Key)) + { BranchStates.Add(kvp.Key, new SortedList()); + } SortedList stateList = BranchStates[kvp.Key]; @@ -895,6 +978,7 @@ namespace BizHawk.Client.Common stateList = new SortedList(); BranchStates[kvp.Key] = stateList; } + stateList.Add(branchHash, kvp.Value); } } @@ -902,7 +986,9 @@ namespace BizHawk.Client.Common public void LoadBranch(int index) { if (index == -1) // backup branch is outsider + { return; + } int branchHash = _movie.BranchHashByIndex(index); @@ -911,10 +997,14 @@ namespace BizHawk.Client.Common foreach (KeyValuePair> kvp in BranchStates) { if (kvp.Key == 0 && States.ContainsKey(0)) + { continue; // TODO: It might be a better idea to just not put state 0 in BranchStates. + } if (kvp.Value.ContainsKey(branchHash)) + { SetState(kvp.Key, kvp.Value[branchHash].State); + } } } diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs index 8e2be48434..d0f39ed204 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs @@ -81,24 +81,18 @@ namespace BizHawk.Client.Common /// [JsonIgnore] [Browsable(false)] - public ulong CapTotal - { - get { return (ulong)(Capacitymb + DiskCapacitymb) * 1024UL * 1024UL; } - } + public ulong CapTotal => (ulong)(Capacitymb + DiskCapacitymb) * 1024UL * 1024UL; /// /// The memory state capacity in bytes. /// [JsonIgnore] [Browsable(false)] - public ulong Cap - { - get { return (ulong)Capacitymb * 1024UL * 1024UL; } - } + public ulong Cap => (ulong)Capacitymb * 1024UL * 1024UL; public override string ToString() { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.AppendLine(DiskSaveCapacitymb.ToString()); sb.AppendLine(Capacitymb.ToString()); @@ -116,7 +110,7 @@ namespace BizHawk.Client.Common { try { - string[] lines = settings.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + string[] lines = settings.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); Capacitymb = int.Parse(lines[1]); int refCapacity; @@ -150,7 +144,7 @@ namespace BizHawk.Client.Common else StateGap = 4; } - catch (Exception) + catch (Exception) // TODO: this is bad { // "GreenZoneSettings inconsistent, ignoring" // if we don't catch it, the project won't load