yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
emulator_service_impl.cc
Go to the documentation of this file.
2#include "app/emu/emulator.h"
4#include "app/emu/input/input_backend.h" // Required for SnesButton enum
5#include "absl/strings/escaping.h"
6#include "absl/strings/str_format.h"
7#include <fstream>
8#include <thread>
9
10namespace yaze::agent {
11
12namespace {
13// Helper to convert our gRPC Button enum to the emulator's SnesButton enum
16 switch (button) {
17 case A: return SnesButton::A;
18 case B: return SnesButton::B;
19 case X: return SnesButton::X;
20 case Y: return SnesButton::Y;
21 case L: return SnesButton::L;
22 case R: return SnesButton::R;
23 case SELECT: return SnesButton::SELECT;
24 case START: return SnesButton::START;
25 case UP: return SnesButton::UP;
26 case DOWN: return SnesButton::DOWN;
27 case LEFT: return SnesButton::LEFT;
28 case RIGHT: return SnesButton::RIGHT;
29 default:
30 return SnesButton::B; // Default fallback
31 }
32}
33} // namespace
34
37
38// --- Lifecycle ---
39
40grpc::Status EmulatorServiceImpl::Start(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
41 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
43 response->set_success(true);
44 response->set_message("Emulator started.");
45 return grpc::Status::OK;
46}
47
48grpc::Status EmulatorServiceImpl::Stop(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
49 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
50 emulator_->set_running(false);
51 response->set_success(true);
52 response->set_message("Emulator stopped.");
53 return grpc::Status::OK;
54}
55
56grpc::Status EmulatorServiceImpl::Pause(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
57 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
58 emulator_->set_running(false);
59 response->set_success(true);
60 response->set_message("Emulator paused.");
61 return grpc::Status::OK;
62}
63
64grpc::Status EmulatorServiceImpl::Resume(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
65 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
67 response->set_success(true);
68 response->set_message("Emulator resumed.");
69 return grpc::Status::OK;
70}
71
72grpc::Status EmulatorServiceImpl::Reset(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
73 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
74 emulator_->snes().Reset(true); // Hard reset
75 response->set_success(true);
76 response->set_message("Emulator reset.");
77 return grpc::Status::OK;
78}
79
80// --- Input Control ---
81
82grpc::Status EmulatorServiceImpl::PressButtons(grpc::ServerContext* context, const ButtonRequest* request, CommandResponse* response) {
83 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
84 auto& input_manager = emulator_->input_manager();
85 for (const auto& button : request->buttons()) {
86 input_manager.PressButton(ToSnesButton(static_cast<Button>(button)));
87 }
88 std::this_thread::sleep_for(std::chrono::milliseconds(50));
89 for (const auto& button : request->buttons()) {
90 input_manager.ReleaseButton(ToSnesButton(static_cast<Button>(button)));
91 }
92 response->set_success(true);
93 return grpc::Status::OK;
94}
95
96grpc::Status EmulatorServiceImpl::ReleaseButtons(grpc::ServerContext* context, const ButtonRequest* request, CommandResponse* response) {
97 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
98 auto& input_manager = emulator_->input_manager();
99 for (const auto& button : request->buttons()) {
100 input_manager.ReleaseButton(ToSnesButton(static_cast<Button>(button)));
101 }
102 response->set_success(true);
103 return grpc::Status::OK;
104}
105
106grpc::Status EmulatorServiceImpl::HoldButtons(grpc::ServerContext* context, const ButtonHoldRequest* request, CommandResponse* response) {
107 if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
108 auto& input_manager = emulator_->input_manager();
109 for (const auto& button : request->buttons()) {
110 input_manager.PressButton(ToSnesButton(static_cast<Button>(button)));
111 }
112 std::this_thread::sleep_for(std::chrono::milliseconds(request->duration_ms()));
113 for (const auto& button : request->buttons()) {
114 input_manager.ReleaseButton(ToSnesButton(static_cast<Button>(button)));
115 }
116 response->set_success(true);
117 return grpc::Status::OK;
118}
119
120// --- State Inspection ---
121
122grpc::Status EmulatorServiceImpl::GetGameState(grpc::ServerContext* context, const GameStateRequest* request, GameStateResponse* response) {
124 return grpc::Status(grpc::StatusCode::UNAVAILABLE, "SNES is not initialized.");
125 }
126 auto& memory = emulator_->snes().memory();
127
128 response->set_game_mode(memory.ReadByte(0x7E0010));
129 response->set_link_state(memory.ReadByte(0x7E005D));
130 response->set_link_pos_x(memory.ReadWord(0x7E0020));
131 response->set_link_pos_y(memory.ReadWord(0x7E0022));
132 response->set_link_health(memory.ReadByte(0x7EF36D));
133
134 for (const auto& mem_req : request->memory_reads()) {
135 auto* mem_resp = response->add_memory_responses();
136 mem_resp->set_address(mem_req.address());
137 std::vector<uint8_t> data(mem_req.size());
138 for (uint32_t i = 0; i < mem_req.size(); ++i) {
139 data[i] = memory.ReadByte(mem_req.address() + i);
140 }
141 mem_resp->set_data(data.data(), data.size());
142 }
143
144#ifdef YAZE_WITH_GRPC
145 if (request->include_screenshot()) {
146 auto screenshot = yaze::test::CaptureHarnessScreenshot();
147 if (screenshot.ok()) {
148 // Read the screenshot file and convert to PNG data
149 std::ifstream file(screenshot->file_path, std::ios::binary);
150 if (file.good()) {
151 std::string png_data((std::istreambuf_iterator<char>(file)),
152 std::istreambuf_iterator<char>());
153 response->set_screenshot_png(png_data);
154 }
155 }
156 }
157#endif
158
159 return grpc::Status::OK;
160}
161
162grpc::Status EmulatorServiceImpl::ReadMemory(grpc::ServerContext* context, const MemoryRequest* request, MemoryResponse* response) {
164 return grpc::Status(grpc::StatusCode::UNAVAILABLE, "SNES is not initialized.");
165 }
166 auto& memory = emulator_->snes().memory();
167 response->set_address(request->address());
168 std::vector<uint8_t> data(request->size());
169 for (uint32_t i = 0; i < request->size(); ++i) {
170 data[i] = memory.ReadByte(request->address() + i);
171 }
172 response->set_data(data.data(), data.size());
173 return grpc::Status::OK;
174}
175
176grpc::Status EmulatorServiceImpl::WriteMemory(grpc::ServerContext* context, const MemoryWriteRequest* request, CommandResponse* response) {
178 return grpc::Status(grpc::StatusCode::UNAVAILABLE, "SNES is not initialized.");
179 }
180 auto& memory = emulator_->snes().memory();
181 const std::string& data = request->data();
182 for (uint32_t i = 0; i < data.size(); ++i) {
183 memory.WriteByte(request->address() + i, static_cast<uint8_t>(data[i]));
184 }
185 response->set_success(true);
186 response->set_message(absl::StrFormat("Wrote %d bytes to 0x%X.", data.size(), request->address()));
187 return grpc::Status::OK;
188}
189
190} // namespace yaze::agent
grpc::Status Stop(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
EmulatorServiceImpl(yaze::emu::Emulator *emulator)
grpc::Status PressButtons(grpc::ServerContext *context, const ButtonRequest *request, CommandResponse *response) override
grpc::Status Pause(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
grpc::Status ReleaseButtons(grpc::ServerContext *context, const ButtonRequest *request, CommandResponse *response) override
grpc::Status Reset(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
grpc::Status WriteMemory(grpc::ServerContext *context, const MemoryWriteRequest *request, CommandResponse *response) override
grpc::Status HoldButtons(grpc::ServerContext *context, const ButtonHoldRequest *request, CommandResponse *response) override
grpc::Status ReadMemory(grpc::ServerContext *context, const MemoryRequest *request, MemoryResponse *response) override
grpc::Status Start(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
grpc::Status GetGameState(grpc::ServerContext *context, const GameStateRequest *request, GameStateResponse *response) override
grpc::Status Resume(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
A class for emulating and debugging SNES games.
Definition emulator.h:33
input::InputManager & input_manager()
Definition emulator.h:71
void set_running(bool running)
Definition emulator.h:49
bool is_snes_initialized() const
Definition emulator.h:75
auto snes() -> Snes &
Definition emulator.h:47
void PressButton(SnesButton button)
void ReleaseButton(SnesButton button)
SnesButton
SNES controller button mapping (platform-agnostic)