yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
emulator_commands.cc
Go to the documentation of this file.
2
4
5#include <grpcpp/grpcpp.h>
6
7#include "absl/status/statusor.h"
8#include "absl/strings/escaping.h"
9#include "absl/strings/str_format.h"
10#include "absl/strings/str_split.h"
11#include "absl/strings/string_view.h"
12#include "absl/time/time.h"
13#include "cli/util/hex_util.h"
14#include "protos/emulator_service.grpc.pb.h"
15
16namespace yaze {
17namespace cli {
18namespace handlers {
19
21
22namespace {
23
24// A simple client for the EmulatorService
26 public:
28 auto channel = grpc::CreateChannel("localhost:50051",
29 grpc::InsecureChannelCredentials());
30 stub_ = agent::EmulatorService::NewStub(channel);
31 }
32
33 template <typename TRequest, typename TResponse>
34 absl::StatusOr<TResponse> CallRpc(
35 grpc::Status (agent::EmulatorService::Stub::*rpc_method)(
36 grpc::ClientContext*, const TRequest&, TResponse*),
37 const TRequest& request) {
38 TResponse response;
39 grpc::ClientContext context;
40
41 auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(5);
42 context.set_deadline(deadline);
43
44 grpc::Status status =
45 (stub_.get()->*rpc_method)(&context, request, &response);
46
47 if (!status.ok()) {
48 return absl::UnavailableError(absl::StrFormat(
49 "RPC failed: (%d) %s", status.error_code(), status.error_message()));
50 }
51 return response;
52 }
53
54 private:
55 std::unique_ptr<agent::EmulatorService::Stub> stub_;
56};
57
58// Helper to parse button from string
59absl::StatusOr<agent::Button> StringToButton(absl::string_view s) {
60 if (s == "A")
61 return agent::Button::A;
62 if (s == "B")
63 return agent::Button::B;
64 if (s == "X")
65 return agent::Button::X;
66 if (s == "Y")
67 return agent::Button::Y;
68 if (s == "L")
69 return agent::Button::L;
70 if (s == "R")
71 return agent::Button::R;
72 if (s == "SELECT")
73 return agent::Button::SELECT;
74 if (s == "START")
75 return agent::Button::START;
76 if (s == "UP")
77 return agent::Button::UP;
78 if (s == "DOWN")
79 return agent::Button::DOWN;
80 if (s == "LEFT")
81 return agent::Button::LEFT;
82 if (s == "RIGHT")
83 return agent::Button::RIGHT;
84 return absl::InvalidArgumentError(absl::StrCat("Unknown button: ", s));
85}
86
87} // namespace
88
89// --- Command Implementations ---
90
92 Rom* rom, const resources::ArgumentParser& parser,
93 resources::OutputFormatter& formatter) {
94 EmulatorClient client;
95 agent::ControlRequest request;
96 request.set_action("reset");
97 auto response_or =
98 client.CallRpc(&agent::EmulatorService::Stub::ControlEmulator, request);
99 if (!response_or.ok()) {
100 return response_or.status();
101 }
102 auto response = response_or.value();
103
104 formatter.BeginObject("EmulatorReset");
105 formatter.AddField("success", response.success());
106 formatter.AddField("message", response.message());
107 formatter.EndObject();
108 return absl::OkStatus();
109}
110
112 Rom* rom, const resources::ArgumentParser& parser,
113 resources::OutputFormatter& formatter) {
114 EmulatorClient client;
115 agent::GameStateRequest request;
116 request.set_include_screenshot(parser.HasFlag("screenshot"));
117
118 auto response_or =
119 client.CallRpc(&agent::EmulatorService::Stub::GetGameState, request);
120 if (!response_or.ok()) {
121 return response_or.status();
122 }
123 auto response = response_or.value();
124
125 formatter.BeginObject("EmulatorState");
126 formatter.AddField("game_mode", static_cast<uint64_t>(response.game_mode()));
127 formatter.AddField("link_state",
128 static_cast<uint64_t>(response.link_state()));
129 formatter.AddField("link_pos_x",
130 static_cast<uint64_t>(response.link_pos_x()));
131 formatter.AddField("link_pos_y",
132 static_cast<uint64_t>(response.link_pos_y()));
133 formatter.AddField("link_health",
134 static_cast<uint64_t>(response.link_health()));
135 if (!response.screenshot_png().empty()) {
136 formatter.AddField("screenshot_size",
137 static_cast<uint64_t>(response.screenshot_png().size()));
138 }
139 formatter.EndObject();
140 return absl::OkStatus();
141}
142
144 Rom* rom, const resources::ArgumentParser& parser,
145 resources::OutputFormatter& formatter) {
146 EmulatorClient client;
147 agent::MemoryRequest request;
148
149 uint32_t address;
150 if (!ParseHexString(parser.GetString("address").value(), &address)) {
151 return absl::InvalidArgumentError("Invalid address format.");
152 }
153 request.set_address(address);
154 request.set_size(parser.GetInt("length").value_or(16));
155
156 auto response_or =
157 client.CallRpc(&agent::EmulatorService::Stub::ReadMemory, request);
158 if (!response_or.ok()) {
159 return response_or.status();
160 }
161 auto response = response_or.value();
162
163 formatter.BeginObject("MemoryRead");
164 formatter.AddHexField("address", response.address());
165 formatter.AddField("data_hex", absl::BytesToHexString(response.data()));
166 formatter.EndObject();
167 return absl::OkStatus();
168}
169
171 Rom* rom, const resources::ArgumentParser& parser,
172 resources::OutputFormatter& formatter) {
173 EmulatorClient client;
174 agent::MemoryWriteRequest request;
175
176 uint32_t address;
177 if (!ParseHexString(parser.GetString("address").value(), &address)) {
178 return absl::InvalidArgumentError("Invalid address format.");
179 }
180 request.set_address(address);
181
182 std::string data_hex = parser.GetString("data").value();
183 request.set_data(absl::HexStringToBytes(data_hex));
184
185 auto response_or =
186 client.CallRpc(&agent::EmulatorService::Stub::WriteMemory, request);
187 if (!response_or.ok()) {
188 return response_or.status();
189 }
190 auto response = response_or.value();
191
192 formatter.BeginObject("MemoryWrite");
193 formatter.AddField("success", response.success());
194 formatter.AddField("message", response.message());
195 formatter.EndObject();
196 return absl::OkStatus();
197}
198
200 Rom* rom, const resources::ArgumentParser& parser,
201 resources::OutputFormatter& formatter) {
202 EmulatorClient client;
203 agent::ButtonRequest request;
204 std::vector<std::string> buttons =
205 absl::StrSplit(parser.GetString("buttons").value(), ',');
206 for (const auto& btn_str : buttons) {
207 auto button_or = StringToButton(btn_str);
208 if (!button_or.ok())
209 return button_or.status();
210 request.add_buttons(button_or.value());
211 }
212
213 auto response_or =
214 client.CallRpc(&agent::EmulatorService::Stub::PressButtons, request);
215 if (!response_or.ok()) {
216 return response_or.status();
217 }
218 auto response = response_or.value();
219
220 formatter.BeginObject("PressButtons");
221 formatter.AddField("success", response.success());
222 formatter.AddField("message", response.message());
223 formatter.EndObject();
224 return absl::OkStatus();
225}
226
228 Rom* rom, const resources::ArgumentParser& parser,
229 resources::OutputFormatter& formatter) {
230 EmulatorClient client;
231 agent::ButtonRequest request;
232 std::vector<std::string> buttons =
233 absl::StrSplit(parser.GetString("buttons").value(), ',');
234 for (const auto& btn_str : buttons) {
235 auto button_or = StringToButton(btn_str);
236 if (!button_or.ok())
237 return button_or.status();
238 request.add_buttons(button_or.value());
239 }
240
241 auto response_or =
242 client.CallRpc(&agent::EmulatorService::Stub::ReleaseButtons, request);
243 if (!response_or.ok()) {
244 return response_or.status();
245 }
246 auto response = response_or.value();
247
248 formatter.BeginObject("ReleaseButtons");
249 formatter.AddField("success", response.success());
250 formatter.AddField("message", response.message());
251 formatter.EndObject();
252 return absl::OkStatus();
253}
254
256 Rom* rom, const resources::ArgumentParser& parser,
257 resources::OutputFormatter& formatter) {
258 EmulatorClient client;
259 agent::ButtonHoldRequest request;
260 std::vector<std::string> buttons =
261 absl::StrSplit(parser.GetString("buttons").value(), ',');
262 for (const auto& btn_str : buttons) {
263 auto button_or = StringToButton(btn_str);
264 if (!button_or.ok())
265 return button_or.status();
266 request.add_buttons(button_or.value());
267 }
268 request.set_duration_ms(parser.GetInt("duration").value());
269
270 auto response_or =
271 client.CallRpc(&agent::EmulatorService::Stub::HoldButtons, request);
272 if (!response_or.ok()) {
273 return response_or.status();
274 }
275 auto response = response_or.value();
276
277 formatter.BeginObject("HoldButtons");
278 formatter.AddField("success", response.success());
279 formatter.AddField("message", response.message());
280 formatter.EndObject();
281 return absl::OkStatus();
282}
283
284// --- Placeholder Implementations for commands not yet migrated to gRPC ---
285
287 Rom* rom, const resources::ArgumentParser& parser,
288 resources::OutputFormatter& formatter) {
289 formatter.BeginObject("Emulator Step");
290 formatter.AddField("status", "not_implemented");
291 formatter.EndObject();
292 return absl::OkStatus();
293}
294
296 Rom* rom, const resources::ArgumentParser& parser,
297 resources::OutputFormatter& formatter) {
298 formatter.BeginObject("Emulator Run");
299 formatter.AddField("status", "not_implemented");
300 formatter.EndObject();
301 return absl::OkStatus();
302}
303
305 Rom* rom, const resources::ArgumentParser& parser,
306 resources::OutputFormatter& formatter) {
307 formatter.BeginObject("Emulator Pause");
308 formatter.AddField("status", "not_implemented");
309 formatter.EndObject();
310 return absl::OkStatus();
311}
312
314 Rom* rom, const resources::ArgumentParser& parser,
315 resources::OutputFormatter& formatter) {
316 formatter.BeginObject("Emulator Breakpoint Set");
317 formatter.AddField("status", "not_implemented");
318 formatter.EndObject();
319 return absl::OkStatus();
320}
321
323 Rom* rom, const resources::ArgumentParser& parser,
324 resources::OutputFormatter& formatter) {
325 formatter.BeginObject("Emulator Breakpoint Cleared");
326 formatter.AddField("status", "not_implemented");
327 formatter.EndObject();
328 return absl::OkStatus();
329}
330
332 Rom* rom, const resources::ArgumentParser& parser,
333 resources::OutputFormatter& formatter) {
334 formatter.BeginObject("Emulator Breakpoints");
335 formatter.AddField("status", "not_implemented");
336 formatter.EndObject();
337 return absl::OkStatus();
338}
339
341 Rom* rom, const resources::ArgumentParser& parser,
342 resources::OutputFormatter& formatter) {
343 formatter.BeginObject("Emulator Registers");
344 formatter.AddField("status", "not_implemented");
345 formatter.EndObject();
346 return absl::OkStatus();
347}
348
350 Rom* rom, const resources::ArgumentParser& parser,
351 resources::OutputFormatter& formatter) {
352 formatter.BeginObject("Emulator Metrics");
353 formatter.AddField("status", "not_implemented");
354 formatter.EndObject();
355 return absl::OkStatus();
356}
357
358} // namespace handlers
359} // namespace cli
360} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::StatusOr< TResponse > CallRpc(grpc::Status(agent::EmulatorService::Stub::*rpc_method)(grpc::ClientContext *, const TRequest &, TResponse *), const TRequest &request)
Utility for parsing common CLI argument patterns.
std::optional< std::string > GetString(const std::string &name) const
Parse a named argument (e.g., –format=json or –format json)
bool HasFlag(const std::string &name) const
Check if a flag is present.
absl::StatusOr< int > GetInt(const std::string &name) const
Parse an integer argument (supports hex with 0x prefix)
Utility for consistent output formatting across commands.
void BeginObject(const std::string &title="")
Start a JSON object or text section.
void EndObject()
End a JSON object or text section.
void AddField(const std::string &key, const std::string &value)
Add a key-value pair.
void AddHexField(const std::string &key, uint64_t value, int width=2)
Add a hex-formatted field.
absl::StatusOr< agent::Button > StringToButton(absl::string_view s)
bool ParseHexString(absl::string_view str, int *out)
Definition hex_util.h:17