openMSX
RecordedCommand.cc
Go to the documentation of this file.
1#include "RecordedCommand.hh"
3#include "TclObject.hh"
4#include "Scheduler.hh"
5#include "StateChange.hh"
6#include "ScopedAssign.hh"
7#include "dynarray.hh"
8#include "serialize.hh"
9#include "serialize_stl.hh"
10#include "stl.hh"
11#include "view.hh"
12#include "xrange.hh"
13#include <string>
14#include <vector>
15
16namespace openmsx {
17
19 StateChangeDistributor& stateChangeDistributor_,
20 Scheduler& scheduler_,
21 std::string_view name_)
22 : Command(commandController_, name_)
23 , stateChangeDistributor(stateChangeDistributor_)
24 , scheduler(scheduler_)
25 , currentResultObject(&dummyResultObject)
26{
27 stateChangeDistributor.registerListener(*this);
28}
29
31{
32 stateChangeDistributor.unregisterListener(*this);
33}
34
35void RecordedCommand::execute(std::span<const TclObject> tokens, TclObject& result)
36{
37 auto time = scheduler.getCurrentTime();
38 if (needRecord(tokens)) {
39 ScopedAssign sa(currentResultObject, &result);
40 stateChangeDistributor.distributeNew<MSXCommandEvent>(time, tokens);
41 } else {
42 execute(tokens, result, time);
43 }
44}
45
46bool RecordedCommand::needRecord(std::span<const TclObject> /*tokens*/) const
47{
48 return true;
49}
50
51[[nodiscard]] static std::string_view getBaseName(std::string_view str)
52{
53 auto pos = str.rfind("::");
54 return (pos == std::string_view::npos) ? str : str.substr(pos + 2);
55}
56
57void RecordedCommand::signalStateChange(const StateChange& event)
58{
59 const auto* commandEvent = dynamic_cast<const MSXCommandEvent*>(&event);
60 if (!commandEvent) return;
61
62 const auto& tokens = commandEvent->getTokens();
63 if (getBaseName(tokens[0].getString()) != getName()) return;
64
65 if (needRecord(tokens)) {
66 execute(tokens, *currentResultObject, commandEvent->getTime());
67 } else {
68 // Normally this shouldn't happen. But it's possible in case
69 // we're replaying a replay file that has manual edits in the
70 // event log. It's crucial for security that we don't blindly
71 // execute such commands. We already only execute
72 // RecordedCommands, but we also need a strict check that
73 // only commands that would be recorded are also replayed.
74 // For example:
75 // debug set_bp 0x0038 true {<some-arbitrary-Tcl-command>}
76 // The debug write/write_block commands should be recorded and
77 // replayed, but via the set_bp it would be possible to
78 // execute arbitrary Tcl code.
79 }
80}
81
82void RecordedCommand::stopReplay(EmuTime::param /*time*/) noexcept
83{
84 // nothing
85}
86
87
88// class MSXCommandEvent
89
90MSXCommandEvent::MSXCommandEvent(EmuTime::param time_, std::span<const TclObject> tokens_)
91 : StateChange(time_)
92 , tokens(dynarray<TclObject>::construct_from_range_tag{}, tokens_)
93{
94}
95
96template<typename Archive>
97void MSXCommandEvent::serialize(Archive& ar, unsigned /*version*/)
98{
99 ar.template serializeBase<StateChange>(*this);
100
101 // serialize vector<TclObject> as vector<string>
102 std::vector<std::string> str;
103 if constexpr (!Archive::IS_LOADER) {
105 tokens, [](auto& t) { return std::string(t.getString()); }));
106 }
107 ar.serialize("tokens", str);
108 if constexpr (Archive::IS_LOADER) {
109 assert(tokens.empty());
111 view::transform(str, [](const auto& s) { return TclObject(s); }));
112 }
113}
115
116} // namespace openmsx
TclObject t
Assign new value to some variable and restore the original value when this object goes out of scope.
const std::string & getName() const
Definition Completer.hh:23
This class is used to for Tcl commands that directly influence the MSX state (e.g.
void serialize(Archive &ar, unsigned version)
virtual bool needRecord(std::span< const TclObject > tokens) const
It's possible that in some cases the command doesn't need to be recorded after all (e....
virtual void execute(std::span< const TclObject > tokens, TclObject &result, EmuTime::param time)=0
This is like the execute() method of the Command class, it only has an extra time parameter.
RecordedCommand(CommandController &commandController, StateChangeDistributor &stateChangeDistributor, Scheduler &scheduler, std::string_view name)
EmuTime::param getCurrentTime() const
Get the current scheduler time.
Definition Scheduler.cc:84
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
void distributeNew(EmuTime::param time, Args &&...args)
Deliver the event to all registered listeners MSX input devices should call the distributeNew() versi...
void unregisterListener(StateChangeListener &listener)
Base class for all external MSX state changing events.
This file implemented 3 utility functions:
Definition Autofire.cc:9
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
#define REGISTER_POLYMORPHIC_CLASS(BASE, CLASS, NAME)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
Definition stl.hh:275