6#include "absl/strings/str_format.h"
7#include "absl/strings/str_split.h"
20 auto address_str = parser.
GetString(
"address").value();
21 auto length = parser.
GetInt(
"length").value_or(16);
22 std::string data_format =
"both";
23 if (
auto fmt = parser.
GetString(
"data-format"); fmt.has_value()) {
25 }
else if (
auto fmt = parser.
GetString(
"format"); fmt.has_value()) {
26 if (*fmt ==
"hex" || *fmt ==
"ascii" || *fmt ==
"both" ||
33 if (!ParseHexString(address_str, &address)) {
34 return absl::InvalidArgumentError(
"Invalid hex address format");
37 return absl::InvalidArgumentError(
"Length must be greater than 0");
39 if (address +
static_cast<uint32_t
>(length) > rom->
size()) {
40 return absl::OutOfRangeError(absl::StrFormat(
41 "Read beyond ROM: 0x%X+%d > %zu", address, length, rom->
size()));
44 const uint8_t* data = rom->
data() + address;
47 formatter.
AddField(
"length", length);
49 bool include_hex = (data_format ==
"hex" || data_format ==
"both");
50 bool include_ascii = (data_format ==
"ascii" || data_format ==
"both");
54 for (
int i = 0; i < length; ++i) {
55 absl::StrAppendFormat(&hex_data,
"%02X", data[i]);
59 formatter.
AddField(
"data", hex_data);
63 std::string ascii_data;
64 ascii_data.reserve(length);
65 for (
int i = 0; i < length; ++i) {
66 char c =
static_cast<char>(data[i]);
67 ascii_data += (std::isprint(
static_cast<unsigned char>(c)) ? c :
'.');
69 formatter.
AddField(
"ascii", ascii_data);
72 formatter.
AddField(
"format", data_format);
73 return absl::OkStatus();
79 auto address_str = parser.
GetString(
"address").value();
80 auto data_str = parser.
GetString(
"data").value();
83 if (!ParseHexString(address_str, &address)) {
84 return absl::InvalidArgumentError(
"Invalid hex address format");
87 std::vector<std::string> byte_strs = absl::StrSplit(data_str,
' ');
88 std::vector<uint8_t> bytes;
89 bytes.reserve(byte_strs.size());
91 for (
const auto& byte_str : byte_strs) {
95 if (!ParseHexString(byte_str, &value)) {
96 return absl::InvalidArgumentError(
97 absl::StrFormat(
"Invalid byte '%s'", byte_str));
99 if (value < 0 || value > 0xFF) {
100 return absl::InvalidArgumentError(
101 absl::StrFormat(
"Byte out of range: %s", byte_str));
103 bytes.push_back(
static_cast<uint8_t
>(value));
107 return absl::InvalidArgumentError(
"No data bytes provided");
110 if (address + bytes.size() > rom->
size()) {
111 return absl::OutOfRangeError(
112 absl::StrFormat(
"Write beyond ROM: 0x%X+%zu > %zu", address,
113 bytes.size(), rom->
size()));
116 for (
size_t i = 0; i < bytes.size(); ++i) {
120 std::string hex_data;
121 for (
size_t i = 0; i < bytes.size(); ++i) {
122 absl::StrAppendFormat(&hex_data,
"%02X", bytes[i]);
123 if (i < bytes.size() - 1)
128 formatter.
AddField(
"bytes_written",
static_cast<int>(bytes.size()));
129 formatter.
AddField(
"data", hex_data);
130 return absl::OkStatus();
137 return absl::FailedPreconditionError(
"ROM must be loaded");
141 formatter.
AddField(
"size", absl::StrFormat(
"0x%X", rom->
size()));
142 formatter.
AddField(
"size_bytes",
static_cast<int>(rom->
size()));
144 return absl::OkStatus();
151 return absl::FailedPreconditionError(
"ROM must be loaded");
155 std::vector<std::string> validation_results;
159 validation_results.push_back(
"checksum: PASSED");
161 validation_results.push_back(
"checksum: FAILED");
166 if (rom->
title() ==
"THE LEGEND OF ZELDA") {
167 validation_results.push_back(
"header: PASSED");
169 validation_results.push_back(
170 "header: FAILED (Invalid title: " + rom->
title() +
")");
174 formatter.
AddField(
"validation_passed", all_ok);
175 std::string results_str;
176 for (
const auto& result : validation_results) {
177 if (!results_str.empty())
179 results_str += result;
181 formatter.
AddField(
"results", results_str);
183 return absl::OkStatus();
189 auto rom_a_opt = parser.
GetString(
"rom_a");
190 auto rom_b_opt = parser.
GetString(
"rom_b");
192 if (!rom_a_opt.has_value()) {
193 return absl::InvalidArgumentError(
"Missing required argument: rom_a");
195 if (!rom_b_opt.has_value()) {
196 return absl::InvalidArgumentError(
"Missing required argument: rom_b");
199 std::string rom_a_path = rom_a_opt.value();
200 std::string rom_b_path = rom_b_opt.value();
204 if (!status_a.ok()) {
210 if (!status_b.ok()) {
215 formatter.
AddField(
"size_match",
false);
216 formatter.
AddField(
"size_a",
static_cast<int>(rom_a.
size()));
217 formatter.
AddField(
"size_b",
static_cast<int>(rom_b.
size()));
218 return absl::OkStatus();
222 std::vector<std::string> diff_details;
224 for (
size_t i = 0; i < rom_a.
size(); ++i) {
227 if (differences <= 10) {
228 diff_details.push_back(absl::StrFormat(
"0x%08X: 0x%02X vs 0x%02X", i,
235 formatter.
AddField(
"identical", differences == 0);
236 formatter.
AddField(
"differences_count", differences);
237 if (!diff_details.empty()) {
238 std::string diff_str;
239 for (
const auto& diff : diff_details) {
240 if (!diff_str.empty())
244 formatter.
AddField(
"differences", diff_str);
247 return absl::OkStatus();
253 auto rom_opt = parser.
GetString(
"rom_file");
254 auto golden_opt = parser.
GetString(
"golden_file");
256 if (!rom_opt.has_value()) {
257 return absl::InvalidArgumentError(
"Missing required argument: rom_file");
259 if (!golden_opt.has_value()) {
260 return absl::InvalidArgumentError(
"Missing required argument: golden_file");
263 std::string rom_path = rom_opt.value();
264 std::string golden_path = golden_opt.value();
272 std::ofstream file(golden_path, std::ios::binary);
273 if (!file.is_open()) {
274 return absl::NotFoundError(
"Could not open file for writing: " +
278 file.write(
reinterpret_cast<const char*
>(source_rom.
vector().data()),
281 formatter.
AddField(
"status",
"success");
282 formatter.
AddField(
"golden_file", golden_path);
283 formatter.
AddField(
"source_file", rom_path);
284 formatter.
AddField(
"size",
static_cast<int>(source_rom.
size()));
286 return absl::OkStatus();
292 auto address_str = parser.
GetString(
"address").value();
293 auto max_offset = parser.
GetInt(
"max-offset").value_or(0x100);
295 uint32_t address = 0;
296 if (!ParseHexString(address_str, &address)) {
297 return absl::InvalidArgumentError(
"Invalid hex address format");
301 formatter.
AddField(
"status",
"no_symbols_loaded");
303 return absl::OkStatus();
307 if (!symbols.empty()) {
308 formatter.
AddField(
"status",
"success");
309 formatter.
AddField(
"match_type",
"exact");
310 if (symbols.size() == 1) {
312 if (!symbols[0].file.empty()) {
313 formatter.
AddField(
"file", symbols[0].file);
314 formatter.
AddField(
"line", symbols[0].line);
318 for (
const auto& sym : symbols) {
327 uint32_t offset = address - nearest->address;
328 if (offset <=
static_cast<uint32_t
>(max_offset)) {
329 formatter.
AddField(
"status",
"success");
330 formatter.
AddField(
"match_type",
"nearest");
331 formatter.
AddField(
"name", nearest->name);
332 formatter.
AddHexField(
"symbol_address", nearest->address, 6);
334 formatter.
AddField(
"formatted", absl::StrFormat(
"%s+$%X", nearest->name, offset));
336 formatter.
AddField(
"status",
"not_found");
337 formatter.
AddField(
"message",
"Nearest symbol too far away");
340 formatter.
AddField(
"status",
"not_found");
344 formatter.
AddHexField(
"requested_address", address, 6);
345 return absl::OkStatus();
354 return absl::FailedPreconditionError(
"No symbols loaded. Provide a ROM with .mlb/.sym file.");
359 formatter.
AddField(
"status",
"success");
360 formatter.
AddField(
"name", symbol->name);
361 formatter.
AddHexField(
"address", symbol->address, 6);
362 if (!symbol->file.empty()) {
363 formatter.
AddField(
"file", symbol->file);
364 formatter.
AddField(
"line", symbol->line);
369 if (!matches.empty()) {
370 formatter.
AddField(
"status",
"success");
371 formatter.
AddField(
"match_type",
"partial");
373 for (
const auto& m : matches) {
381 formatter.
AddField(
"status",
"not_found");
385 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 LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
absl::Status WriteByte(int addr, uint8_t value)
const auto & vector() const
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.
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)
absl::StatusOr< int > GetInt(const std::string &name) const
Parse an integer argument (supports hex with 0x prefix)
emu::debug::SymbolProvider * symbol_provider_
bool HasSymbols() const
Check if any symbols are loaded.
std::vector< Symbol > GetSymbolsAtAddress(uint32_t address) const
Get all symbols at an address (there may be multiple)
std::optional< Symbol > GetNearestSymbol(uint32_t address) const
Get nearest symbol at or before an address.
std::optional< Symbol > FindSymbol(const std::string &name) const
Find symbol by name.
std::vector< Symbol > FindSymbolsMatching(const std::string &pattern) const
Find symbols matching a pattern (supports wildcards)
bool ParseHexString(absl::string_view str, int *out)