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