OXYGENHUB
◆ main openmsx / src / input / RecordedCommand.cc
2.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "RecordedCommand.hh"
2
3
#include "Scheduler.hh"
4
#include "StateChange.hh"
5
#include "StateChangeDistributor.hh"
6
#include "TclObject.hh"
7
8
#include "ScopedAssign.hh"
9
#include "dynarray.hh"
10
#include "serialize.hh"
11
#include "serialize_stl.hh"
12
#include "stl.hh"
13
14
#include <ranges>
15
#include <string>
16
#include <vector>
17
18
namespace openmsx {
19
20
RecordedCommand::RecordedCommand(CommandController& commandController_,
21
                                 StateChangeDistributor& stateChangeDistributor_,
22
                                 Scheduler& scheduler_,
23
                                 std::string_view name_)
24
	: Command(commandController_, name_)
25
	, stateChangeDistributor(stateChangeDistributor_)
26
	, scheduler(scheduler_)
27
	, currentResultObject(&dummyResultObject)
28
{
29
	stateChangeDistributor.registerListener(*this);
30
}
31
32
RecordedCommand::~RecordedCommand()
33
{
34
	stateChangeDistributor.unregisterListener(*this);
35
}
36
37
void RecordedCommand::execute(std::span<const TclObject> tokens, TclObject& result)
38
{
39
	auto time = scheduler.getCurrentTime();
40
	if (needRecord(tokens)) {
41
		ScopedAssign sa(currentResultObject, &result);
42
		stateChangeDistributor.distributeNew<MSXCommandEvent>(time, tokens);
43
	} else {
44
		execute(tokens, result, time);
45
	}
46
}
47
48
bool RecordedCommand::needRecord(std::span<const TclObject> /*tokens*/) const
49
{
50
	return true;
51
}
52
53
[[nodiscard]] static std::string_view getBaseName(std::string_view str)
54
{
55
	auto pos = str.rfind("::");
56
	return (pos == std::string_view::npos) ? str : str.substr(pos + 2);
57
}
58
59
void RecordedCommand::signalStateChange(const StateChange& event)
60
{
61
	const auto* commandEvent = std::get_if<MSXCommandEvent>(&event);
62
	if (!commandEvent) return;
63
64
	const auto& tokens = commandEvent->getTokens();
65
	if (getBaseName(tokens[0].getString()) != getName()) return;
66
67
	if (needRecord(tokens)) {
68
		execute(tokens, *currentResultObject, commandEvent->getTime());
69
	} else {
70
		// Normally this shouldn't happen. But it's possible in case
71
		// we're replaying a replay file that has manual edits in the
72
		// event log. It's crucial for security that we don't blindly
73
		// execute such commands. We already only execute
74
		// RecordedCommands, but we also need a strict check that
75
		// only commands that would be recorded are also replayed.
76
		// For example:
77
		//   debug set_bp 0x0038 true {<some-arbitrary-Tcl-command>}
78
		// The debug write/write_block commands should be recorded and
79
		// replayed, but via the set_bp it would be possible to
80
		// execute arbitrary Tcl code.
81
	}
82
}
83
84
void RecordedCommand::stopReplay(EmuTime /*time*/) noexcept
85
{
86
	// nothing
87
}
88
89
} // namespace openmsx