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"
13#include "core/project.h"
15
16ABSL_DECLARE_FLAG(std::string, rom);
17ABSL_DECLARE_FLAG(bool, mock_rom);
18
19namespace yaze {
20namespace cli {
21namespace resources {
22
23// ============================================================================
24// CommandContext Implementation
25// ============================================================================
26
27CommandContext::CommandContext(const Config& config) : config_(config) {}
28
30 if (initialized_) {
31 return absl::OkStatus();
32 }
33
34 // If external ROM context is provided, use it
35 if (config_.external_rom_context != nullptr &&
38 initialized_ = true;
39 return absl::OkStatus();
40 }
41
42 // Check if mock ROM mode is enabled
45 if (!status.ok()) {
46 return status;
47 }
49 initialized_ = true;
50 return absl::OkStatus();
51 }
52
53 // Load ROM from file if path is provided
54 if (config_.rom_path.has_value() && !config_.rom_path->empty()) {
56 if (!status.ok()) {
57 return absl::FailedPreconditionError(
58 absl::StrFormat("Failed to load ROM from '%s': %s", *config_.rom_path,
59 status.message()));
60 }
62 initialized_ = true;
63 return absl::OkStatus();
64 }
65
66 // Try loading from flags as fallback
67 std::string rom_path = absl::GetFlag(FLAGS_rom);
68 if (!rom_path.empty()) {
69 auto status = rom_storage_.LoadFromFile(rom_path);
70 if (!status.ok()) {
71 return absl::FailedPreconditionError(absl::StrFormat(
72 "Failed to load ROM from '%s': %s", 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 project::YazeProject project;
106 project.use_embedded_labels = true;
107 auto labels_status = project.InitializeEmbeddedLabels(
109 if (labels_status.ok()) {
110 rom->resource_label()->labels_ = project.resource_labels;
111 rom->resource_label()->labels_loaded_ = true;
112 } else {
113 return labels_status;
114 }
115 }
116
117 return absl::OkStatus();
118}
119
120// ============================================================================
121// ArgumentParser Implementation
122// ============================================================================
123
124ArgumentParser::ArgumentParser(const std::vector<std::string>& args)
125 : args_(args) {}
126
127std::optional<std::string> ArgumentParser::FindArgValue(
128 const std::string& name) const {
129 std::string flag = "--" + name;
130 std::string equals_form = flag + "=";
131
132 for (size_t i = 0; i < args_.size(); ++i) {
133 const std::string& arg = args_[i];
134
135 // Check for --name=value form
136 if (absl::StartsWith(arg, equals_form)) {
137 return arg.substr(equals_form.length());
138 }
139
140 // Check for --name value form
141 if (arg == flag && i + 1 < args_.size()) {
142 return args_[i + 1];
143 }
144 }
145
146 return std::nullopt;
147}
148
149std::optional<std::string> ArgumentParser::GetString(
150 const std::string& name) const {
151 return FindArgValue(name);
152}
153
154absl::StatusOr<int> ArgumentParser::GetInt(const std::string& name) const {
155 auto value = FindArgValue(name);
156 if (!value.has_value()) {
157 return absl::NotFoundError(
158 absl::StrFormat("Argument '--%s' not found", name));
159 }
160
161 // Try hex first (with 0x prefix)
162 if (absl::StartsWith(*value, "0x") || absl::StartsWith(*value, "0X")) {
163 int result;
164 if (absl::SimpleHexAtoi(value->substr(2), &result)) {
165 return result;
166 }
167 return absl::InvalidArgumentError(
168 absl::StrFormat("Invalid hex integer for '--%s': %s", name, *value));
169 }
170
171 // Try decimal
172 int result;
173 if (absl::SimpleAtoi(*value, &result)) {
174 return result;
175 }
176
177 return absl::InvalidArgumentError(
178 absl::StrFormat("Invalid integer for '--%s': %s", name, *value));
179}
180
181absl::StatusOr<int> ArgumentParser::GetHex(const std::string& name) const {
182 auto value = FindArgValue(name);
183 if (!value.has_value()) {
184 return absl::NotFoundError(
185 absl::StrFormat("Argument '--%s' not found", name));
186 }
187
188 // Strip 0x prefix if present
189 std::string hex_str = *value;
190 if (absl::StartsWith(hex_str, "0x") || absl::StartsWith(hex_str, "0X")) {
191 hex_str = hex_str.substr(2);
192 }
193
194 int result;
195 if (absl::SimpleHexAtoi(hex_str, &result)) {
196 return result;
197 }
198
199 return absl::InvalidArgumentError(
200 absl::StrFormat("Invalid hex value for '--%s': %s", name, *value));
201}
202
203bool ArgumentParser::HasFlag(const std::string& name) const {
204 std::string flag = "--" + name;
205 for (const auto& arg : args_) {
206 if (arg == flag) {
207 return true;
208 }
209 }
210 return false;
211}
212
213std::vector<std::string> ArgumentParser::GetPositional() const {
214 std::vector<std::string> positional;
215 for (size_t i = 0; i < args_.size(); ++i) {
216 const std::string& arg = args_[i];
217 if (!absl::StartsWith(arg, "--")) {
218 positional.push_back(arg);
219 } else if (arg.find('=') == std::string::npos && i + 1 < args_.size()) {
220 // Skip the next argument as it's the value for this flag
221 ++i;
222 }
223 }
224 return positional;
225}
226
228 const std::vector<std::string>& required) const {
229 std::vector<std::string> missing;
230 for (const auto& arg : required) {
231 if (!FindArgValue(arg).has_value()) {
232 missing.push_back("--" + arg);
233 }
234 }
235
236 if (!missing.empty()) {
237 return absl::InvalidArgumentError(absl::StrFormat(
238 "Missing required arguments: %s", 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(absl::StrFormat(
257 "Unknown format: %s (expected 'json' or 'text')", format));
258 }
259}
260
261void OutputFormatter::BeginObject(const std::string& title) {
262 if (IsJson()) {
263 buffer_ += "{\n";
265 first_field_ = true;
266 } else if (IsText() && !title.empty()) {
267 buffer_ += absl::StrFormat("=== %s ===\n", title);
268 }
269}
270
272 if (IsJson()) {
273 buffer_ += "\n";
275 AddIndent();
276 buffer_ += "}";
277 }
278}
279
280void OutputFormatter::AddField(const std::string& key,
281 const std::string& value) {
282 if (IsJson()) {
283 if (!first_field_) {
284 buffer_ += ",\n";
285 }
286 first_field_ = false;
287 AddIndent();
288 buffer_ +=
289 absl::StrFormat("\"%s\": \"%s\"", EscapeJson(key), EscapeJson(value));
290 } else {
291 buffer_ += absl::StrFormat(" %-20s : %s\n", key, value);
292 }
293}
294
295void OutputFormatter::AddField(const std::string& key, int value) {
296 if (IsJson()) {
297 if (!first_field_) {
298 buffer_ += ",\n";
299 }
300 first_field_ = false;
301 AddIndent();
302 buffer_ += absl::StrFormat("\"%s\": %d", EscapeJson(key), value);
303 } else {
304 buffer_ += absl::StrFormat(" %-20s : %d\n", key, value);
305 }
306}
307
308void OutputFormatter::AddField(const std::string& key, uint64_t value) {
309 if (IsJson()) {
310 if (!first_field_) {
311 buffer_ += ",\n";
312 }
313 first_field_ = false;
314 AddIndent();
315 buffer_ += absl::StrFormat("\"%s\": %llu", EscapeJson(key), value);
316 } else {
317 buffer_ += absl::StrFormat(" %-20s : %llu\n", key, value);
318 }
319}
320
321void OutputFormatter::AddField(const std::string& key, bool value) {
322 if (IsJson()) {
323 if (!first_field_) {
324 buffer_ += ",\n";
325 }
326 first_field_ = false;
327 AddIndent();
328 buffer_ += absl::StrFormat("\"%s\": %s", EscapeJson(key),
329 value ? "true" : "false");
330 } else {
331 buffer_ += absl::StrFormat(" %-20s : %s\n", key, value ? "yes" : "no");
332 }
333}
334
335void OutputFormatter::AddHexField(const std::string& key, uint64_t value,
336 int width) {
337 if (IsJson()) {
338 if (!first_field_) {
339 buffer_ += ",\n";
340 }
341 first_field_ = false;
342 AddIndent();
343 buffer_ +=
344 absl::StrFormat("\"%s\": \"0x%0*X\"", EscapeJson(key), width, value);
345 } else {
346 buffer_ += absl::StrFormat(" %-20s : 0x%0*X\n", key, width, value);
347 }
348}
349
350void OutputFormatter::BeginArray(const std::string& key) {
351 in_array_ = true;
353
354 if (IsJson()) {
355 if (!first_field_) {
356 buffer_ += ",\n";
357 }
358 first_field_ = false;
359 AddIndent();
360 buffer_ += absl::StrFormat("\"%s\": [\n", EscapeJson(key));
362 } else {
363 buffer_ += absl::StrFormat(" %s:\n", key);
364 }
365}
366
368 in_array_ = false;
369
370 if (IsJson()) {
371 buffer_ += "\n";
373 AddIndent();
374 buffer_ += "]";
375 }
376}
377
378void OutputFormatter::AddArrayItem(const std::string& item) {
379 if (IsJson()) {
380 if (array_item_count_ > 0) {
381 buffer_ += ",\n";
382 }
383 AddIndent();
384 buffer_ += absl::StrFormat("\"%s\"", EscapeJson(item));
385 } else {
386 buffer_ += absl::StrFormat(" - %s\n", item);
387 }
389}
390
391std::string OutputFormatter::GetOutput() const {
392 return buffer_;
393}
394
396 std::cout << buffer_;
397 if (IsJson()) {
398 std::cout << "\n";
399 }
400}
401
403 for (int i = 0; i < indent_level_; ++i) {
404 buffer_ += " ";
405 }
406}
407
408std::string OutputFormatter::EscapeJson(const std::string& str) const {
409 std::string result;
410 result.reserve(str.size() + 10);
411
412 for (char c : str) {
413 switch (c) {
414 case '"':
415 result += "\\\"";
416 break;
417 case '\\':
418 result += "\\\\";
419 break;
420 case '\b':
421 result += "\\b";
422 break;
423 case '\f':
424 result += "\\f";
425 break;
426 case '\n':
427 result += "\\n";
428 break;
429 case '\r':
430 result += "\\r";
431 break;
432 case '\t':
433 result += "\\t";
434 break;
435 default:
436 if (c < 0x20) {
437 result += absl::StrFormat("\\u%04x", static_cast<int>(c));
438 } else {
439 result += c;
440 }
441 }
442 }
443
444 return result;
445}
446
447} // namespace resources
448} // namespace cli
449} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
project::ResourceLabelManager * resource_label()
Definition rom.h:146
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:74
bool is_loaded() const
Definition rom.h:128
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.
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
Configuration for command context.
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
Definition project.h:301
Modern project structure with comprehensive settings consolidation.
Definition project.h:84
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > resource_labels
Definition project.h:108
absl::Status InitializeEmbeddedLabels(const std::unordered_map< std::string, std::unordered_map< std::string, std::string > > &labels)
Definition project.cc:1334
static std::unordered_map< std::string, std::unordered_map< std::string, std::string > > ToResourceLabels()
Convert all labels to a structured map for project embedding.