9#include "absl/strings/str_format.h"
21 return grpc::Status::OK;
23 grpc::StatusCode code = grpc::StatusCode::UNKNOWN;
24 switch (status.code()) {
25 case absl::StatusCode::kOk:
26 code = grpc::StatusCode::OK;
28 case absl::StatusCode::kCancelled:
29 code = grpc::StatusCode::CANCELLED;
31 case absl::StatusCode::kUnknown:
32 code = grpc::StatusCode::UNKNOWN;
34 case absl::StatusCode::kInvalidArgument:
35 code = grpc::StatusCode::INVALID_ARGUMENT;
37 case absl::StatusCode::kDeadlineExceeded:
38 code = grpc::StatusCode::DEADLINE_EXCEEDED;
40 case absl::StatusCode::kNotFound:
41 code = grpc::StatusCode::NOT_FOUND;
43 case absl::StatusCode::kAlreadyExists:
44 code = grpc::StatusCode::ALREADY_EXISTS;
46 case absl::StatusCode::kPermissionDenied:
47 code = grpc::StatusCode::PERMISSION_DENIED;
49 case absl::StatusCode::kUnauthenticated:
50 code = grpc::StatusCode::UNAUTHENTICATED;
52 case absl::StatusCode::kResourceExhausted:
53 code = grpc::StatusCode::RESOURCE_EXHAUSTED;
55 case absl::StatusCode::kFailedPrecondition:
56 code = grpc::StatusCode::FAILED_PRECONDITION;
58 case absl::StatusCode::kAborted:
59 code = grpc::StatusCode::ABORTED;
61 case absl::StatusCode::kOutOfRange:
62 code = grpc::StatusCode::OUT_OF_RANGE;
64 case absl::StatusCode::kUnimplemented:
65 code = grpc::StatusCode::UNIMPLEMENTED;
67 case absl::StatusCode::kInternal:
68 code = grpc::StatusCode::INTERNAL;
70 case absl::StatusCode::kUnavailable:
71 code = grpc::StatusCode::UNAVAILABLE;
73 case absl::StatusCode::kDataLoss:
74 code = grpc::StatusCode::DATA_LOSS;
77 code = grpc::StatusCode::UNKNOWN;
80 return grpc::Status(code, std::string(status.message()));
87 : emulator_(emulator), rom_getter_(rom_getter), rom_loader_(rom_loader) {}
92 const agent::LoadRomRequest* request,
93 agent::LoadRomResponse* response) {
95 response->set_success(
false);
96 response->set_message(
"Emulator middleware not initialized");
97 return grpc::Status::OK;
101 response->set_success(
true);
102 response->set_message(
"ROM loaded successfully");
106 response->set_rom_title(rom->
title());
107 response->set_rom_size(rom->
size());
111 response->set_success(
false);
112 response->set_message(
"Failed to load ROM: " +
113 std::string(status.message()));
115 return grpc::Status::OK;
119 grpc::ServerContext* context,
const agent::Empty* request,
120 agent::LoadedRomPathResponse* response) {
124 response->set_has_rom(
true);
125 response->set_filepath(path);
126 response->set_title(std::filesystem::path(path).stem().
string());
127 return grpc::Status::OK;
131 response->set_has_rom(
false);
132 return grpc::Status::OK;
138 grpc::ServerContext* context,
const agent::ControlRequest* request,
139 agent::CommandResponse* response) {
141 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
142 "Emulator not connected.");
144 std::string action = request->action();
145 if (action ==
"start" || action ==
"resume") {
147 response->set_message(
"Emulator resumed.");
148 }
else if (action ==
"stop" || action ==
"pause") {
150 response->set_message(
"Emulator paused.");
151 }
else if (action ==
"reset") {
153 response->set_message(
"Emulator reset.");
154 }
else if (action ==
"init" || action ==
"initialize") {
155 response->set_message(
"Emulator connection active.");
157 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
158 "Unknown action: " + action);
161 response->set_success(
true);
162 return grpc::Status::OK;
166 grpc::ServerContext* context,
const agent::StepControlRequest* request,
167 agent::StepResponse* response) {
169 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
170 "Emulator not connected.");
173 std::string mode = request->mode();
175 if (mode ==
"instruction") {
177 response->set_message(
"Stepped 1 instruction.");
178 }
else if (mode ==
"over") {
180 response->set_message(
"Step Over executed.");
181 }
else if (mode ==
"out") {
183 response->set_message(
"Step Out executed.");
185 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
186 "Unknown step mode: " + mode);
190 return ToGrpcStatus(status);
194 if (!cpu_status.ok())
195 return ToGrpcStatus(cpu_status);
196 emu::ToProtoCpuState(cpu_snap, response->mutable_cpu_state());
197 response->set_success(
true);
198 return grpc::Status::OK;
202 grpc::ServerContext* context,
const agent::Empty* request,
203 agent::BreakpointHitResponse* response) {
205 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
206 "Emulator not initialized.");
211 return ToGrpcStatus(status);
212 emu::ToProtoBreakpointHitResponse(result, response);
213 return grpc::Status::OK;
219 grpc::ServerContext* context,
const agent::ButtonRequest* request,
220 agent::CommandResponse* response) {
222 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
223 "Emulator not initialized.");
225 std::vector<emu::InputButton> pressed_buttons;
226 pressed_buttons.reserve(request->buttons_size());
227 for (
int i = 0; i < request->buttons_size(); i++) {
229 emu::FromProtoButton(
static_cast<agent::Button
>(request->buttons(i)));
231 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
232 absl::StrFormat(
"Invalid button at index %d: %d", i,
233 request->buttons(i)));
236 if (!press_status.ok()) {
237 for (
auto it = pressed_buttons.rbegin(); it != pressed_buttons.rend();
241 return ToGrpcStatus(press_status);
243 pressed_buttons.push_back(btn);
245 std::this_thread::sleep_for(std::chrono::milliseconds(50));
246 for (
auto it = pressed_buttons.rbegin(); it != pressed_buttons.rend(); ++it) {
248 if (!release_status.ok())
249 return ToGrpcStatus(release_status);
251 response->set_success(
true);
252 return grpc::Status::OK;
256 grpc::ServerContext* context,
const agent::ButtonRequest* request,
257 agent::CommandResponse* response) {
259 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
260 "Emulator not initialized.");
262 for (
int i = 0; i < request->buttons_size(); i++) {
264 emu::FromProtoButton(
static_cast<agent::Button
>(request->buttons(i)));
266 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
267 absl::StrFormat(
"Invalid button at index %d: %d", i,
268 request->buttons(i)));
272 return ToGrpcStatus(status);
274 response->set_success(
true);
275 return grpc::Status::OK;
279 grpc::ServerContext* context,
const agent::ButtonHoldRequest* request,
280 agent::CommandResponse* response) {
282 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
283 "Emulator not initialized.");
285 std::vector<emu::InputButton> held_buttons;
286 held_buttons.reserve(request->buttons_size());
287 for (
int i = 0; i < request->buttons_size(); i++) {
289 emu::FromProtoButton(
static_cast<agent::Button
>(request->buttons(i)));
291 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
292 absl::StrFormat(
"Invalid button at index %d: %d", i,
293 request->buttons(i)));
296 if (!press_status.ok()) {
297 for (
auto it = held_buttons.rbegin(); it != held_buttons.rend(); ++it) {
300 return ToGrpcStatus(press_status);
302 held_buttons.push_back(btn);
304 std::this_thread::sleep_for(
305 std::chrono::milliseconds(request->duration_ms()));
306 for (
auto it = held_buttons.rbegin(); it != held_buttons.rend(); ++it) {
308 if (!release_status.ok())
309 return ToGrpcStatus(release_status);
311 response->set_success(
true);
312 return grpc::Status::OK;
316 grpc::ServerContext* context,
const agent::GameStateRequest* request,
317 agent::GameStateResponse* response) {
319 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
320 "Emulator not initialized.");
325 return ToGrpcStatus(status);
326 emu::ToProtoGameState(snapshot, response);
329 for (
const auto& mem_req : request->memory_reads()) {
330 auto* mem_resp = response->add_memory_responses();
331 mem_resp->set_address(mem_req.address());
334 mem_resp->set_data(data_or->data(), data_or->size());
339 if (request->include_screenshot()) {
343 absl::StatusOr<test::ScreenshotArtifact> screenshot =
346 : absl::FailedPreconditionError(
347 "No render-thread screenshot capturer is configured");
348 if (!screenshot.ok()) {
349 LOG_WARN(
"EmulatorService",
"GetGameState screenshot skipped: %s",
350 std::string(screenshot.status().message()).c_str());
353 std::ifstream file(screenshot->file_path, std::ios::binary);
355 std::string png_data((std::istreambuf_iterator<char>(file)),
356 std::istreambuf_iterator<char>());
357 response->set_screenshot_png(png_data);
362 std::error_code remove_error;
363 std::filesystem::remove(screenshot->file_path, remove_error);
367 return grpc::Status::OK;
371 grpc::ServerContext* context,
const agent::MemoryRequest* request,
372 agent::MemoryResponse* response) {
374 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
375 "Emulator not initialized.");
377 response->set_address(request->address());
380 return ToGrpcStatus(data_or.status());
382 response->set_data(data_or->data(), data_or->size());
383 return grpc::Status::OK;
387 grpc::ServerContext* context,
const agent::MemoryWriteRequest* request,
388 agent::CommandResponse* response) {
390 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
391 "Emulator not initialized.");
393 std::vector<uint8_t> data(request->data().begin(), request->data().end());
396 return ToGrpcStatus(status);
398 response->set_success(
true);
399 return grpc::Status::OK;
405 grpc::ServerContext* context,
406 const agent::BreakpointControlRequest* request,
407 agent::BreakpointControlResponse* response) {
409 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
410 "Emulator not initialized.");
412 std::string action = request->action();
413 if (action ==
"add") {
415 request->address(), emu::FromProtoBreakpointType(request->type()),
416 emu::FromProtoCpuType(request->cpu()), request->condition(),
417 request->description());
419 return ToGrpcStatus(id_or.status());
420 response->set_breakpoint_id(*id_or);
421 response->set_message(
"Breakpoint added.");
422 }
else if (action ==
"remove") {
425 return ToGrpcStatus(status);
426 response->set_message(
"Breakpoint removed.");
427 }
else if (action ==
"toggle") {
431 return ToGrpcStatus(status);
432 response->set_message(
"Breakpoint toggled.");
433 }
else if (action ==
"list") {
435 for (
const auto& bp_snap : list) {
436 emu::ToProtoBreakpointInfo(bp_snap, response->add_breakpoints());
439 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
440 "Unknown action: " + action);
443 response->set_success(
true);
444 return grpc::Status::OK;
448 grpc::ServerContext* context,
449 const agent::WatchpointControlRequest* request,
450 agent::WatchpointControlResponse* response) {
451 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
452 "WatchpointManager integration pending.");
458 grpc::ServerContext* context,
const agent::DisassemblyRequest* request,
459 agent::DisassemblyResponse* response) {
461 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
462 "Emulator not initialized.");
466 uint32_t addr = request->start_address();
467 int fetch_size = request->count() * 4;
470 return ToGrpcStatus(data_or.status());
472 const auto& data = *data_or;
473 auto mem_reader = [&](uint32_t read_addr) -> uint8_t {
474 if (read_addr >= addr && read_addr < addr + data.size()) {
475 return data[read_addr - addr];
482 if (!cpu_status.ok())
483 return ToGrpcStatus(cpu_status);
484 bool m_flag = (cpu_snap.
status & 0x20) != 0;
485 bool x_flag = (cpu_snap.
status & 0x10) != 0;
487 for (uint32_t i = 0; i < request->count(); ++i) {
488 auto inst = dis.
Disassemble(addr, mem_reader, m_flag, x_flag);
489 auto* line = response->add_lines();
490 line->set_address(inst.address);
491 line->set_mnemonic(inst.mnemonic);
492 line->set_operand_str(inst.operand_str);
494 if (addr >= request->start_address() + fetch_size)
497 return grpc::Status::OK;
501 grpc::ServerContext* context,
const agent::TraceRequest* request,
502 agent::TraceResponse* response) {
503 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
504 "Trace not implemented.");
508 grpc::ServerContext* context,
const agent::SymbolLookupRequest* request,
509 agent::SymbolLookupResponse* response) {
512 response->set_found(
true);
513 response->set_symbol_name(sym->name);
514 response->set_address(sym->address);
516 return grpc::Status::OK;
520 grpc::ServerContext* context,
const agent::AddressRequest* request,
521 agent::SymbolLookupResponse* response) {
524 response->set_found(
true);
525 response->set_symbol_name(sym->name);
526 response->set_address(sym->address);
528 return grpc::Status::OK;
532 grpc::ServerContext* context,
const agent::SymbolFileRequest* request,
533 agent::CommandResponse* response) {
537 response->set_success(status.ok());
538 response->set_message(std::string(status.message()));
539 return grpc::Status::OK;
545 grpc::ServerContext* context,
const agent::Empty* request,
546 agent::DebugStatusResponse* response) {
548 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
549 "Emulator not initialized.");
555 if (!cpu_status.ok())
556 return ToGrpcStatus(cpu_status);
557 emu::ToProtoCpuState(cpu_snap, response->mutable_cpu_state());
560 return grpc::Status::OK;
564 const agent::TestRunRequest* request,
565 agent::TestRunResponse* response) {
566 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
567 "TestRun requires refactoring for IEmulator");
573 grpc::ServerContext* context,
const agent::SaveStateRequest* request,
574 agent::SaveStateResponse* response) {
575 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
576 "SaveState not yet ported to IEmulator");
580 grpc::ServerContext* context,
const agent::LoadStateRequest* request,
581 agent::LoadStateResponse* response) {
582 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
583 "LoadState not yet ported to IEmulator");
587 grpc::ServerContext* context,
const agent::ListStatesRequest* request,
588 agent::ListStatesResponse* response) {
589 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
590 "ListStates not yet ported to IEmulator");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Abstract interface for emulator backends (Internal vs Mesen2)
virtual bool IsRunning() const =0
virtual absl::StatusOr< std::vector< uint8_t > > ReadBlock(uint32_t addr, size_t len)=0
virtual absl::Status StepOver()=0
virtual absl::Status LoadRom(const std::string &path)=0
virtual absl::Status GetCpuState(CpuStateSnapshot *out_state)=0
virtual absl::Status RemoveBreakpoint(uint32_t breakpoint_id)=0
virtual absl::Status ToggleBreakpoint(uint32_t breakpoint_id, bool enabled)=0
virtual absl::Status Step(int count)=0
virtual absl::Status ReleaseButton(InputButton button)=0
virtual absl::Status GetGameState(GameSnapshot *out_state)=0
virtual absl::Status StepOut()=0
virtual std::string GetLoadedRomPath() const =0
virtual bool IsConnected() const =0
virtual absl::Status WriteBlock(uint32_t addr, const std::vector< uint8_t > &data)=0
virtual absl::Status PressButton(InputButton button)=0
virtual std::vector< BreakpointSnapshot > ListBreakpoints()=0
virtual absl::StatusOr< uint32_t > AddBreakpoint(uint32_t addr, BreakpointKind type, CpuKind cpu, const std::string &condition, const std::string &description)=0
virtual absl::Status RunToBreakpoint(BreakpointHitResult *response)=0
65816 CPU disassembler for debugging and ROM hacking
DisassembledInstruction Disassemble(uint32_t address, MemoryReader read_byte, bool m_flag=true, bool x_flag=true) const
Disassemble a single instruction.
absl::Status LoadSymbolFile(const std::string &path, SymbolFormat format=SymbolFormat::kAuto)
Load symbols from a .sym file (various formats)
std::optional< Symbol > GetSymbol(uint32_t address) const
Get full symbol info for an address.
std::optional< Symbol > FindSymbol(const std::string &name) const
Find symbol by name.
grpc::Status GetSymbolAt(grpc::ServerContext *context, const agent::AddressRequest *request, agent::SymbolLookupResponse *response) override
std::function< bool(const std::string &path)> RomLoader
grpc::Status ControlEmulator(grpc::ServerContext *context, const agent::ControlRequest *request, agent::CommandResponse *response) override
grpc::Status TestRun(grpc::ServerContext *context, const agent::TestRunRequest *request, agent::TestRunResponse *response) override
grpc::Status GetDebugStatus(grpc::ServerContext *context, const agent::Empty *request, agent::DebugStatusResponse *response) override
ScreenshotCapturer screenshot_capturer_
grpc::Status WriteMemory(grpc::ServerContext *context, const agent::MemoryWriteRequest *request, agent::CommandResponse *response) override
grpc::Status ReleaseButtons(grpc::ServerContext *context, const agent::ButtonRequest *request, agent::CommandResponse *response) override
grpc::Status PressButtons(grpc::ServerContext *context, const agent::ButtonRequest *request, agent::CommandResponse *response) override
grpc::Status GetGameState(grpc::ServerContext *context, const agent::GameStateRequest *request, agent::GameStateResponse *response) override
grpc::Status BreakpointControl(grpc::ServerContext *context, const agent::BreakpointControlRequest *request, agent::BreakpointControlResponse *response) override
grpc::Status GetDisassembly(grpc::ServerContext *context, const agent::DisassemblyRequest *request, agent::DisassemblyResponse *response) override
grpc::Status LoadSymbols(grpc::ServerContext *context, const agent::SymbolFileRequest *request, agent::CommandResponse *response) override
grpc::Status ReadMemory(grpc::ServerContext *context, const agent::MemoryRequest *request, agent::MemoryResponse *response) override
grpc::Status StepEmulator(grpc::ServerContext *context, const agent::StepControlRequest *request, agent::StepResponse *response) override
grpc::Status HoldButtons(grpc::ServerContext *context, const agent::ButtonHoldRequest *request, agent::CommandResponse *response) override
grpc::Status LoadRom(grpc::ServerContext *context, const agent::LoadRomRequest *request, agent::LoadRomResponse *response) override
grpc::Status GetLoadedRomPath(grpc::ServerContext *context, const agent::Empty *request, agent::LoadedRomPathResponse *response) override
grpc::Status LoadState(grpc::ServerContext *context, const agent::LoadStateRequest *request, agent::LoadStateResponse *response) override
grpc::Status SaveState(grpc::ServerContext *context, const agent::SaveStateRequest *request, agent::SaveStateResponse *response) override
std::function< Rom *()> RomGetter
grpc::Status ResolveSymbol(grpc::ServerContext *context, const agent::SymbolLookupRequest *request, agent::SymbolLookupResponse *response) override
emu::IEmulator * emulator_
emu::debug::SymbolProvider symbol_provider_
grpc::Status ListStates(grpc::ServerContext *context, const agent::ListStatesRequest *request, agent::ListStatesResponse *response) override
grpc::Status GetExecutionTrace(grpc::ServerContext *context, const agent::TraceRequest *request, agent::TraceResponse *response) override
grpc::Status RunToBreakpoint(grpc::ServerContext *context, const agent::Empty *request, agent::BreakpointHitResponse *response) override
EmulatorServiceImpl(emu::IEmulator *emulator, RomGetter rom_getter=nullptr, RomLoader rom_loader=nullptr)
grpc::Status WatchpointControl(grpc::ServerContext *context, const agent::WatchpointControlRequest *request, agent::WatchpointControlResponse *response) override
#define LOG_WARN(category, format,...)
SymbolFormat
Supported symbol file formats.
grpc::Status ToGrpcStatus(const absl::Status &status)