7#include "absl/strings/str_cat.h"
8#include "absl/strings/str_format.h"
9#include "absl/strings/str_join.h"
19namespace fs = std::filesystem;
22 std::string token(value);
24 return absl::InvalidArgumentError(
"Address is empty");
26 if (token[0] ==
'$') {
27 token = token.substr(1);
28 }
else if (token.size() > 2 && token[0] ==
'0' &&
29 (token[1] ==
'x' || token[1] ==
'X')) {
30 token = token.substr(2);
33 return static_cast<uint32_t
>(std::stoul(token,
nullptr, 16));
35 return absl::InvalidArgumentError(absl::StrCat(
"Invalid address: ", value));
43 std::ifstream file(path);
44 if (!file.is_open()) {
56 if ((address & 0xFF0000u) >= 0x800000u) {
68 return absl::InvalidArgumentError(
"Bank is empty");
71 return std::stoi(std::string(value),
nullptr, 16);
73 return absl::InvalidArgumentError(absl::StrCat(
"Invalid bank: ", value));
91 const std::string& path) {
92 std::map<std::string, core::AsarSymbol> symbols;
93 std::ifstream file(path);
94 if (!file.is_open()) {
99 while (std::getline(file, line)) {
100 const size_t first_colon = line.find(
':');
101 const size_t second_colon = line.find(
':', first_colon + 1);
102 if (first_colon == std::string::npos || second_colon == std::string::npos) {
107 line.substr(first_colon + 1, second_colon - first_colon - 1));
113 symbol.
name = line.substr(second_colon + 1);
115 symbols[symbol.
name] = std::move(symbol);
125 const Json* best =
nullptr;
126 uint32_t best_address = 0;
127 for (
const auto& entry : sourcemap[
"entries"]) {
128 auto addr_or =
ParseAddress(entry.value(
"address",
"0x0"));
129 if (!addr_or.ok() || *addr_or > address) {
132 if (!best || *addr_or >= best_address) {
134 best_address = *addr_or;
144 for (
const auto& file : sourcemap[
"files"]) {
145 if (file.value(
"id", -1) == file_id) {
146 return file.
value(
"path",
"");
170 return (dir / absl::StrFormat(
"bank_%02X.asm", bank)).string();
183 return absl::FailedPreconditionError(
"Project context not available.");
186 std::string query_type = parser.
GetString(
"query").value();
188 if (query_type ==
"info") {
190 }
else if (query_type ==
"files") {
193 }
else if (query_type ==
"symbols") {
195 }
else if (query_type ==
"lookup") {
197 }
else if (query_type ==
"writes") {
199 }
else if (query_type ==
"bank") {
202 return absl::InvalidArgumentError(
203 absl::StrCat(
"Unknown query type: ", query_type));
218 return absl::OkStatus();
224 if (!fs::exists(abs_path)) {
225 return absl::NotFoundError(absl::StrCat(
"Path not found: ", path));
229 for (
const auto& entry : fs::directory_iterator(abs_path)) {
231 formatter.
AddField(
"name", entry.path().filename().string());
232 formatter.
AddField(
"type", entry.is_directory() ?
"directory" :
"file");
238 return absl::OkStatus();
248 std::map<std::string, core::AsarSymbol> fallback;
249 const bool had_live_source =
251 bool loaded_from_artifact =
false;
252 const std::map<std::string, core::AsarSymbol>* symbols_ptr =
256 symbols_ptr = &fallback;
257 }
else if (!symbols_ptr &&
project_) {
259 loaded_from_artifact = !fallback.empty();
260 symbols_ptr = &fallback;
263 static const std::map<std::string, core::AsarSymbol> kEmpty;
264 symbols_ptr = &kEmpty;
266 const auto& symbols = *symbols_ptr;
267 if (symbols.empty()) {
268 if (!had_live_source && !loaded_from_artifact) {
269 return absl::FailedPreconditionError(
270 "No assembler backend or emitted symbol artifact is available.");
272 return absl::NotFoundError(
273 "No symbols loaded. Load symbols via the Assemble menu or ensure the "
274 "build script generates them.");
278 const bool has_sourcemap =
282 for (
const auto& [
name, symbol] : symbols) {
284 formatter.
AddField(
"name", symbol.name);
285 formatter.
AddField(
"address", absl::StrFormat(
"$%06X", symbol.address));
286 formatter.
AddField(
"bank", absl::StrFormat(
"$%02X", symbol.address >> 16));
289 const int file_id = entry->value(
"file_id", -1);
290 const int line = entry->value(
"line", 0);
303 return absl::OkStatus();
310 return absl::FailedPreconditionError(
"Project context not available.");
313 std::map<std::string, core::AsarSymbol> symbols;
322 uint32_t address = 0;
323 if (
auto symbol = parser.
GetString(
"symbol"); symbol.has_value()) {
324 auto it = symbols.find(*symbol);
325 if (it == symbols.end()) {
326 return absl::NotFoundError(absl::StrCat(
"Unknown symbol: ", *symbol));
328 address = it->second.address;
329 formatter.
AddField(
"symbol", it->second.name);
330 }
else if (
auto address_arg = parser.
GetString(
"address");
331 address_arg.has_value()) {
333 if (!address_or.ok()) {
334 return address_or.status();
336 address = *address_or;
338 return absl::InvalidArgumentError(
339 "lookup query requires --symbol or --address");
342 formatter.
AddField(
"address", absl::StrFormat(
"$%06X", address));
343 formatter.
AddField(
"bank", absl::StrFormat(
"$%02X", address >> 16));
346 for (
const auto& [
name, symbol] : symbols) {
347 if (symbol.address != address) {
362 formatter.
AddField(
"line", entry->value(
"line", 0));
363 formatter.
AddField(
"entry_address", entry->value(
"address",
"0x0"));
368 return absl::OkStatus();
374 return absl::FailedPreconditionError(
"Project context not available.");
380 return absl::NotFoundError(
381 "No z3dk hook coverage artifact found. Assemble with z3dk first.");
384 std::map<int, int> bytes_by_bank;
386 for (
const auto& hook : hooks[
"hooks"]) {
387 const std::string address_str = hook.value(
"address",
"0x0");
389 if (!address_or.ok()) {
392 const uint32_t address = *address_or;
394 const int size = hook.value(
"size", 0);
395 bytes_by_bank[bank] += size;
398 formatter.
AddField(
"address", absl::StrFormat(
"$%06X", address));
399 formatter.
AddField(
"bank", absl::StrFormat(
"$%02X", bank));
401 formatter.
AddField(
"kind", hook.value(
"kind",
"patch"));
402 formatter.
AddField(
"name", hook.value(
"name",
""));
403 if (hook.contains(
"source")) {
404 formatter.
AddField(
"source", hook.value(
"source",
""));
411 for (
const auto& [bank, bytes] : bytes_by_bank) {
413 formatter.
AddField(
"bank", absl::StrFormat(
"$%02X", bank));
414 formatter.
AddField(
"bytes_written", bytes);
418 return absl::OkStatus();
425 return absl::FailedPreconditionError(
"Project context not available.");
428 auto bank_arg = parser.
GetString(
"bank");
429 if (!bank_arg.has_value()) {
430 return absl::InvalidArgumentError(
"bank query requires --bank=<hex>");
434 return bank_or.status();
436 const int bank = *bank_or;
438 formatter.
AddField(
"bank", absl::StrFormat(
"$%02X", bank));
440 formatter.
AddField(
"disasm_file", disasm_file);
441 formatter.
AddField(
"disasm_file_exists",
442 static_cast<bool>(std::filesystem::exists(disasm_file)));
448 for (
const auto& entry : sourcemap[
"entries"]) {
449 auto address_or =
ParseAddress(entry.value(
"address",
"0x0"));
454 formatter.
AddField(
"address", absl::StrFormat(
"$%06X", *address_or));
457 formatter.
AddField(
"line", entry.value(
"line", 0));
467 for (
const auto& hook : hooks[
"hooks"]) {
468 auto address_or =
ParseAddress(hook.value(
"address",
"0x0"));
473 formatter.
AddField(
"address", absl::StrFormat(
"$%06X", *address_or));
474 formatter.
AddField(
"kind", hook.value(
"kind",
"patch"));
475 formatter.
AddField(
"name", hook.value(
"name",
""));
476 formatter.
AddField(
"size", hook.value(
"size", 0));
477 formatter.
AddField(
"source", hook.value(
"source",
""));
482 return absl::OkStatus();
bool contains(const std::string &) const
T value(const std::string &, const T &def) const
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
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::Status RequireArgs(const std::vector< std::string > &required) const
Validate that required arguments are present.
const std::map< std::string, core::AsarSymbol > * assembly_symbol_table_
core::AsarWrapper * asar_wrapper_
project::YazeProject * project_
std::map< std::string, AsarSymbol > GetSymbolTable() const
Symbol information extracted from Asar assembly.
Modern project structure with comprehensive settings consolidation.
std::string git_repository
std::string GetZ3dkArtifactPath(absl::string_view artifact_name) const
std::string GetRelativePath(const std::string &absolute_path) const
std::string GetAbsolutePath(const std::string &relative_path) const
std::string last_build_hash
std::string symbols_filename
Z3dkSettings z3dk_settings
std::string sourcemap_json
Z3dkArtifactPaths artifact_paths