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 <cstdlib>
2#include <iostream>
3#include <optional>
4#include <string>
5#include <string_view>
6#include <vector>
7
8#include "absl/flags/declare.h"
9#include "absl/flags/flag.h"
10#include "absl/strings/match.h"
11#include "absl/strings/str_format.h"
12#include "cli/cli.h"
13#include "cli/tui/tui.h"
14#include "cli/z3ed_ascii_logo.h"
15#include "yaze_config.h"
16
17// Define all CLI flags
18ABSL_FLAG(bool, tui, false, "Launch interactive Text User Interface");
19ABSL_FLAG(bool, quiet, false, "Suppress non-essential output");
20ABSL_FLAG(bool, version, false, "Show version information");
21ABSL_DECLARE_FLAG(std::string, rom);
22ABSL_DECLARE_FLAG(std::string, ai_provider);
23ABSL_DECLARE_FLAG(std::string, ai_model);
24ABSL_DECLARE_FLAG(std::string, gemini_api_key);
25ABSL_DECLARE_FLAG(std::string, ollama_host);
26ABSL_DECLARE_FLAG(std::string, prompt_version);
27ABSL_DECLARE_FLAG(bool, use_function_calling);
28
29namespace {
30
32 std::cout << yaze::cli::GetColoredLogo() << "\n";
33 std::cout << absl::StrFormat(" Version %d.%d.%d\n",
34 YAZE_VERSION_MAJOR,
35 YAZE_VERSION_MINOR,
36 YAZE_VERSION_PATCH);
37 std::cout << " Yet Another Zelda3 Editor - Command Line Interface\n";
38 std::cout << " https://github.com/scawful/yaze\n\n";
39}
40
42 std::cout << yaze::cli::GetColoredLogo() << "\n";
43 std::cout << " \033[1;37mYet Another Zelda3 Editor - AI-Powered CLI\033[0m\n\n";
44
45 std::cout << "\033[1;36mUSAGE:\033[0m\n";
46 std::cout << " z3ed [command] [flags]\n";
47 std::cout << " z3ed --tui # Interactive TUI mode\n";
48 std::cout << " z3ed --version # Show version\n";
49 std::cout << " z3ed --help <category> # Category help\n\n";
50
51 std::cout << "\033[1;36mCOMMANDS:\033[0m\n";
52 std::cout << " \033[1;33magent\033[0m AI conversational agent for ROM inspection\n";
53 std::cout << " \033[1;33mrom\033[0m ROM operations (info, validate, diff)\n";
54 std::cout << " \033[1;33mdungeon\033[0m Dungeon inspection and editing\n";
55 std::cout << " \033[1;33moverworld\033[0m Overworld inspection and editing\n";
56 std::cout << " \033[1;33mmessage\033[0m Message/dialogue inspection\n";
57 std::cout << " \033[1;33mgfx\033[0m Graphics operations (export, import)\n";
58 std::cout << " \033[1;33mpalette\033[0m Palette operations\n";
59 std::cout << " \033[1;33mpatch\033[0m Apply patches (BPS, Asar)\n";
60 std::cout << " \033[1;33mproject\033[0m Project management (init, build)\n\n";
61
62 std::cout << "\033[1;36mCOMMON FLAGS:\033[0m\n";
63 std::cout << " --rom=<path> Path to ROM file\n";
64 std::cout << " --tui Launch interactive TUI\n";
65 std::cout << " --quiet, -q Suppress output\n";
66 std::cout << " --version Show version\n";
67 std::cout << " --help <category> Show category help\n\n";
68
69 std::cout << "\033[1;36mEXAMPLES:\033[0m\n";
70 std::cout << " z3ed agent test-conversation --rom=zelda3.sfc\n";
71 std::cout << " z3ed rom info --rom=zelda3.sfc\n";
72 std::cout << " z3ed agent message-search --rom=zelda3.sfc --query=\"Master Sword\"\n";
73 std::cout << " z3ed dungeon export --rom=zelda3.sfc --id=1\n\n";
74
75 std::cout << "For detailed help: z3ed --help <command>\n";
76 std::cout << "For all commands: z3ed --list-commands\n\n";
77}
78
80 std::vector<char*> positional;
81 bool show_help = false;
82 bool show_version = false;
83 bool list_commands = false;
84 std::optional<std::string> help_category;
85 std::optional<std::string> error;
86};
87
88ParsedGlobals ParseGlobalFlags(int argc, char* argv[]) {
89 ParsedGlobals result;
90 if (argc <= 0 || argv == nullptr) {
91 result.error = "Invalid argv provided";
92 return result;
93 }
94
95 result.positional.reserve(argc);
96 result.positional.push_back(argv[0]);
97
98 bool passthrough = false;
99 for (int i = 1; i < argc; ++i) {
100 char* current = argv[i];
101 std::string_view token(current);
102
103 if (!passthrough) {
104 if (token == "--") {
105 passthrough = true;
106 continue;
107 }
108
109 // Help flags
110 if (absl::StartsWith(token, "--help=")) {
111 std::string category(token.substr(7));
112 if (!category.empty()) {
113 result.help_category = category;
114 } else {
115 result.show_help = true;
116 }
117 continue;
118 }
119 if (token == "--help" || token == "-h") {
120 if (i + 1 < argc && argv[i + 1][0] != '-') {
121 result.help_category = std::string(argv[++i]);
122 } else {
123 result.show_help = true;
124 }
125 continue;
126 }
127
128 // Version flag
129 if (token == "--version" || token == "-v") {
130 result.show_version = true;
131 continue;
132 }
133
134 // List commands
135 if (token == "--list-commands" || token == "--list") {
136 result.list_commands = true;
137 continue;
138 }
139
140 // TUI mode
141 if (token == "--tui" || token == "--interactive") {
142 absl::SetFlag(&FLAGS_tui, true);
143 continue;
144 }
145
146 // Quiet mode
147 if (token == "--quiet" || token == "-q") {
148 absl::SetFlag(&FLAGS_quiet, true);
149 continue;
150 }
151 if (absl::StartsWith(token, "--quiet=")) {
152 std::string value(token.substr(8));
153 absl::SetFlag(&FLAGS_quiet, value == "true" || value == "1");
154 continue;
155 }
156
157 // ROM path
158 if (absl::StartsWith(token, "--rom=")) {
159 absl::SetFlag(&FLAGS_rom, std::string(token.substr(6)));
160 continue;
161 }
162 if (token == "--rom") {
163 if (i + 1 >= argc) {
164 result.error = "--rom flag requires a value";
165 return result;
166 }
167 absl::SetFlag(&FLAGS_rom, std::string(argv[++i]));
168 continue;
169 }
170
171 // AI provider flags
172 if (absl::StartsWith(token, "--ai_provider=") ||
173 absl::StartsWith(token, "--ai-provider=")) {
174 size_t eq_pos = token.find('=');
175 absl::SetFlag(&FLAGS_ai_provider, std::string(token.substr(eq_pos + 1)));
176 continue;
177 }
178 if (token == "--ai_provider" || token == "--ai-provider") {
179 if (i + 1 >= argc) {
180 result.error = "--ai-provider flag requires a value";
181 return result;
182 }
183 absl::SetFlag(&FLAGS_ai_provider, std::string(argv[++i]));
184 continue;
185 }
186
187 if (absl::StartsWith(token, "--ai_model=") ||
188 absl::StartsWith(token, "--ai-model=")) {
189 size_t eq_pos = token.find('=');
190 absl::SetFlag(&FLAGS_ai_model, std::string(token.substr(eq_pos + 1)));
191 continue;
192 }
193 if (token == "--ai_model" || token == "--ai-model") {
194 if (i + 1 >= argc) {
195 result.error = "--ai-model flag requires a value";
196 return result;
197 }
198 absl::SetFlag(&FLAGS_ai_model, std::string(argv[++i]));
199 continue;
200 }
201
202 if (absl::StartsWith(token, "--gemini_api_key=") ||
203 absl::StartsWith(token, "--gemini-api-key=")) {
204 size_t eq_pos = token.find('=');
205 absl::SetFlag(&FLAGS_gemini_api_key, std::string(token.substr(eq_pos + 1)));
206 continue;
207 }
208 if (token == "--gemini_api_key" || token == "--gemini-api-key") {
209 if (i + 1 >= argc) {
210 result.error = "--gemini-api-key flag requires a value";
211 return result;
212 }
213 absl::SetFlag(&FLAGS_gemini_api_key, std::string(argv[++i]));
214 continue;
215 }
216
217 if (absl::StartsWith(token, "--ollama_host=") ||
218 absl::StartsWith(token, "--ollama-host=")) {
219 size_t eq_pos = token.find('=');
220 absl::SetFlag(&FLAGS_ollama_host, std::string(token.substr(eq_pos + 1)));
221 continue;
222 }
223 if (token == "--ollama_host" || token == "--ollama-host") {
224 if (i + 1 >= argc) {
225 result.error = "--ollama-host flag requires a value";
226 return result;
227 }
228 absl::SetFlag(&FLAGS_ollama_host, std::string(argv[++i]));
229 continue;
230 }
231
232 if (absl::StartsWith(token, "--prompt_version=") ||
233 absl::StartsWith(token, "--prompt-version=")) {
234 size_t eq_pos = token.find('=');
235 absl::SetFlag(&FLAGS_prompt_version, std::string(token.substr(eq_pos + 1)));
236 continue;
237 }
238 if (token == "--prompt_version" || token == "--prompt-version") {
239 if (i + 1 >= argc) {
240 result.error = "--prompt-version flag requires a value";
241 return result;
242 }
243 absl::SetFlag(&FLAGS_prompt_version, std::string(argv[++i]));
244 continue;
245 }
246
247 if (absl::StartsWith(token, "--use_function_calling=") ||
248 absl::StartsWith(token, "--use-function-calling=")) {
249 size_t eq_pos = token.find('=');
250 std::string value(token.substr(eq_pos + 1));
251 absl::SetFlag(&FLAGS_use_function_calling, value == "true" || value == "1");
252 continue;
253 }
254 if (token == "--use_function_calling" || token == "--use-function-calling") {
255 if (i + 1 >= argc) {
256 result.error = "--use-function-calling flag requires a value";
257 return result;
258 }
259 std::string value(argv[++i]);
260 absl::SetFlag(&FLAGS_use_function_calling, value == "true" || value == "1");
261 continue;
262 }
263 }
264
265 result.positional.push_back(current);
266 }
267
268 return result;
269}
270
271} // namespace
272
273int main(int argc, char* argv[]) {
274 // Parse global flags
275 ParsedGlobals globals = ParseGlobalFlags(argc, argv);
276
277 if (globals.error.has_value()) {
278 std::cerr << "Error: " << *globals.error << "\n";
279 std::cerr << "Use --help for usage information.\n";
280 return EXIT_FAILURE;
281 }
282
283 // Handle version flag
284 if (globals.show_version) {
285 PrintVersion();
286 return EXIT_SUCCESS;
287 }
288
289 // Handle TUI mode
290 if (absl::GetFlag(FLAGS_tui)) {
291 // Load ROM if specified before launching TUI
292 std::string rom_path = absl::GetFlag(FLAGS_rom);
293 if (!rom_path.empty()) {
294 auto status = yaze::cli::app_context.rom.LoadFromFile(rom_path);
295 if (!status.ok()) {
296 std::cerr << "\n\033[1;31mError:\033[0m Failed to load ROM: "
297 << status.message() << "\n";
298 // Continue to TUI anyway, user can load ROM from there
299 }
300 }
302 return EXIT_SUCCESS;
303 }
304
305 // Create CLI instance
307
308 // Handle category-specific help
309 if (globals.help_category.has_value()) {
310 cli.PrintCategoryHelp(*globals.help_category);
311 return EXIT_SUCCESS;
312 }
313
314 // Handle list commands
315 if (globals.list_commands) {
317 return EXIT_SUCCESS;
318 }
319
320 // Handle general help or no arguments
321 if (globals.show_help || globals.positional.size() <= 1) {
323 return EXIT_SUCCESS;
324 }
325
326 // Run CLI commands
327 auto status = cli.Run(static_cast<int>(globals.positional.size()),
328 globals.positional.data());
329
330 if (!status.ok()) {
331 std::cerr << "\n\033[1;31mError:\033[0m " << status.message() << "\n";
332 std::cerr << "Use --help for usage information.\n";
333 return EXIT_FAILURE;
334 }
335
336 return EXIT_SUCCESS;
337}
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:289
absl::Status Run(int argc, char *argv[])
Definition cli.cc:24
void PrintCategoryHelp(const std::string &category) const
Definition cli.cc:164
void PrintCommandSummary() const
Definition cli.cc:168
ABSL_DECLARE_FLAG(std::string, rom)
ABSL_FLAG(bool, tui, false, "Launch interactive Text User Interface")
ParsedGlobals ParseGlobalFlags(int argc, char *argv[])
Definition cli_main.cc:88
void ShowMain()
Definition tui.cc:919
std::string GetColoredLogo()
std::optional< std::string > help_category
Definition cli_main.cc:84