openMSX
ReverseManager.hh
Go to the documentation of this file.
1#ifndef REVERSEMANGER_HH
2#define REVERSEMANGER_HH
3
4#include "Schedulable.hh"
5#include "EventListener.hh"
6#include "Command.hh"
7#include "EmuTime.hh"
8
9#include "MemBuffer.hh"
10#include "DeltaBlock.hh"
11#include "outer.hh"
12#include "view.hh"
13
14#include <cstdint>
15#include <deque>
16#include <span>
17#include <map>
18#include <memory>
19#include <string_view>
20#include <vector>
21
22namespace openmsx {
23
24class EventDelay;
25class EventDistributor;
26class Interpreter;
27class MSXMotherBoard;
28class Keyboard;
29class StateChange;
30class TclObject;
31
32class ReverseManager final : private EventListener
33{
34public:
35 static constexpr std::string_view REPLAY_DIR = "replays";
36 static constexpr std::string_view REPLAY_EXTENSION = ".omr";
37
38public:
39 explicit ReverseManager(MSXMotherBoard& motherBoard);
41
42 // Keyboard is special because we need to transfer the host keyboard
43 // state on 'reverse goto' to be able to resynchronize when replay
44 // stops. See Keyboard::transferHostKeyMatrix() for more info.
45 void registerKeyboard(Keyboard& keyboard_) {
46 keyboard = &keyboard_;
47 }
48
49 // To not loose any events we need to flush delayed events before
50 // switching machine. See comments in goTo() for more info.
51 void registerEventDelay(EventDelay& eventDelay_) {
52 eventDelay = &eventDelay_;
53 }
54
55 // Should only be used by MSXMotherBoard to be able to transfer
56 // reRecordCount to ReverseManager for version 2 of MSXMotherBoard
57 // serializers.
58 void setReRecordCount(unsigned count) {
59 reRecordCount = count;
60 }
61
62 [[nodiscard]] bool isReplaying() const;
63 void stopReplay(EmuTime::param time) noexcept;
64
65 template<typename T, typename... Args>
66 StateChange& record(EmuTime::param time, Args&& ...args) {
67 assert(!isReplaying());
68 ++replayIndex;
69 history.events.push_back(std::make_unique<T>(time, std::forward<Args>(args)...));
70 return *history.events.back();
71 }
72
73 [[nodiscard]] bool isCollecting() const { return collecting; }
74 [[nodiscard]] bool isViewOnlyMode() const;
75 [[nodiscard]] double getBegin() const;
76 [[nodiscard]] double getEnd() const;
77 [[nodiscard]] double getCurrent() const;
78 [[nodiscard]] auto getSnapshotTimes() const {
79 return view::transform(history.chunks, [](auto& p) {
80 return (p.second.time - EmuTime::zero()).toDouble();
81 });
82 }
83
84private:
85 struct ReverseChunk {
86 EmuTime time = EmuTime::zero();
87 std::vector<std::shared_ptr<DeltaBlock>> deltaBlocks;
88 MemBuffer<uint8_t> savestate;
89 size_t size;
90
91 // Number of recorded events (or replay index) when this
92 // snapshot was created. So when going back replay should
93 // start at this index.
94 unsigned eventCount;
95 };
96 using Chunks = std::map<unsigned, ReverseChunk>;
97 using Events = std::deque<std::unique_ptr<StateChange>>;
98
99 struct ReverseHistory {
100 void swap(ReverseHistory& other) noexcept;
101 void clear();
102 [[nodiscard]] unsigned getNextSeqNum(EmuTime::param time) const;
103
104 Chunks chunks;
105 Events events;
106 LastDeltaBlocks lastDeltaBlocks;
107 };
108
109 void start();
110 void stop();
111 void status(TclObject& result) const;
112 void debugInfo(TclObject& result) const;
113 void goBack(std::span<const TclObject> tokens);
114 void goTo(std::span<const TclObject> tokens);
115 void saveReplay(Interpreter& interp,
116 std::span<const TclObject> tokens, TclObject& result);
117 void loadReplay(Interpreter& interp,
118 std::span<const TclObject> tokens, TclObject& result);
119
120 void signalStopReplay(EmuTime::param time);
121 [[nodiscard]] EmuTime::param getEndTime(const ReverseHistory& history) const;
122 void goTo(EmuTime::param targetTime, bool noVideo);
123 void goTo(EmuTime::param targetTime, bool noVideo,
124 ReverseHistory& history, bool sameTimeLine);
125 void transferHistory(ReverseHistory& oldHistory,
126 unsigned oldEventCount);
127 void transferState(MSXMotherBoard& newBoard);
128 void takeSnapshot(EmuTime::param time);
129 void schedule(EmuTime::param time);
130 void replayNextEvent();
131 template<unsigned N> void dropOldSnapshots(unsigned count);
132
133 // Schedulable
134 struct SyncNewSnapshot final : Schedulable {
135 friend class ReverseManager;
136 explicit SyncNewSnapshot(Scheduler& s) : Schedulable(s) {}
137 void executeUntil(EmuTime::param /*time*/) override {
138 auto& rm = OUTER(ReverseManager, syncNewSnapshot);
139 rm.execNewSnapshot();
140 }
141 } syncNewSnapshot;
142 struct SyncInputEvent final : Schedulable {
143 friend class ReverseManager;
144 explicit SyncInputEvent(Scheduler& s) : Schedulable(s) {}
145 void executeUntil(EmuTime::param /*time*/) override {
146 auto& rm = OUTER(ReverseManager, syncInputEvent);
147 rm.execInputEvent();
148 }
149 } syncInputEvent;
150
151 void execNewSnapshot();
152 void execInputEvent();
153 [[nodiscard]] EmuTime::param getCurrentTime() const { return syncNewSnapshot.getCurrentTime(); }
154
155 // EventListener
156 bool signalEvent(const Event& event) override;
157
158private:
159 MSXMotherBoard& motherBoard;
160 EventDistributor& eventDistributor;
161
162 struct ReverseCmd final : Command {
163 explicit ReverseCmd(CommandController& controller);
164 void execute(std::span<const TclObject> tokens, TclObject& result) override;
165 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
166 void tabCompletion(std::vector<std::string>& tokens) const override;
167 } reverseCmd;
168
169 Keyboard* keyboard = nullptr;
170 EventDelay* eventDelay = nullptr;
171 ReverseHistory history;
172 unsigned replayIndex = 0;
173 bool collecting = false;
174 bool pendingTakeSnapshot = false;
175
176 unsigned reRecordCount = 0;
177
178 friend struct Replay;
179};
180
181} // namespace openmsx
182
183#endif
This class is responsible for translating host events into MSX events.
Definition EventDelay.hh:27
This class manages the lifetime of a block of memory.
Definition MemBuffer.hh:29
void setReRecordCount(unsigned count)
void registerKeyboard(Keyboard &keyboard_)
void stopReplay(EmuTime::param time) noexcept
static constexpr std::string_view REPLAY_DIR
void registerEventDelay(EventDelay &eventDelay_)
static constexpr std::string_view REPLAY_EXTENSION
StateChange & record(EmuTime::param time, Args &&...args)
Schedulable(const Schedulable &)=delete
Base class for all external MSX state changing events.
This file implemented 3 utility functions:
Definition Autofire.cc:11
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:445
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
#define OUTER(type, member)
Definition outer.hh:42