/// <summary> /// Rewind from the current event in the timeline to the most recent bookmark, and begin a new timeline branch from there. /// </summary> public void JumpToMostRecentBookmark() { HistoryEvent lastBookmarkEvent = _history.CurrentEvent; while (lastBookmarkEvent != null) { if (lastBookmarkEvent is BookmarkHistoryEvent b && !b.Bookmark.System && b.Ticks != Core.Ticks) { TimeSpan before = Helpers.GetTimeSpanFromTicks(Core.Ticks); JumpToBookmark(lastBookmarkEvent); TimeSpan after = Helpers.GetTimeSpanFromTicks(Core.Ticks); Status = String.Format("Rewound to {0} (-{1})", after.ToString(@"hh\:mm\:ss"), (after - before).ToString(@"hh\:mm\:ss")); return; } lastBookmarkEvent = lastBookmarkEvent.Parent; } // No bookmarks? Go all the way back to the root! JumpToBookmark(_history.RootEvent); Status = "Rewound to start"; }
private void SeekToBookmark(int bookmarkEventIndex) { Core core; int startIndex = 0; // First find the bookmark. if (bookmarkEventIndex == -1) { core = Core.Create(Core.LatestVersion, Core.Type.CPC6128); Display.GetFromBookmark(null); } else { Bookmark bookmark = (_historyEvents[bookmarkEventIndex] as BookmarkHistoryEvent).Bookmark; core = Core.Create(bookmark.Version, bookmark.State.GetBytes()); Display.GetFromBookmark(bookmark); startIndex = bookmarkEventIndex; } for (int i = startIndex; i < _historyEvents.Count; i++) { HistoryEvent historyEvent = _historyEvents[i]; if (historyEvent is CoreActionHistoryEvent coreActionHistoryEvent) { core.PushRequest(CoreRequest.RunUntil(coreActionHistoryEvent.Ticks)); core.PushRequest(coreActionHistoryEvent.CoreAction); } } core.PushRequest(CoreRequest.RunUntil(_endTicks)); Core = core; Core.Auditors = RequestProcessed; Core.IdleRequest += IdleRequest; SetCoreRunning(); }
private void SelectBookmark(IJumpableMachine jumpableMachine) { if (jumpableMachine == null) { return; } using ((jumpableMachine as IPausableMachine)?.AutoPause()) { PromptForBookmarkEventArgs args = new PromptForBookmarkEventArgs(); PromptForBookmark?.Invoke(this, args); HistoryEvent historyEvent = args.SelectedBookmark; if (historyEvent != null) { jumpableMachine.JumpToBookmark(historyEvent); if (jumpableMachine is IMachine machine) { machine.Status = String.Format("Jumped to {0}", Helpers.GetTimeSpanFromTicks(historyEvent.Ticks).ToString(@"hh\:mm\:ss")); } } } }
public bool IsEqualToOrAncestorOf(HistoryEvent ancestor) { return(Node.IsEqualToOrAncestorOf(ancestor?.Node)); }
public bool DeleteBookmark(HistoryEvent e) { return(_history.DeleteBookmark(e)); }
/// <summary> /// Delegate for logging core actions. /// </summary> /// <param name="core">The core the request was made for.</param> /// <param name="request">The original request.</param> /// <param name="action">The action taken.</param> private void RequestProcessed(Core core, CoreRequest request, CoreAction action) { if (core == _core) { if (action != null) { Auditors?.Invoke(action); if (action.Type != CoreAction.Types.CreateSnapshot && action.Type != CoreAction.Types.DeleteSnapshot && action.Type != CoreAction.Types.RevertToSnapshot) { HistoryEvent e = _history.AddCoreAction(action); switch (action.Type) { case CoreRequest.Types.LoadDisc: Status = (action.MediaBuffer.GetBytes() != null) ? "Loaded disc" : "Ejected disc"; break; case CoreRequest.Types.LoadTape: Status = (action.MediaBuffer.GetBytes() != null) ? "Loaded tape" : "Ejected tape"; break; case CoreRequest.Types.Reset: Status = "Reset"; break; } } if (action.Type == CoreAction.Types.RunUntil) { lock (_snapshots) { SnapshotInfo newSnapshot = _snapshots.LastOrDefault(); if (newSnapshot != null && action.AudioSamples != null) { newSnapshot.AudioBuffer.Write(action.AudioSamples); } } } else if (action.Type == CoreAction.Types.RevertToSnapshot) { HistoryEvent historyEvent = ((SnapshotInfo)request.UserData).HistoryEvent; if (_history.CurrentEvent != historyEvent) { _history.CurrentEvent = historyEvent; } Display.CopyScreenAsync(); } else if (action.Type == CoreAction.Types.CreateSnapshot) { lock (_snapshots) { // Figure out what history event should be set as current if we revert to this snapshot. // If the current event is a RunUntil, it may not be "finalized" yet (i.e. it may still // be updated), so go with its parent. HistoryEvent historyEvent = _history.MostRecentClosedEvent(_history.CurrentEvent); SnapshotInfo newSnapshot = new SnapshotInfo(action.SnapshotId, historyEvent); _snapshots.Add(newSnapshot); while (_snapshots.Count > _snapshotLimit) { SnapshotInfo snapshot = _snapshots[0]; _snapshots.RemoveAt(0); _core.PushRequest(CoreRequest.DeleteSnapshot(snapshot.Id)); } } } } } }
public SnapshotInfo(int id, HistoryEvent historyEvent) { Id = id; AudioBuffer = new AudioBuffer(-1); HistoryEvent = historyEvent; }
private void Notify(HistoryEvent historyEvent, HistoryChangedAction action) { Auditors?.Invoke(historyEvent, action); }
public void WriteHistory(string name) { List <string> lines = new List <string> { NameCommand(name) }; // As the history tree could be very deep, keep a "stack" of history events in order to avoid recursive calls. List <HistoryEvent> historyEvents = new List <HistoryEvent>(); historyEvents.AddRange(History.RootEvent.Children); HistoryEvent previousEvent = null; while (historyEvents.Count > 0) { HistoryEvent currentEvent = historyEvents[0]; if (previousEvent != currentEvent.Parent && previousEvent != null) { lines.Add(CurrentCommand(currentEvent.Parent.Id)); } GetLines(currentEvent, lines); historyEvents.RemoveAt(0); previousEvent = currentEvent; // Place the current event's children at the top of the "stack". This effectively means we're doing a depth-first traversion of the history tree. historyEvents.InsertRange(0, currentEvent.Children); } lines.Add(CurrentCommand(History.CurrentEvent.Id)); // If we have any "arg" commands, stick them in a "args" command and put them at the start of the file. // Putting them in a single command should allow them to be better compressed than individually. Dictionary <int, string> args = new Dictionary <int, string>(); int i = 0; while (i < lines.Count) { string[] tokens = lines[i].Split(':'); if (tokens[0] == _idArg) { lines.RemoveAt(i); MachineFileReader.ReadArgCommand(tokens[1], args); } else { i++; } } // Write the file. if (args.Count > 0) { lines.Insert(0, ArgsCommand(args, true)); } foreach (string line in lines) { _textFile.WriteLine(line); } }
private int EventAncestorIndex(HistoryEvent historyEvent) { return(Events.FindIndex(descendant => historyEvent?.IsEqualToOrAncestorOf(descendant) ?? false)); }
private int EventIndex(HistoryEvent historyEvent) { return(Events.IndexOf(historyEvent)); }