5#include <grpcpp/grpcpp.h>
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"
14#include "protos/emulator_service.grpc.pb.h"
28 auto channel = grpc::CreateChannel(
"localhost:50051",
29 grpc::InsecureChannelCredentials());
30 stub_ = agent::EmulatorService::NewStub(channel);
33 template <
typename TRequest,
typename TResponse>
35 grpc::Status (agent::EmulatorService::Stub::*rpc_method)(
36 grpc::ClientContext*, const TRequest&, TResponse*),
37 const TRequest& request) {
39 grpc::ClientContext context;
41 auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(5);
42 context.set_deadline(deadline);
45 (stub_.get()->*rpc_method)(&context, request, &response);
48 return absl::UnavailableError(absl::StrFormat(
49 "RPC failed: (%d) %s", status.error_code(), status.error_message()));
55 std::unique_ptr<agent::EmulatorService::Stub>
stub_;
61 return agent::Button::A;
63 return agent::Button::B;
65 return agent::Button::X;
67 return agent::Button::Y;
69 return agent::Button::L;
71 return agent::Button::R;
73 return agent::Button::SELECT;
75 return agent::Button::START;
77 return agent::Button::UP;
79 return agent::Button::DOWN;
81 return agent::Button::LEFT;
83 return agent::Button::RIGHT;
84 return absl::InvalidArgumentError(absl::StrCat(
"Unknown button: ", s));
94 EmulatorClient client;
95 agent::ControlRequest request;
96 request.set_action(
"reset");
98 client.CallRpc(&agent::EmulatorService::Stub::ControlEmulator, request);
99 if (!response_or.ok()) {
100 return response_or.status();
102 auto response = response_or.value();
105 formatter.
AddField(
"success", response.success());
106 formatter.
AddField(
"message", response.message());
108 return absl::OkStatus();
114 EmulatorClient client;
115 agent::GameStateRequest request;
116 request.set_include_screenshot(parser.
HasFlag(
"screenshot"));
119 client.CallRpc(&agent::EmulatorService::Stub::GetGameState, request);
120 if (!response_or.ok()) {
121 return response_or.status();
123 auto response = response_or.value();
126 formatter.
AddField(
"game_mode",
static_cast<uint64_t
>(response.game_mode()));
128 static_cast<uint64_t
>(response.link_state()));
130 static_cast<uint64_t
>(response.link_pos_x()));
132 static_cast<uint64_t
>(response.link_pos_y()));
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()));
140 return absl::OkStatus();
146 EmulatorClient client;
147 agent::MemoryRequest request;
150 if (!ParseHexString(parser.
GetString(
"address").value(), &address)) {
151 return absl::InvalidArgumentError(
"Invalid address format.");
153 request.set_address(address);
154 request.set_size(parser.
GetInt(
"length").value_or(16));
157 client.CallRpc(&agent::EmulatorService::Stub::ReadMemory, request);
158 if (!response_or.ok()) {
159 return response_or.status();
161 auto response = response_or.value();
164 formatter.
AddHexField(
"address", response.address());
165 formatter.
AddField(
"data_hex", absl::BytesToHexString(response.data()));
167 return absl::OkStatus();
173 EmulatorClient client;
174 agent::MemoryWriteRequest request;
177 if (!ParseHexString(parser.
GetString(
"address").value(), &address)) {
178 return absl::InvalidArgumentError(
"Invalid address format.");
180 request.set_address(address);
182 std::string data_hex = parser.
GetString(
"data").value();
183 request.set_data(absl::HexStringToBytes(data_hex));
186 client.CallRpc(&agent::EmulatorService::Stub::WriteMemory, request);
187 if (!response_or.ok()) {
188 return response_or.status();
190 auto response = response_or.value();
193 formatter.
AddField(
"success", response.success());
194 formatter.
AddField(
"message", response.message());
196 return absl::OkStatus();
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);
209 return button_or.status();
210 request.add_buttons(button_or.value());
214 client.CallRpc(&agent::EmulatorService::Stub::PressButtons, request);
215 if (!response_or.ok()) {
216 return response_or.status();
218 auto response = response_or.value();
221 formatter.
AddField(
"success", response.success());
222 formatter.
AddField(
"message", response.message());
224 return absl::OkStatus();
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);
237 return button_or.status();
238 request.add_buttons(button_or.value());
242 client.CallRpc(&agent::EmulatorService::Stub::ReleaseButtons, request);
243 if (!response_or.ok()) {
244 return response_or.status();
246 auto response = response_or.value();
249 formatter.
AddField(
"success", response.success());
250 formatter.
AddField(
"message", response.message());
252 return absl::OkStatus();
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);
265 return button_or.status();
266 request.add_buttons(button_or.value());
268 request.set_duration_ms(parser.
GetInt(
"duration").value());
271 client.CallRpc(&agent::EmulatorService::Stub::HoldButtons, request);
272 if (!response_or.ok()) {
273 return response_or.status();
275 auto response = response_or.value();
278 formatter.
AddField(
"success", response.success());
279 formatter.
AddField(
"message", response.message());
281 return absl::OkStatus();
290 formatter.
AddField(
"status",
"not_implemented");
292 return absl::OkStatus();
299 formatter.
AddField(
"status",
"not_implemented");
301 return absl::OkStatus();
308 formatter.
AddField(
"status",
"not_implemented");
310 return absl::OkStatus();
317 formatter.
AddField(
"status",
"not_implemented");
319 return absl::OkStatus();
325 formatter.
BeginObject(
"Emulator Breakpoint Cleared");
326 formatter.
AddField(
"status",
"not_implemented");
328 return absl::OkStatus();
335 formatter.
AddField(
"status",
"not_implemented");
337 return absl::OkStatus();
344 formatter.
AddField(
"status",
"not_implemented");
346 return absl::OkStatus();
353 formatter.
AddField(
"status",
"not_implemented");
355 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
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)
std::unique_ptr< agent::EmulatorService::Stub > stub_
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)
absl::StatusOr< agent::Button > StringToButton(absl::string_view s)
bool ParseHexString(absl::string_view str, int *out)