yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
command_context.cc
Go to the documentation of this file.
2
3#include <iostream>
4
5#include "absl/flags/declare.h"
6#include "absl/flags/flag.h"
7#include "absl/strings/ascii.h"
8#include "absl/strings/match.h"
9#include "absl/strings/numbers.h"
10#include "absl/strings/str_format.h"
11#include "absl/strings/str_join.h"
12#include "app/core/project.h"
14
15ABSL_DECLARE_FLAG(std::string, rom);
16ABSL_DECLARE_FLAG(bool, mock_rom);
17
18namespace yaze {
19namespace cli {
20namespace resources {
21
22// ============================================================================
23// CommandContext Implementation
24// ============================================================================
25
26CommandContext::CommandContext(const Config& config) : config_(config) {}
27
29 if (initialized_) {
30 return absl::OkStatus();
31 }
32
33 // If external ROM context is provided, use it
34 if (config_.external_rom_context != nullptr &&
37 initialized_ = true;
38 return absl::OkStatus();
39 }
40
41 // Check if mock ROM mode is enabled
44 if (!status.ok()) {
45 return status;
46 }
48 initialized_ = true;
49 return absl::OkStatus();
50 }
51
52 // Load ROM from file if path is provided
53 if (config_.rom_path.has_value() && !config_.rom_path->empty()) {
55 if (!status.ok()) {
56 return absl::FailedPreconditionError(
57 absl::StrFormat("Failed to load ROM from '%s': %s",
58 *config_.rom_path, status.message()));
59 }
61 initialized_ = true;
62 return absl::OkStatus();
63 }
64
65 // Try loading from flags as fallback
66 std::string rom_path = absl::GetFlag(FLAGS_rom);
67 if (!rom_path.empty()) {
68 auto status = rom_storage_.LoadFromFile(rom_path);
69 if (!status.ok()) {
70 return absl::FailedPreconditionError(
71 absl::StrFormat("Failed to load ROM from '%s': %s",
72 rom_path, status.message()));
73 }
75 initialized_ = true;
76 return absl::OkStatus();
77 }
78
79 return absl::FailedPreconditionError(
80 "No ROM loaded. Use --rom=<path> or --mock-rom for testing.");
81}
82
83absl::StatusOr<Rom*> CommandContext::GetRom() {
84 if (!initialized_) {
85 auto status = Initialize();
86 if (!status.ok()) {
87 return status;
88 }
89 }
90
91 if (active_rom_ == nullptr) {
92 return absl::FailedPreconditionError("ROM not loaded");
93 }
94
95 return active_rom_;
96}
97
99 if (!rom->resource_label()) {
100 return absl::FailedPreconditionError("ROM has no resource label manager");
101 }
102
103 if (!rom->resource_label()->labels_loaded_ ||
104 rom->resource_label()->labels_.empty()) {
105 core::YazeProject project;
106 project.use_embedded_labels = true;
107 auto labels_status = project.InitializeEmbeddedLabels();
108 if (labels_status.ok()) {
109 rom->resource_label()->labels_ = project.resource_labels;
110 rom->resource_label()->labels_loaded_ = true;
111 } else {
112 return labels_status;
113 }
114 }
115
116 return absl::OkStatus();
117}
118
119// ============================================================================
120// ArgumentParser Implementation
121// ============================================================================
122
123ArgumentParser::ArgumentParser(const std::vector<std::string>& args)
124 : args_(args) {}
125
126std::optional<std::string> ArgumentParser::FindArgValue(
127 const std::string& name) const {
128 std::string flag = "--" + name;
129 std::string equals_form = flag + "=";
130
131 for (size_t i = 0; i < args_.size(); ++i) {
132 const std::string& arg = args_[i];
133
134 // Check for --name=value form
135 if (absl::StartsWith(arg, equals_form)) {
136 return arg.substr(equals_form.length());
137 }
138
139 // Check for --name value form
140 if (arg == flag && i + 1 < args_.size()) {
141 return args_[i + 1];
142 }
143 }
144
145 return std::nullopt;
146}
147
148std::optional<std::string> ArgumentParser::GetString(
149 const std::string& name) const {
150 return FindArgValue(name);
151}
152
153absl::StatusOr<int> ArgumentParser::GetInt(const std::string& name) const {
154 auto value = FindArgValue(name);
155 if (!value.has_value()) {
156 return absl::NotFoundError(
157 absl::StrFormat("Argument '--%s' not found", name));
158 }
159
160 // Try hex first (with 0x prefix)
161 if (absl::StartsWith(*value, "0x") || absl::StartsWith(*value, "0X")) {
162 int result;
163 if (absl::SimpleHexAtoi(value->substr(2), &result)) {
164 return result;
165 }
166 return absl::InvalidArgumentError(
167 absl::StrFormat("Invalid hex integer for '--%s': %s", name, *value));
168 }
169
170 // Try decimal
171 int result;
172 if (absl::SimpleAtoi(*value, &result)) {
173 return result;
174 }
175
176 return absl::InvalidArgumentError(
177 absl::StrFormat("Invalid integer for '--%s': %s", name, *value));
178}
179
180absl::StatusOr<int> ArgumentParser::GetHex(const std::string& name) const {
181 auto value = FindArgValue(name);
182 if (!value.has_value()) {
183 return absl::NotFoundError(
184 absl::StrFormat("Argument '--%s' not found", name));
185 }
186
187 // Strip 0x prefix if present
188 std::string hex_str = *value;
189 if (absl::StartsWith(hex_str, "0x") || absl::StartsWith(hex_str, "0X")) {
190 hex_str = hex_str.substr(2);
191 }
192
193 int result;
194 if (absl::SimpleHexAtoi(hex_str, &result)) {
195 return result;
196 }
197
198 return absl::InvalidArgumentError(
199 absl::StrFormat("Invalid hex value for '--%s': %s", name, *value));
200}
201
202bool ArgumentParser::HasFlag(const std::string& name) const {
203 std::string flag = "--" + name;
204 for (const auto& arg : args_) {
205 if (arg == flag) {
206 return true;
207 }
208 }
209 return false;
210}
211
212std::vector<std::string> ArgumentParser::GetPositional() const {
213 std::vector<std::string> positional;
214 for (size_t i = 0; i < args_.size(); ++i) {
215 const std::string& arg = args_[i];
216 if (!absl::StartsWith(arg, "--")) {
217 positional.push_back(arg);
218 } else if (arg.find('=') == std::string::npos && i + 1 < args_.size()) {
219 // Skip the next argument as it's the value for this flag
220 ++i;
221 }
222 }
223 return positional;
224}
225
227 const std::vector<std::string>& required) const {
228 std::vector<std::string> missing;
229 for (const auto& arg : required) {
230 if (!FindArgValue(arg).has_value()) {
231 missing.push_back("--" + arg);
232 }
233 }
234
235 if (!missing.empty()) {
236 return absl::InvalidArgumentError(
237 absl::StrFormat("Missing required arguments: %s",
238 absl::StrJoin(missing, ", ")));
239 }
240
241 return absl::OkStatus();
242}
243
244// ============================================================================
245// OutputFormatter Implementation
246// ============================================================================
247
248absl::StatusOr<OutputFormatter> OutputFormatter::FromString(
249 const std::string& format) {
250 std::string lower = absl::AsciiStrToLower(format);
251 if (lower == "json") {
253 } else if (lower == "text") {
255 } else {
256 return absl::InvalidArgumentError(
257 absl::StrFormat("Unknown format: %s (expected 'json' or 'text')",
258 format));
259 }
260}
261
262void OutputFormatter::BeginObject(const std::string& title) {
263 if (IsJson()) {
264 buffer_ += "{\n";
266 first_field_ = true;
267 } else if (IsText() && !title.empty()) {
268 buffer_ += absl::StrFormat("=== %s ===\n", title);
269 }
270}
271
273 if (IsJson()) {
274 buffer_ += "\n";
276 AddIndent();
277 buffer_ += "}";
278 }
279}
280
281void OutputFormatter::AddField(const std::string& key, const std::string& value) {
282 if (IsJson()) {
283 if (!first_field_) {
284 buffer_ += ",\n";
285 }
286 first_field_ = false;
287 AddIndent();
288 buffer_ += absl::StrFormat("\"%s\": \"%s\"", EscapeJson(key), EscapeJson(value));
289 } else {
290 buffer_ += absl::StrFormat(" %-20s : %s\n", key, value);
291 }
292}
293
294void OutputFormatter::AddField(const std::string& key, int value) {
295 if (IsJson()) {
296 if (!first_field_) {
297 buffer_ += ",\n";
298 }
299 first_field_ = false;
300 AddIndent();
301 buffer_ += absl::StrFormat("\"%s\": %d", EscapeJson(key), value);
302 } else {
303 buffer_ += absl::StrFormat(" %-20s : %d\n", key, value);
304 }
305}
306
307void OutputFormatter::AddField(const std::string& key, uint64_t value) {
308 if (IsJson()) {
309 if (!first_field_) {
310 buffer_ += ",\n";
311 }
312 first_field_ = false;
313 AddIndent();
314 buffer_ += absl::StrFormat("\"%s\": %llu", EscapeJson(key), value);
315 } else {
316 buffer_ += absl::StrFormat(" %-20s : %llu\n", key, value);
317 }
318}
319
320void OutputFormatter::AddField(const std::string& key, bool value) {
321 if (IsJson()) {
322 if (!first_field_) {
323 buffer_ += ",\n";
324 }
325 first_field_ = false;
326 AddIndent();
327 buffer_ += absl::StrFormat("\"%s\": %s", EscapeJson(key),
328 value ? "true" : "false");
329 } else {
330 buffer_ += absl::StrFormat(" %-20s : %s\n", key, value ? "yes" : "no");
331 }
332}
333
334void OutputFormatter::AddHexField(const std::string& key, uint64_t value,
335 int width) {
336 if (IsJson()) {
337 if (!first_field_) {
338 buffer_ += ",\n";
339 }
340 first_field_ = false;
341 AddIndent();
342 buffer_ += absl::StrFormat("\"%s\": \"0x%0*X\"", EscapeJson(key), width, value);
343 } else {
344 buffer_ += absl::StrFormat(" %-20s : 0x%0*X\n", key, width, value);
345 }
346}
347
348void OutputFormatter::BeginArray(const std::string& key) {
349 in_array_ = true;
351
352 if (IsJson()) {
353 if (!first_field_) {
354 buffer_ += ",\n";
355 }
356 first_field_ = false;
357 AddIndent();
358 buffer_ += absl::StrFormat("\"%s\": [\n", EscapeJson(key));
360 } else {
361 buffer_ += absl::StrFormat(" %s:\n", key);
362 }
363}
364
366 in_array_ = false;
367
368 if (IsJson()) {
369 buffer_ += "\n";
371 AddIndent();
372 buffer_ += "]";
373 }
374}
375
376void OutputFormatter::AddArrayItem(const std::string& item) {
377 if (IsJson()) {
378 if (array_item_count_ > 0) {
379 buffer_ += ",\n";
380 }
381 AddIndent();
382 buffer_ += absl::StrFormat("\"%s\"", EscapeJson(item));
383 } else {
384 buffer_ += absl::StrFormat(" - %s\n", item);
385 }
387}
388
389std::string OutputFormatter::GetOutput() const {
390 return buffer_;
391}
392
394 std::cout << buffer_;
395 if (IsJson()) {
396 std::cout << "\n";
397 }
398}
399
401 for (int i = 0; i < indent_level_; ++i) {
402 buffer_ += " ";
403 }
404}
405
406std::string OutputFormatter::EscapeJson(const std::string& str) const {
407 std::string result;
408 result.reserve(str.size() + 10);
409
410 for (char c : str) {
411 switch (c) {
412 case '"': result += "\\\""; break;
413 case '\\': result += "\\\\"; break;
414 case '\b': result += "\\b"; break;
415 case '\f': result += "\\f"; break;
416 case '\n': result += "\\n"; break;
417 case '\r': result += "\\r"; break;
418 case '\t': result += "\\t"; break;
419 default:
420 if (c < 0x20) {
421 result += absl::StrFormat("\\u%04x", static_cast<int>(c));
422 } else {
423 result += c;
424 }
425 }
426 }
427
428 return result;
429}
430
431} // namespace resources
432} // namespace cli
433} // namespace yaze
434
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:71
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:289
core::ResourceLabelManager * resource_label()
Definition rom.h:220
bool is_loaded() const
Definition rom.h:197
std::optional< std::string > FindArgValue(const std::string &name) const
std::vector< std::string > args_
ArgumentParser(const std::vector< std::string > &args)
std::vector< std::string > GetPositional() const
Get all remaining positional arguments.
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::Status RequireArgs(const std::vector< std::string > &required) const
Validate that required arguments are present.
absl::StatusOr< int > GetHex(const std::string &name) const
Parse a hex integer argument.
absl::StatusOr< int > GetInt(const std::string &name) const
Parse an integer argument (supports hex with 0x prefix)
absl::StatusOr< Rom * > GetRom()
Get the ROM instance (loads if not already loaded)
absl::Status EnsureLabelsLoaded(Rom *rom)
Ensure resource labels are loaded.
absl::Status Initialize()
Initialize the context and load ROM if needed.
Utility for consistent output formatting across commands.
void BeginArray(const std::string &key)
Begin an array.
std::string GetOutput() const
Get the formatted output.
static absl::StatusOr< OutputFormatter > FromString(const std::string &format)
Create formatter from string ("json" or "text")
void AddArrayItem(const std::string &item)
Add an item to current array.
void BeginObject(const std::string &title="")
Start a JSON object or text section.
void EndObject()
End a JSON object or text section.
std::string EscapeJson(const std::string &str) const
void AddField(const std::string &key, const std::string &value)
Add a key-value pair.
bool IsJson() const
Check if using JSON format.
bool IsText() const
Check if using text format.
void AddHexField(const std::string &key, uint64_t value, int width=2)
Add a hex-formatted field.
void Print() const
Print the formatted output to stdout.
ABSL_DECLARE_FLAG(std::string, rom)
absl::Status InitializeMockRom(Rom &rom)
Initialize a mock ROM for testing without requiring an actual ROM file.
Definition mock_rom.cc:16
Main namespace for the application.
Configuration for command context.
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
Definition project.h:235
Modern project structure with comprehensive settings consolidation.
Definition project.h:78
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > resource_labels
Definition project.h:100
absl::Status InitializeEmbeddedLabels()
Definition project.cc:887