3#include <grpcpp/grpcpp.h>
4#include "protos/emulator_service.grpc.pb.h"
5#include "absl/strings/str_split.h"
6#include "absl/strings/string_view.h"
7#include "absl/time/time.h"
8#include "absl/status/statusor.h"
9#include "absl/strings/escaping.h"
21 auto channel = grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials());
22 stub_ = agent::EmulatorService::NewStub(channel);
25 template <
typename TRequest,
typename TResponse>
27 grpc::Status (agent::EmulatorService::Stub::*rpc_method)(
grpc::ClientContext*, const TRequest&, TResponse*),
28 const TRequest& request) {
31 grpc::ClientContext context;
33 auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(5);
34 context.set_deadline(deadline);
36 grpc::Status status = (stub_.get()->*rpc_method)(&context, request, &response);
39 return absl::UnavailableError(absl::StrFormat(
40 "RPC failed: (%d) %s", status.error_code(), status.error_message()));
46 std::unique_ptr<agent::EmulatorService::Stub>
stub_;
51 if (s ==
"A")
return agent::Button::A;
52 if (s ==
"B")
return agent::Button::B;
53 if (s ==
"X")
return agent::Button::X;
54 if (s ==
"Y")
return agent::Button::Y;
55 if (s ==
"L")
return agent::Button::L;
56 if (s ==
"R")
return agent::Button::R;
57 if (s ==
"SELECT")
return agent::Button::SELECT;
58 if (s ==
"START")
return agent::Button::START;
59 if (s ==
"UP")
return agent::Button::UP;
60 if (s ==
"DOWN")
return agent::Button::DOWN;
61 if (s ==
"LEFT")
return agent::Button::LEFT;
62 if (s ==
"RIGHT")
return agent::Button::RIGHT;
63 return absl::InvalidArgumentError(absl::StrCat(
"Unknown button: ", s));
72 EmulatorClient client;
74 auto response_or = client.CallRpc(&agent::EmulatorService::Stub::Reset, request);
75 if (!response_or.ok()) {
76 return response_or.status();
78 auto response = response_or.value();
81 formatter.
AddField(
"success", response.success());
82 formatter.
AddField(
"message", response.message());
84 return absl::OkStatus();
89 EmulatorClient client;
90 agent::GameStateRequest request;
91 request.set_include_screenshot(parser.
HasFlag(
"screenshot"));
93 auto response_or = client.CallRpc(&agent::EmulatorService::Stub::GetGameState, request);
94 if (!response_or.ok()) {
95 return response_or.status();
97 auto response = response_or.value();
100 formatter.
AddField(
"game_mode",
static_cast<uint64_t
>(response.game_mode()));
101 formatter.
AddField(
"link_state",
static_cast<uint64_t
>(response.link_state()));
102 formatter.
AddField(
"link_pos_x",
static_cast<uint64_t
>(response.link_pos_x()));
103 formatter.
AddField(
"link_pos_y",
static_cast<uint64_t
>(response.link_pos_y()));
104 formatter.
AddField(
"link_health",
static_cast<uint64_t
>(response.link_health()));
105 if (!response.screenshot_png().empty()) {
106 formatter.
AddField(
"screenshot_size",
static_cast<uint64_t
>(response.screenshot_png().size()));
109 return absl::OkStatus();
114 EmulatorClient client;
115 agent::MemoryRequest request;
118 if (!absl::SimpleHexAtoi(parser.
GetString(
"address").value(), &address)) {
119 return absl::InvalidArgumentError(
"Invalid address format.");
121 request.set_address(address);
122 request.set_size(parser.
GetInt(
"length").value_or(16));
124 auto response_or = client.CallRpc(&agent::EmulatorService::Stub::ReadMemory, request);
125 if (!response_or.ok()) {
126 return response_or.status();
128 auto response = response_or.value();
131 formatter.
AddHexField(
"address", response.address());
132 formatter.
AddField(
"data_hex", absl::BytesToHexString(response.data()));
134 return absl::OkStatus();
139 EmulatorClient client;
140 agent::MemoryWriteRequest request;
143 if (!absl::SimpleHexAtoi(parser.
GetString(
"address").value(), &address)) {
144 return absl::InvalidArgumentError(
"Invalid address format.");
146 request.set_address(address);
148 std::string data_hex = parser.
GetString(
"data").value();
149 request.set_data(absl::HexStringToBytes(data_hex));
151 auto response_or = client.CallRpc(&agent::EmulatorService::Stub::WriteMemory, request);
152 if (!response_or.ok()) {
153 return response_or.status();
155 auto response = response_or.value();
158 formatter.
AddField(
"success", response.success());
159 formatter.
AddField(
"message", response.message());
161 return absl::OkStatus();
167 EmulatorClient client;
168 agent::ButtonRequest request;
169 std::vector<std::string> buttons = absl::StrSplit(parser.
GetString(
"buttons").value(),
',');
170 for (
const auto& btn_str : buttons) {
171 auto button_or = StringToButton(btn_str);
172 if (!button_or.ok())
return button_or.status();
173 request.add_buttons(button_or.value());
176 auto response_or = client.CallRpc(&agent::EmulatorService::Stub::PressButtons, request);
177 if (!response_or.ok()) {
178 return response_or.status();
180 auto response = response_or.value();
183 formatter.
AddField(
"success", response.success());
184 formatter.
AddField(
"message", response.message());
186 return absl::OkStatus();
191 EmulatorClient client;
192 agent::ButtonRequest request;
193 std::vector<std::string> buttons = absl::StrSplit(parser.
GetString(
"buttons").value(),
',');
194 for (
const auto& btn_str : buttons) {
195 auto button_or = StringToButton(btn_str);
196 if (!button_or.ok())
return button_or.status();
197 request.add_buttons(button_or.value());
200 auto response_or = client.CallRpc(&agent::EmulatorService::Stub::ReleaseButtons, request);
201 if (!response_or.ok()) {
202 return response_or.status();
204 auto response = response_or.value();
207 formatter.
AddField(
"success", response.success());
208 formatter.
AddField(
"message", response.message());
210 return absl::OkStatus();
215 EmulatorClient client;
216 agent::ButtonHoldRequest request;
217 std::vector<std::string> buttons = absl::StrSplit(parser.
GetString(
"buttons").value(),
',');
218 for (
const auto& btn_str : buttons) {
219 auto button_or = StringToButton(btn_str);
220 if (!button_or.ok())
return button_or.status();
221 request.add_buttons(button_or.value());
223 request.set_duration_ms(parser.
GetInt(
"duration").value());
225 auto response_or = client.CallRpc(&agent::EmulatorService::Stub::HoldButtons, request);
226 if (!response_or.ok()) {
227 return response_or.status();
229 auto response = response_or.value();
232 formatter.
AddField(
"success", response.success());
233 formatter.
AddField(
"message", response.message());
235 return absl::OkStatus();
244 formatter.
AddField(
"status",
"not_implemented");
246 return absl::OkStatus();
252 formatter.
AddField(
"status",
"not_implemented");
254 return absl::OkStatus();
260 formatter.
AddField(
"status",
"not_implemented");
262 return absl::OkStatus();
268 formatter.
AddField(
"status",
"not_implemented");
270 return absl::OkStatus();
275 formatter.
BeginObject(
"Emulator Breakpoint Cleared");
276 formatter.
AddField(
"status",
"not_implemented");
278 return absl::OkStatus();
284 formatter.
AddField(
"status",
"not_implemented");
286 return absl::OkStatus();
292 formatter.
AddField(
"status",
"not_implemented");
294 return absl::OkStatus();
300 formatter.
AddField(
"status",
"not_implemented");
302 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data.
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)
Main namespace for the application.