OXYGENHUB
◆ main openmsx / src / ReverseManager.hh
4.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef REVERSEMANGER_HH
2
#define REVERSEMANGER_HH
3
4
#include "Command.hh"
5
#include "EmuTime.hh"
6
#include "EventListener.hh"
7
#include "Schedulable.hh"
8
#include "StateChange.hh"
9
10
#include "DeltaBlock.hh"
11
#include "MemBuffer.hh"
12
#include "outer.hh"
13
14
#include <cstdint>
15
#include <deque>
16
#include <map>
17
#include <memory>
18
#include <ranges>
19
#include <span>
20
#include <string_view>
21
#include <vector>
22
23
namespace openmsx {
24
25
class EventDelay;
26
class EventDistributor;
27
class Interpreter;
28
class MSXMotherBoard;
29
class TclObject;
30
31
class ReverseManager final : private EventListener
32
{
33
public:
34
	static constexpr std::string_view REPLAY_DIR = "replays";
35
	static constexpr std::string_view REPLAY_EXTENSION = ".omr";
36
37
public:
38
	explicit ReverseManager(MSXMotherBoard& motherBoard);
39
	~ReverseManager();
40
41
	// To not loose any events we need to flush delayed events before
42
	// switching machine. See comments in goTo() for more info.
43
	void registerEventDelay(EventDelay& eventDelay_) {
44
		eventDelay = &eventDelay_;
45
	}
46
47
	// Should only be used by MSXMotherBoard to be able to transfer
48
	// reRecordCount to ReverseManager for version 2 of MSXMotherBoard
49
	// serializers.
50
	void setReRecordCount(unsigned count) {
51
		reRecordCount = count;
52
	}
53
54
	[[nodiscard]] bool isReplaying() const;
55
	void stopReplay(EmuTime time) noexcept;
56
57
	template<typename T, typename... Args>
58
	StateChange& record(EmuTime time, Args&& ...args) {
59
		assert(!isReplaying());
60
		++replayIndex;
61
		history.events.emplace_back(std::in_place_type_t<T>{}, time, std::forward<Args>(args)...);
62
		return history.events.back();
63
	}
64
65
	[[nodiscard]] bool isCollecting() const { return collecting; }
66
	[[nodiscard]] bool isViewOnlyMode() const;
67
	void setViewOnlyMode(bool value);
68
	[[nodiscard]] double getBegin() const;
69
	[[nodiscard]] double getEnd() const;
70
	[[nodiscard]] double getCurrent() const;
71
	[[nodiscard]] auto getSnapshotTimes() const {
72
		return std::views::transform(history.chunks, [](auto& p) {
73
			return p.second.time.toDouble();
74
		});
75
	}
76
77
private:
78
	struct ReverseChunk {
79
		EmuTime time = EmuTime::zero();
80
		std::vector<std::shared_ptr<DeltaBlock>> deltaBlocks;
81
		MemBuffer<uint8_t> savestate;
82
83
		// Number of recorded events (or replay index) when this
84
		// snapshot was created. So when going back replay should
85
		// start at this index.
86
		unsigned eventCount;
87
	};
88
	using Chunks = std::map<unsigned, ReverseChunk>;
89
	using Events = std::deque<StateChange>;
90
91
	struct ReverseHistory {
92
		void swap(ReverseHistory& other) noexcept;
93
		void clear();
94
		[[nodiscard]] unsigned getNextSeqNum(EmuTime time) const;
95
96
		Chunks chunks;
97
		Events events;
98
		LastDeltaBlocks lastDeltaBlocks;
99
	};
100
101
	void start();
102
	void stop();
103
	void status(TclObject& result) const;
104
	void debugInfo(TclObject& result) const;
105
	void goBack(std::span<const TclObject> tokens);
106
	void goTo(std::span<const TclObject> tokens);
107
	void saveReplay(Interpreter& interp,
108
	                std::span<const TclObject> tokens, TclObject& result);
109
	void loadReplay(Interpreter& interp,
110
	                std::span<const TclObject> tokens, TclObject& result);
111
112
	void signalStopReplay(EmuTime time);
113
	[[nodiscard]] EmuTime getEndTime(const ReverseHistory& history) const;
114
	void goTo(EmuTime targetTime, bool noVideo);
115
	void goTo(EmuTime targetTime, bool noVideo,
116
	          ReverseHistory& history, bool sameTimeLine);
117
	void transferHistory(ReverseHistory& oldHistory,
118
	                     unsigned oldEventCount);
119
	void transferState(MSXMotherBoard& newBoard);
120
	void takeSnapshot(EmuTime time);
121
	void schedule(EmuTime time);
122
	void replayNextEvent();
123
	template<unsigned N> void dropOldSnapshots(unsigned count);
124
125
	// Schedulable
126
	struct SyncNewSnapshot final : Schedulable {
127
		friend class ReverseManager;
128
		explicit SyncNewSnapshot(Scheduler& s) : Schedulable(s) {}
129
		void executeUntil(EmuTime /*time*/) override {
130
			auto& rm = OUTER(ReverseManager, syncNewSnapshot);
131
			rm.execNewSnapshot();
132
		}
133
	} syncNewSnapshot;
134
	struct SyncInputEvent final : Schedulable {
135
		friend class ReverseManager;
136
		explicit SyncInputEvent(Scheduler& s) : Schedulable(s) {}
137
		void executeUntil(EmuTime /*time*/) override {
138
			auto& rm = OUTER(ReverseManager, syncInputEvent);
139
			rm.execInputEvent();
140
		}
141
	} syncInputEvent;
142
143
	void execNewSnapshot();
144
	void execInputEvent();
145
	[[nodiscard]] EmuTime getCurrentTime() const { return syncNewSnapshot.getCurrentTime(); }
146
147
	// EventListener
148
	bool signalEvent(const Event& event) override;
149
150
private:
151
	MSXMotherBoard& motherBoard;
152
	EventDistributor& eventDistributor;
153
154
	struct ReverseCmd final : Command {
155
		explicit ReverseCmd(CommandController& controller);
156
		void execute(std::span<const TclObject> tokens, TclObject& result) override;
157
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
158
		void tabCompletion(std::vector<std::string>& tokens) const override;
159
	} reverseCmd;
160
161
	EventDelay* eventDelay = nullptr;
162
	ReverseHistory history;
163
	unsigned replayIndex = 0;
164
	bool collecting = false;
165
	bool pendingTakeSnapshot = false;
166
167
	unsigned reRecordCount = 0;
168
169
	friend struct Replay;
170
};
171
172
} // namespace openmsx
173
174
#endif