yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
cli_main.cc
Go to the documentation of this file.
1#include <atomic>
2#include <chrono>
3#include <csignal>
4#include <cstdlib>
5#include <iostream>
6#include <optional>
7#include <string>
8#include <thread>
9#include <vector>
10
11#include "absl/flags/declare.h"
12#include "absl/flags/flag.h"
13#include "absl/strings/match.h"
14#include "absl/strings/str_format.h"
15#include "absl/strings/string_view.h"
16#include "cli/cli.h"
17#ifdef YAZE_ENABLE_AGENT_CLI
19#endif
21#include "cli/z3ed_ascii_logo.h"
22#include "rom/rom.h"
23#include "util/platform_paths.h"
24#include "yaze_config.h"
25
26#ifdef YAZE_HTTP_API_ENABLED
29#include "util/log.h"
30#include "zelda3/game_data.h"
31#endif
32
33// Define all CLI flags
34ABSL_DECLARE_FLAG(bool, quiet);
35ABSL_DECLARE_FLAG(bool, sandbox);
36ABSL_FLAG(bool, version, false, "Show version information");
37ABSL_FLAG(bool, self_test, false,
38 "Run self-test diagnostics to verify CLI functionality");
39#ifdef YAZE_HTTP_API_ENABLED
40ABSL_FLAG(int, http_port, 0,
41 "HTTP API server port (0 = disabled, default: 8080 when enabled)");
42ABSL_FLAG(std::string, http_host, "localhost",
43 "HTTP API server host (default: localhost)");
44#endif
45ABSL_DECLARE_FLAG(std::string, rom);
46ABSL_DECLARE_FLAG(std::string, ai_provider);
47ABSL_DECLARE_FLAG(std::string, ai_model);
48ABSL_DECLARE_FLAG(std::string, gemini_api_key);
49ABSL_DECLARE_FLAG(std::string, anthropic_api_key);
50ABSL_DECLARE_FLAG(std::string, ollama_host);
51ABSL_DECLARE_FLAG(std::string, mesen_socket);
52ABSL_DECLARE_FLAG(std::string, gui_server_address);
53ABSL_DECLARE_FLAG(std::string, prompt_version);
54ABSL_DECLARE_FLAG(bool, use_function_calling);
55
56namespace {
57
59 std::cout << yaze::cli::GetColoredLogo() << "\n";
60 std::cout << absl::StrFormat(" Version %d.%d.%d\n", YAZE_VERSION_MAJOR,
61 YAZE_VERSION_MINOR, YAZE_VERSION_PATCH);
62 std::cout << " Yet Another Zelda3 Editor - Command Line Interface\n";
63 std::cout << " https://github.com/scawful/yaze\n\n";
64}
65
71 std::cout << "\n\033[1;36m=== z3ed Self-Test ===\033[0m\n\n";
72 int passed = 0;
73 int failed = 0;
74
75 auto run_test = [&](const char* name, bool condition) {
76 if (condition) {
77 std::cout << " \033[1;32m✓\033[0m " << name << "\n";
78 ++passed;
79 } else {
80 std::cout << " \033[1;31m✗\033[0m " << name << "\n";
81 ++failed;
82 }
83 };
84
85 // Test 1: Version info is available
86 run_test("Version info available",
87 YAZE_VERSION_MAJOR >= 0 && YAZE_VERSION_MINOR >= 0);
88
89 // Test 2: CLI instance can be created
90 {
91 bool cli_created = false;
92 try {
94 cli_created = true;
95 } catch (...) {
96 cli_created = false;
97 }
98 run_test("CLI instance creation", cli_created);
99 }
100
101 // Test 3: App context is accessible
102 run_test("App context accessible", true); // Always passes if we got here
103
104 // Test 4: ROM class can be instantiated
105 {
106 bool rom_ok = false;
107 try {
108 yaze::Rom test_rom;
109 rom_ok = true;
110 } catch (...) {
111 rom_ok = false;
112 }
113 run_test("ROM class instantiation", rom_ok);
114 }
115
116 // Test 5: Flag parsing works
117 run_test("Flag parsing functional", absl::GetFlag(FLAGS_self_test) == true);
118
119#ifdef YAZE_HTTP_API_ENABLED
120 // Test 6: HTTP API available (if compiled in)
121 run_test("HTTP API compiled in", true);
122#else
123 run_test("HTTP API not compiled (expected)", true);
124#endif
125
126 // Representative runtime assets must resolve from portable, bundle, and
127 // FHS package layouts without depending on the source checkout.
128 run_test(
129 "Agent catalogue asset available",
130 yaze::util::PlatformPaths::FindAsset("agent/prompt_catalogue.yaml").ok());
131 run_test("Overworld patch asset available",
133 "patches/Overworld/TailMapExpansion.asm")
134 .ok());
135
136 // Summary
137 std::cout << "\n\033[1;36m=== Results ===\033[0m\n";
138 std::cout << " Passed: \033[1;32m" << passed << "\033[0m\n";
139 std::cout << " Failed: \033[1;31m" << failed << "\033[0m\n";
140
141 if (failed == 0) {
142 std::cout << "\n\033[1;32mAll self-tests passed!\033[0m\n\n";
143 return EXIT_SUCCESS;
144 } else {
145 std::cout << "\n\033[1;31mSome self-tests failed.\033[0m\n\n";
146 return EXIT_FAILURE;
147 }
148}
149
151 auto& registry = yaze::cli::CommandRegistry::Instance();
152 auto categories = registry.GetCategories();
153
154 std::cout << yaze::cli::GetColoredLogo() << "\n";
155 std::cout
156 << " \033[1;37mYet Another Zelda3 Editor - AI-Powered CLI\033[0m\n\n";
157
158 std::cout << "\033[1;36mUSAGE:\033[0m\n";
159 std::cout << " z3ed [command] [flags]\n";
160 std::cout << " z3ed help <command|category> # Scoped help\n";
161 std::cout << " z3ed --export-schemas # JSON schemas for agents\n";
162 std::cout << " z3ed --version # Show version\n\n";
163
164 std::cout << "\033[1;36mCATEGORIES:\033[0m\n";
165 for (const auto& category : categories) {
166 auto commands = registry.GetCommandsInCategory(category);
167 std::cout << " \033[1;33m" << category << "\033[0m (" << commands.size()
168 << " commands)\n";
169 }
170 std::cout << "\n";
171
172 std::cout << "\033[1;36mCOMMON FLAGS:\033[0m\n";
173 std::cout << " --rom=<path> Path to ROM file\n";
174 std::cout << " --sandbox Run ROM commands in a sandbox copy\n";
175 std::cout << " --quiet, -q Suppress output\n";
176 std::cout << " --ai_provider=<name> AI provider (auto, ollama, gemini, "
177 "openai,\n";
178 std::cout << " anthropic, mock)\n";
179 std::cout << " --ai_model=<name> Provider-specific model override\n";
180 std::cout << " --version Show version\n";
181 std::cout << " --self-test Run self-test diagnostics\n";
182 std::cout << " --help <scope> Show help for command or category\n";
183 std::cout << " --export-schemas Export command schemas as JSON\n";
184#ifdef YAZE_HTTP_API_ENABLED
185 std::cout << " --http-port=<port> HTTP API server port (0=disabled)\n";
186 std::cout
187 << " --http-host=<host> HTTP API display host (printed URLs)\n";
188 std::cout << " (no command keeps server alive until "
189 "Ctrl+C)\n";
190#endif
191 std::cout << " --mesen-socket=<path> Override Mesen2 socket path\n";
192 std::cout << "\n";
193
194 std::cout << "\033[1;36mEXAMPLES:\033[0m\n";
195#ifdef YAZE_ENABLE_AGENT_CLI
196 std::cout << " z3ed agent simple-chat --rom=zelda3.sfc\n";
197#endif
198 std::cout << " z3ed rom-info --rom=zelda3.sfc\n";
199 std::cout
200 << " z3ed rom read --address=0x1000 --length=16 --rom=zelda3.sfc\n";
201 std::cout << " z3ed debug state\n";
202 std::cout
203 << " z3ed message-search --rom=zelda3.sfc --query=\"Master Sword\"\n";
204 std::cout << " z3ed dungeon-export --rom=zelda3.sfc --id=1\n\n";
205
206 std::cout << "For detailed help: z3ed help <command>\n";
207 std::cout << "For all commands: z3ed --list-commands\n\n";
208}
209
210#ifdef YAZE_HTTP_API_ENABLED
211std::atomic<bool> g_http_shutdown_requested{false};
212
213void HandleHttpShutdownSignal(int) {
214 g_http_shutdown_requested.store(true);
215}
216
217void WaitForHttpServer() {
218 std::signal(SIGINT, HandleHttpShutdownSignal);
219 std::signal(SIGTERM, HandleHttpShutdownSignal);
220 while (!g_http_shutdown_requested.load()) {
221 std::this_thread::sleep_for(std::chrono::milliseconds(200));
222 }
223}
224#endif
225
227 std::vector<char*> positional;
228 bool show_help = false;
229 bool show_version = false;
230 bool list_commands = false;
231 bool self_test = false;
232 bool export_schemas = false;
233 std::optional<std::string> help_target;
234 std::optional<std::string> error;
235};
236
237ParsedGlobals ParseGlobalFlags(int argc, char* argv[]) {
238 ParsedGlobals result;
239 if (argc <= 0 || argv == nullptr) {
240 result.error = "Invalid argv provided";
241 return result;
242 }
243
244 result.positional.reserve(argc);
245 result.positional.push_back(argv[0]);
246
247 bool passthrough = false;
248 for (int i = 1; i < argc; ++i) {
249 char* current = argv[i];
250 absl::string_view token(current);
251
252 if (!passthrough) {
253 if (token == "--") {
254 passthrough = true;
255 continue;
256 }
257
258 // Help flags
259 if (absl::StartsWith(token, "--help=")) {
260 std::string target(token.substr(7));
261 if (!target.empty()) {
262 result.help_target = target;
263 } else {
264 result.show_help = true;
265 }
266 continue;
267 }
268 if (token == "--help" || token == "-h") {
269 if (i + 1 < argc && argv[i + 1][0] != '-') {
270 result.help_target = std::string(argv[++i]);
271 } else {
272 result.show_help = true;
273 }
274 continue;
275 }
276
277 // Version flag
278 if (token == "--version" || token == "-v") {
279 result.show_version = true;
280 continue;
281 }
282
283 // List commands
284 if (token == "--list-commands" || token == "--list") {
285 result.list_commands = true;
286 continue;
287 }
288
289 // Schema export
290 if (token == "--export-schemas" || token == "--export_schemas") {
291 result.export_schemas = true;
292 continue;
293 }
294
295 // Self-test mode
296 if (token == "--self-test" || token == "--selftest") {
297 result.self_test = true;
298 continue;
299 }
300
301 if (token == "--tui" || token == "--interactive") {
302 result.error =
303 "--tui/--interactive was removed; use `z3ed help` for CLI "
304 "workflows";
305 return result;
306 }
307
308 // Quiet mode
309 if (token == "--quiet" || token == "-q") {
310 absl::SetFlag(&FLAGS_quiet, true);
311 continue;
312 }
313 if (absl::StartsWith(token, "--quiet=")) {
314 std::string value(token.substr(8));
315 absl::SetFlag(&FLAGS_quiet, value == "true" || value == "1");
316 continue;
317 }
318
319 // Sandbox mode
320 if (token == "--sandbox") {
321 absl::SetFlag(&FLAGS_sandbox, true);
322 continue;
323 }
324 if (absl::StartsWith(token, "--sandbox=")) {
325 std::string value(token.substr(10));
326 absl::SetFlag(&FLAGS_sandbox, value == "true" || value == "1");
327 continue;
328 }
329
330 // ROM path
331 if (absl::StartsWith(token, "--rom=")) {
332 absl::SetFlag(&FLAGS_rom, std::string(token.substr(6)));
333 continue;
334 }
335 if (token == "--rom") {
336 if (i + 1 >= argc) {
337 result.error = "--rom flag requires a value";
338 return result;
339 }
340 absl::SetFlag(&FLAGS_rom, std::string(argv[++i]));
341 continue;
342 }
343
344 // AI provider flags
345 if (absl::StartsWith(token, "--ai_provider=") ||
346 absl::StartsWith(token, "--ai-provider=")) {
347 size_t eq_pos = token.find('=');
348 absl::SetFlag(&FLAGS_ai_provider,
349 std::string(token.substr(eq_pos + 1)));
350 continue;
351 }
352 if (token == "--ai_provider" || token == "--ai-provider") {
353 if (i + 1 >= argc) {
354 result.error = "--ai-provider flag requires a value";
355 return result;
356 }
357 absl::SetFlag(&FLAGS_ai_provider, std::string(argv[++i]));
358 continue;
359 }
360
361 if (absl::StartsWith(token, "--ai_model=") ||
362 absl::StartsWith(token, "--ai-model=")) {
363 size_t eq_pos = token.find('=');
364 absl::SetFlag(&FLAGS_ai_model, std::string(token.substr(eq_pos + 1)));
365 continue;
366 }
367 if (token == "--ai_model" || token == "--ai-model") {
368 if (i + 1 >= argc) {
369 result.error = "--ai-model flag requires a value";
370 return result;
371 }
372 absl::SetFlag(&FLAGS_ai_model, std::string(argv[++i]));
373 continue;
374 }
375
376 if (absl::StartsWith(token, "--gemini_api_key=") ||
377 absl::StartsWith(token, "--gemini-api-key=")) {
378 size_t eq_pos = token.find('=');
379 absl::SetFlag(&FLAGS_gemini_api_key,
380 std::string(token.substr(eq_pos + 1)));
381 continue;
382 }
383 if (token == "--gemini_api_key" || token == "--gemini-api-key") {
384 if (i + 1 >= argc) {
385 result.error = "--gemini-api-key flag requires a value";
386 return result;
387 }
388 absl::SetFlag(&FLAGS_gemini_api_key, std::string(argv[++i]));
389 continue;
390 }
391
392 if (absl::StartsWith(token, "--anthropic_api_key=") ||
393 absl::StartsWith(token, "--anthropic-api-key=")) {
394 size_t eq_pos = token.find('=');
395 absl::SetFlag(&FLAGS_anthropic_api_key,
396 std::string(token.substr(eq_pos + 1)));
397 continue;
398 }
399 if (token == "--anthropic_api_key" || token == "--anthropic-api-key") {
400 if (i + 1 >= argc) {
401 result.error = "--anthropic-api-key flag requires a value";
402 return result;
403 }
404 absl::SetFlag(&FLAGS_anthropic_api_key, std::string(argv[++i]));
405 continue;
406 }
407
408 if (absl::StartsWith(token, "--gui_server_address=") ||
409 absl::StartsWith(token, "--gui-server-address=")) {
410 size_t eq_pos = token.find('=');
411 absl::SetFlag(&FLAGS_gui_server_address,
412 std::string(token.substr(eq_pos + 1)));
413 continue;
414 }
415 if (token == "--gui_server_address" || token == "--gui-server-address") {
416 if (i + 1 >= argc) {
417 result.error = "--gui-server-address flag requires a value";
418 return result;
419 }
420 absl::SetFlag(&FLAGS_gui_server_address, std::string(argv[++i]));
421 continue;
422 }
423
424 if (absl::StartsWith(token, "--ollama_host=") ||
425 absl::StartsWith(token, "--ollama-host=")) {
426 size_t eq_pos = token.find('=');
427 absl::SetFlag(&FLAGS_ollama_host,
428 std::string(token.substr(eq_pos + 1)));
429 continue;
430 }
431 if (token == "--ollama_host" || token == "--ollama-host") {
432 if (i + 1 >= argc) {
433 result.error = "--ollama-host flag requires a value";
434 return result;
435 }
436 absl::SetFlag(&FLAGS_ollama_host, std::string(argv[++i]));
437 continue;
438 }
439
440 if (absl::StartsWith(token, "--mesen-socket=") ||
441 absl::StartsWith(token, "--mesen_socket=")) {
442 size_t eq_pos = token.find('=');
443 absl::SetFlag(&FLAGS_mesen_socket,
444 std::string(token.substr(eq_pos + 1)));
445 continue;
446 }
447 if (token == "--mesen-socket" || token == "--mesen_socket") {
448 if (i + 1 >= argc) {
449 result.error = "--mesen-socket flag requires a value";
450 return result;
451 }
452 absl::SetFlag(&FLAGS_mesen_socket, std::string(argv[++i]));
453 continue;
454 }
455
456 if (absl::StartsWith(token, "--prompt_version=") ||
457 absl::StartsWith(token, "--prompt-version=")) {
458 size_t eq_pos = token.find('=');
459 absl::SetFlag(&FLAGS_prompt_version,
460 std::string(token.substr(eq_pos + 1)));
461 continue;
462 }
463 if (token == "--prompt_version" || token == "--prompt-version") {
464 if (i + 1 >= argc) {
465 result.error = "--prompt-version flag requires a value";
466 return result;
467 }
468 absl::SetFlag(&FLAGS_prompt_version, std::string(argv[++i]));
469 continue;
470 }
471
472 if (absl::StartsWith(token, "--use_function_calling=") ||
473 absl::StartsWith(token, "--use-function-calling=")) {
474 size_t eq_pos = token.find('=');
475 std::string value(token.substr(eq_pos + 1));
476 absl::SetFlag(&FLAGS_use_function_calling,
477 value == "true" || value == "1");
478 continue;
479 }
480 if (token == "--use_function_calling" ||
481 token == "--use-function-calling") {
482 if (i + 1 >= argc) {
483 result.error = "--use-function-calling flag requires a value";
484 return result;
485 }
486 std::string value(argv[++i]);
487 absl::SetFlag(&FLAGS_use_function_calling,
488 value == "true" || value == "1");
489 continue;
490 }
491
492#ifdef YAZE_HTTP_API_ENABLED
493 // HTTP server flags
494 if (absl::StartsWith(token, "--http-port=") ||
495 absl::StartsWith(token, "--http_port=")) {
496 size_t eq_pos = token.find('=');
497 try {
498 int port = std::stoi(std::string(token.substr(eq_pos + 1)));
499 absl::SetFlag(&FLAGS_http_port, port);
500 } catch (...) {
501 result.error = "--http-port requires an integer value";
502 return result;
503 }
504 continue;
505 }
506 if (token == "--http-port" || token == "--http_port") {
507 if (i + 1 >= argc) {
508 result.error = "--http-port flag requires a value";
509 return result;
510 }
511 try {
512 int port = std::stoi(std::string(argv[++i]));
513 absl::SetFlag(&FLAGS_http_port, port);
514 } catch (...) {
515 result.error = "--http-port requires an integer value";
516 return result;
517 }
518 continue;
519 }
520
521 if (absl::StartsWith(token, "--http-host=") ||
522 absl::StartsWith(token, "--http_host=")) {
523 size_t eq_pos = token.find('=');
524 absl::SetFlag(&FLAGS_http_host, std::string(token.substr(eq_pos + 1)));
525 continue;
526 }
527 if (token == "--http-host" || token == "--http_host") {
528 if (i + 1 >= argc) {
529 result.error = "--http-host flag requires a value";
530 return result;
531 }
532 absl::SetFlag(&FLAGS_http_host, std::string(argv[++i]));
533 continue;
534 }
535#endif
536 }
537
538 result.positional.push_back(current);
539 }
540
541 return result;
542}
543
544} // namespace
545
546int main(int argc, char* argv[]) {
547 // Parse global flags
548 ParsedGlobals globals = ParseGlobalFlags(argc, argv);
549
550 if (globals.error.has_value()) {
551 std::cerr << "Error: " << *globals.error << "\n";
552 std::cerr << "Use --help for usage information.\n";
553 return EXIT_FAILURE;
554 }
555
556 // Handle version flag
557 if (globals.show_version) {
558 PrintVersion();
559 return EXIT_SUCCESS;
560 }
561
562 // Handle self-test flag
563 if (globals.self_test) {
564 absl::SetFlag(&FLAGS_self_test, true); // Ensure flag is set for test
565 return RunSelfTest();
566 }
567
568 auto& registry = yaze::cli::CommandRegistry::Instance();
569#ifdef YAZE_ENABLE_AGENT_CLI
571#endif
572
573 if (globals.export_schemas) {
574 std::cout << registry.ExportFunctionSchemas() << "\n";
575 return EXIT_SUCCESS;
576 }
577
578#ifdef YAZE_HTTP_API_ENABLED
579 // Start HTTP API server if requested
580 std::unique_ptr<yaze::cli::api::HttpServer> http_server;
581
582 // Shared ROM / GameData / RenderService owned here, passed by pointer to the
583 // server. Lifetime: alive until end of main (outlives the server thread).
584 std::unique_ptr<yaze::Rom> api_rom;
585 std::unique_ptr<yaze::zelda3::GameData> api_game_data;
586 std::unique_ptr<yaze::app::service::RenderService> api_render_service;
587
588 int http_port = absl::GetFlag(FLAGS_http_port);
589
590 if (http_port > 0) {
591 std::string http_host = absl::GetFlag(FLAGS_http_host);
592 http_server = std::make_unique<yaze::cli::api::HttpServer>();
593
594 // Wire RenderService if ROM is available.
595 const std::string api_rom_path = absl::GetFlag(FLAGS_rom);
596 if (!api_rom_path.empty()) {
597 api_rom = std::make_unique<yaze::Rom>();
598 auto rom_st = api_rom->LoadFromFile(api_rom_path);
599 if (rom_st.ok()) {
600 api_game_data = std::make_unique<yaze::zelda3::GameData>();
601 auto gd_st = yaze::zelda3::LoadGameData(*api_rom, *api_game_data);
602 if (gd_st.ok()) {
603 api_render_service =
604 std::make_unique<yaze::app::service::RenderService>(
605 api_rom.get(), api_game_data.get());
606 yaze::app::service::RenderService* rs_raw = api_render_service.get();
607 http_server->SetRenderServiceSource([rs_raw]() { return rs_raw; });
608 }
609 }
610 }
611
612 auto status = http_server->Start(http_port);
613 if (!status.ok()) {
614 std::cerr
615 << "\n\033[1;31mWarning:\033[0m Failed to start HTTP API server: "
616 << status.message() << "\n";
617 std::cerr << "Continuing without HTTP API...\n\n";
618 http_server.reset();
619 } else if (!absl::GetFlag(FLAGS_quiet)) {
620 std::cout << "\033[1;32m✓\033[0m HTTP API server started on " << http_host
621 << ":" << http_port << "\n";
622 std::cout << " Health check: http://" << http_host << ":" << http_port
623 << "/api/v1/health\n";
624 std::cout << " Models list: http://" << http_host << ":" << http_port
625 << "/api/v1/models\n\n";
626 }
627 } else if (http_port == 0 && !absl::GetFlag(FLAGS_quiet)) {
628 // Port 0 means explicitly disabled, only show message in verbose mode
629 }
630#endif
631
632 // Create CLI instance
634
635 // Route `z3ed <command> --help` to command/category scoped help.
636 if (globals.show_help && !globals.help_target.has_value() &&
637 globals.positional.size() > 1) {
638 std::string inferred_target(globals.positional[1]);
639 if (inferred_target != "help") {
640 globals.help_target = inferred_target;
641 globals.show_help = false;
642 }
643 }
644
645 // Handle targeted help (command or category)
646 if (globals.help_target.has_value()) {
647 const std::string& target = *globals.help_target;
648 if (target == "all") {
649 std::cout << registry.GenerateCompleteHelp() << "\n";
650 } else if (registry.HasCommand(target)) {
651 std::cout << registry.GenerateHelp(target) << "\n";
652 } else if (!registry.GetCommandsInCategory(target).empty()) {
653 cli.PrintCategoryHelp(target);
654 } else {
655 std::cerr << "\n\033[1;31mError:\033[0m Unknown command or category '"
656 << target << "'\n";
657 std::cerr << "Use --list-commands for a full command list.\n";
658 return EXIT_FAILURE;
659 }
660 return EXIT_SUCCESS;
661 }
662
663 // Handle list commands
664 if (globals.list_commands) {
666 return EXIT_SUCCESS;
667 }
668
669#ifdef YAZE_HTTP_API_ENABLED
670 if (!globals.show_help && globals.positional.size() <= 1 && http_server &&
671 http_server->IsRunning()) {
672 if (!absl::GetFlag(FLAGS_quiet)) {
673 std::cout << "HTTP API server running. Press Ctrl+C to exit.\n";
674 }
675 WaitForHttpServer();
676 return EXIT_SUCCESS;
677 }
678#endif
679
680 // Handle general help or no arguments
681 if (globals.show_help || globals.positional.size() <= 1) {
683 return EXIT_SUCCESS;
684 }
685
686 // Run CLI commands
687 auto status = cli.Run(static_cast<int>(globals.positional.size()),
688 globals.positional.data());
689
690 if (!status.ok()) {
691 std::cerr << "\n\033[1;31mError:\033[0m " << status.message() << "\n";
692 std::cerr << "Use --help for usage information.\n";
693 return EXIT_FAILURE;
694 }
695
696 return EXIT_SUCCESS;
697}
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
static CommandRegistry & Instance()
absl::Status Run(int argc, char *argv[])
Definition cli.cc:27
void PrintCategoryHelp(const std::string &category) const
Definition cli.cc:335
void PrintCommandSummary() const
Definition cli.cc:339
static absl::StatusOr< std::filesystem::path > FindAsset(const std::string &relative_path)
Find an asset file in multiple standard locations.
int main(int argc, char *argv[])
Definition cli_main.cc:546
ABSL_FLAG(bool, version, false, "Show version information")
ABSL_DECLARE_FLAG(bool, quiet)
int RunSelfTest()
Run self-test diagnostics to verify CLI functionality.
Definition cli_main.cc:70
ParsedGlobals ParseGlobalFlags(int argc, char *argv[])
Definition cli_main.cc:237
std::string GetColoredLogo()
absl::Status LoadGameData(Rom &rom, GameData &data, const LoadOptions &options)
Loads all Zelda3-specific game data from a generic ROM.
Definition game_data.cc:123
std::optional< std::string > help_target
Definition cli_main.cc:233