yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_commands.cc
Go to the documentation of this file.
2
3#include "absl/strings/str_format.h"
5#include "cli/util/hex_util.h"
9
10namespace yaze {
11namespace cli {
12namespace handlers {
13
15
17 Rom* rom, const resources::ArgumentParser& parser,
18 resources::OutputFormatter& formatter) {
19 auto tile_id_str = parser.GetString("tile").value();
20
21 int tile_id;
22 if (!ParseHexString(tile_id_str, &tile_id)) {
23 return absl::InvalidArgumentError("Invalid tile ID format. Must be hex.");
24 }
25
26 // Load the Overworld from ROM
27 zelda3::Overworld overworld(rom);
28 auto ow_status = overworld.Load(rom);
29 if (!ow_status.ok()) {
30 return ow_status;
31 }
32
33 // Call the helper function to find tile matches
34 auto matches_or = overworld::FindTileMatches(overworld, tile_id);
35 if (!matches_or.ok()) {
36 return matches_or.status();
37 }
38 const auto& matches = matches_or.value();
39
40 // Format the output
41 formatter.BeginObject("Overworld Tile Search");
42 formatter.AddField("tile_id", absl::StrFormat("0x%03X", tile_id));
43 formatter.AddField("matches_found", static_cast<int>(matches.size()));
44
45 formatter.BeginArray("matches");
46 for (const auto& match : matches) {
47 formatter.BeginObject();
48 formatter.AddField("map_id", absl::StrFormat("0x%02X", match.map_id));
49 formatter.AddField("world", overworld::WorldName(match.world));
50 formatter.AddField("local_x", match.local_x);
51 formatter.AddField("local_y", match.local_y);
52 formatter.AddField("global_x", match.global_x);
53 formatter.AddField("global_y", match.global_y);
54 formatter.EndObject();
55 }
56 formatter.EndArray();
57 formatter.EndObject();
58
59 return absl::OkStatus();
60}
61
63 Rom* rom, const resources::ArgumentParser& parser,
64 resources::OutputFormatter& formatter) {
65 auto screen_id_str = parser.GetString("screen").value();
66
67 int screen_id;
68 if (!ParseHexString(screen_id_str, &screen_id)) {
69 return absl::InvalidArgumentError("Invalid screen ID format. Must be hex.");
70 }
71
72 // Load the Overworld from ROM
73 zelda3::Overworld overworld(rom);
74 auto ow_status = overworld.Load(rom);
75 if (!ow_status.ok()) {
76 return ow_status;
77 }
78
79 // Call the helper function to build the map summary
80 auto summary_or = overworld::BuildMapSummary(overworld, screen_id);
81 if (!summary_or.ok()) {
82 return summary_or.status();
83 }
84 const auto& summary = summary_or.value();
85
86 // Format the output using OutputFormatter
87 formatter.AddField("screen_id", absl::StrFormat("0x%02X", summary.map_id));
88 formatter.AddField("world", overworld::WorldName(summary.world));
89
90 formatter.BeginObject("grid");
91 formatter.AddField("x", summary.map_x);
92 formatter.AddField("y", summary.map_y);
93 formatter.AddField("local_index", summary.local_index);
94 formatter.EndObject();
95
96 formatter.BeginObject("size");
97 formatter.AddField("label", summary.area_size);
98 formatter.AddField("is_large", summary.is_large_map);
99 formatter.AddField("parent", absl::StrFormat("0x%02X", summary.parent_map));
100 formatter.AddField("quadrant", summary.large_quadrant);
101 formatter.EndObject();
102
103 formatter.AddField("message_id",
104 absl::StrFormat("0x%04X", summary.message_id));
105 formatter.AddField("area_graphics",
106 absl::StrFormat("0x%02X", summary.area_graphics));
107 formatter.AddField("area_palette",
108 absl::StrFormat("0x%02X", summary.area_palette));
109 formatter.AddField("main_palette",
110 absl::StrFormat("0x%02X", summary.main_palette));
111 formatter.AddField("animated_gfx",
112 absl::StrFormat("0x%02X", summary.animated_gfx));
113 formatter.AddField("subscreen_overlay",
114 absl::StrFormat("0x%04X", summary.subscreen_overlay));
115 formatter.AddField("area_specific_bg_color",
116 absl::StrFormat("0x%04X", summary.area_specific_bg_color));
117
118 // Entrances
119 formatter.BeginArray("entrances");
120 auto entrances_or = zelda3::LoadEntrances(rom);
121 if (entrances_or.ok()) {
122 for (const auto& entrance : entrances_or.value()) {
123 if (entrance.map_id_ == screen_id) {
124 formatter.BeginObject();
125 formatter.AddField("entrance_id", entrance.entrance_id_);
126 formatter.AddField("x", entrance.x_);
127 formatter.AddField("y", entrance.y_);
128 formatter.AddField("is_hole", false);
129 formatter.EndObject();
130 }
131 }
132 }
133 // Holes (as entrances)
134 auto holes_or = zelda3::LoadHoles(rom);
135 if (holes_or.ok()) {
136 for (const auto& hole : holes_or.value()) {
137 if (hole.map_id_ == screen_id) {
138 formatter.BeginObject();
139 formatter.AddField("entrance_id", hole.entrance_id_);
140 formatter.AddField("x", hole.x_);
141 formatter.AddField("y", hole.y_);
142 formatter.AddField("is_hole", true);
143 formatter.EndObject();
144 }
145 }
146 }
147 formatter.EndArray();
148
149 // Exits
150 formatter.BeginArray("exits");
151 auto exits_or = zelda3::LoadExits(rom);
152 if (exits_or.ok()) {
153 for (const auto& exit : exits_or.value()) {
154 if (exit.map_id_ == screen_id) {
155 formatter.BeginObject();
156 formatter.AddField("room_id", exit.room_id_);
157 formatter.AddField("x", exit.x_);
158 formatter.AddField("y", exit.y_);
159 formatter.EndObject();
160 }
161 }
162 }
163 formatter.EndArray();
164
165 formatter.BeginArray("sprite_graphics");
166 for (uint8_t gfx : summary.sprite_graphics) {
167 formatter.AddArrayItem(absl::StrFormat("0x%02X", gfx));
168 }
169 formatter.EndArray();
170
171 formatter.BeginArray("sprite_palettes");
172 for (uint8_t pal : summary.sprite_palettes) {
173 formatter.AddArrayItem(absl::StrFormat("0x%02X", pal));
174 }
175 formatter.EndArray();
176
177 formatter.BeginArray("area_music");
178 for (uint8_t music : summary.area_music) {
179 formatter.AddArrayItem(absl::StrFormat("0x%02X", music));
180 }
181 formatter.EndArray();
182
183 formatter.BeginArray("static_graphics");
184 for (uint8_t sgfx : summary.static_graphics) {
185 formatter.AddArrayItem(absl::StrFormat("0x%02X", sgfx));
186 }
187 formatter.EndArray();
188
189 formatter.BeginObject("overlay");
190 formatter.AddField("enabled", summary.has_overlay);
191 formatter.AddField("id", absl::StrFormat("0x%04X", summary.overlay_id));
192 formatter.EndObject();
193
194 formatter.EndObject();
195
196 return absl::OkStatus();
197}
198
200 Rom* rom, const resources::ArgumentParser& parser,
201 resources::OutputFormatter& formatter) {
202 auto screen_id_str = parser.GetString("screen").value_or("all");
203
204 // Load the Overworld from ROM
205 zelda3::Overworld overworld(rom);
206 auto ow_status = overworld.Load(rom);
207 if (!ow_status.ok()) {
208 return ow_status;
209 }
210
211 // Build the query
213 if (screen_id_str != "all") {
214 int map_id;
215 if (!ParseHexString(screen_id_str, &map_id)) {
216 return absl::InvalidArgumentError(
217 "Invalid screen ID format. Must be hex.");
218 }
219 query.map_id = map_id;
220 }
221
222 // Call the helper function to collect warp entries
223 auto warps_or = overworld::CollectWarpEntries(overworld, query);
224 if (!warps_or.ok()) {
225 return warps_or.status();
226 }
227 const auto& warps = warps_or.value();
228
229 // Format the output
230 formatter.BeginObject("Overworld Warps");
231 formatter.AddField("screen_filter", screen_id_str);
232 formatter.AddField("total_warps", static_cast<int>(warps.size()));
233
234 formatter.BeginArray("warps");
235 for (const auto& warp : warps) {
236 formatter.BeginObject();
237 formatter.AddField("type", overworld::WarpTypeName(warp.type));
238 formatter.AddField("map_id", absl::StrFormat("0x%02X", warp.map_id));
239 formatter.AddField("world", overworld::WorldName(warp.world));
240 formatter.AddField("position",
241 absl::StrFormat("(%d,%d)", warp.pixel_x, warp.pixel_y));
242 formatter.AddField("map_pos", absl::StrFormat("0x%04X", warp.map_pos));
243
244 if (warp.entrance_id.has_value()) {
245 formatter.AddField("entrance_id",
246 absl::StrFormat("0x%02X", warp.entrance_id.value()));
247 }
248 if (warp.entrance_name.has_value()) {
249 formatter.AddField("entrance_name", warp.entrance_name.value());
250 }
251 if (warp.room_id.has_value()) {
252 formatter.AddField("room_id",
253 absl::StrFormat("0x%04X", warp.room_id.value()));
254 }
255
256 formatter.AddField("deleted", warp.deleted);
257 formatter.AddField("is_hole", warp.is_hole);
258 formatter.EndObject();
259 }
260 formatter.EndArray();
261 formatter.EndObject();
262
263 return absl::OkStatus();
264}
265
267 Rom* rom, const resources::ArgumentParser& parser,
268 resources::OutputFormatter& formatter) {
269 auto screen_id_str = parser.GetString("screen").value_or("all");
270
271 // Load the Overworld from ROM
272 zelda3::Overworld overworld(rom);
273 auto ow_status = overworld.Load(rom);
274 if (!ow_status.ok()) {
275 return ow_status;
276 }
277
278 // Build the query
280 if (screen_id_str != "all") {
281 int map_id;
282 if (!ParseHexString(screen_id_str, &map_id)) {
283 return absl::InvalidArgumentError(
284 "Invalid screen ID format. Must be hex.");
285 }
286 query.map_id = map_id;
287 }
288
289 // Call the helper function to collect sprites
290 auto sprites_or = overworld::CollectOverworldSprites(overworld, query);
291 if (!sprites_or.ok()) {
292 return sprites_or.status();
293 }
294 const auto& sprites = sprites_or.value();
295
296 // Format the output
297 formatter.BeginObject("Overworld Sprites");
298 formatter.AddField("screen_filter", screen_id_str);
299 formatter.AddField("total_sprites", static_cast<int>(sprites.size()));
300
301 formatter.BeginArray("sprites");
302 for (const auto& sprite : sprites) {
303 formatter.BeginObject();
304 formatter.AddField("sprite_id",
305 absl::StrFormat("0x%02X", sprite.sprite_id));
306 formatter.AddField("map_id", absl::StrFormat("0x%02X", sprite.map_id));
307 formatter.AddField("world", overworld::WorldName(sprite.world));
308 formatter.AddField("position",
309 absl::StrFormat("(%d,%d)", sprite.x, sprite.y));
310
311 if (sprite.sprite_name.has_value()) {
312 formatter.AddField("name", sprite.sprite_name.value());
313 }
314
315 formatter.EndObject();
316 }
317 formatter.EndArray();
318 formatter.EndObject();
319
320 return absl::OkStatus();
321}
322
324 Rom* rom, const resources::ArgumentParser& parser,
325 resources::OutputFormatter& formatter) {
326 auto screen_id_str = parser.GetString("screen").value_or("all");
327
328 // Load the Overworld from ROM
329 zelda3::Overworld overworld(rom);
330 auto ow_status = overworld.Load(rom);
331 if (!ow_status.ok()) {
332 return ow_status;
333 }
334
335 // Optional screen filter
336 std::optional<int> map_filter;
337 if (screen_id_str != "all") {
338 int map_id;
339 if (!ParseHexString(screen_id_str, &map_id)) {
340 return absl::InvalidArgumentError(
341 "Invalid screen ID format. Must be hex.");
342 }
343 map_filter = map_id;
344 }
345
346 auto maps = overworld.overworld_maps();
347 ASSIGN_OR_RETURN(auto items, zelda3::LoadItems(rom, maps));
348
349 const auto& item_names = zelda3::Zelda3Labels::GetItemNames();
350
351 formatter.BeginObject("Overworld Items");
352 formatter.AddField("screen_filter", screen_id_str);
353
354 formatter.BeginArray("items");
355 int total_items = 0;
356 for (const auto& item : items) {
357 if (map_filter.has_value() &&
358 static_cast<int>(item.room_map_id_) != map_filter.value()) {
359 continue;
360 }
361
362 std::string world_name = "Unknown";
363 auto world_or = overworld::InferWorldFromMapId(item.room_map_id_);
364 if (world_or.ok()) {
365 world_name = overworld::WorldName(world_or.value());
366 }
367
368 formatter.BeginObject();
369 formatter.AddField("item_id", absl::StrFormat("0x%02X", item.id_));
370
371 if (item.id_ < item_names.size()) {
372 formatter.AddField("item_name", item_names[item.id_]);
373 }
374
375 formatter.AddField("map_id", absl::StrFormat("0x%02X", item.room_map_id_));
376 formatter.AddField("world", world_name);
377 formatter.AddField("tile_pos",
378 absl::StrFormat("(%d,%d)", item.game_x_, item.game_y_));
379 formatter.AddField("pixel_pos",
380 absl::StrFormat("(%d,%d)", item.x_, item.y_));
381 formatter.EndObject();
382
383 total_items++;
384 }
385 formatter.EndArray();
386 formatter.AddField("total_items", total_items);
387 formatter.EndObject();
388
389 return absl::OkStatus();
390}
391
393 Rom* rom, const resources::ArgumentParser& parser,
394 resources::OutputFormatter& formatter) {
395 auto entrance_id_str = parser.GetString("entrance").value();
396
397 int entrance_id;
398 if (!ParseHexString(entrance_id_str, &entrance_id)) {
399 return absl::InvalidArgumentError(
400 "Invalid entrance ID format. Must be hex.");
401 }
402
403 // Load the Overworld from ROM
404 zelda3::Overworld overworld(rom);
405 auto ow_status = overworld.Load(rom);
406 if (!ow_status.ok()) {
407 return ow_status;
408 }
409
410 // Call the helper function to get entrance details
411 auto details_or = overworld::GetEntranceDetails(overworld, entrance_id);
412 if (!details_or.ok()) {
413 return details_or.status();
414 }
415 const auto& details = details_or.value();
416
417 // Format the output
418 formatter.BeginObject("Overworld Entrance");
419 formatter.AddField("entrance_id",
420 absl::StrFormat("0x%02X", details.entrance_id));
421 formatter.AddField("map_id", absl::StrFormat("0x%02X", details.map_id));
422 formatter.AddField("world", overworld::WorldName(details.world));
423 formatter.AddField("position",
424 absl::StrFormat("(%d,%d)", details.x, details.y));
425 formatter.AddField("area_position", absl::StrFormat("(%d,%d)", details.area_x,
426 details.area_y));
427 formatter.AddField("map_pos", absl::StrFormat("0x%04X", details.map_pos));
428 formatter.AddField("is_hole", details.is_hole);
429
430 if (details.entrance_name.has_value()) {
431 formatter.AddField("name", details.entrance_name.value());
432 }
433
434 formatter.EndObject();
435
436 return absl::OkStatus();
437}
438
440 Rom* rom, const resources::ArgumentParser& parser,
441 resources::OutputFormatter& formatter) {
442 auto screen_id_str = parser.GetString("screen").value_or("all");
443
444 // Load the Overworld from ROM
445 zelda3::Overworld overworld(rom);
446 auto ow_status = overworld.Load(rom);
447 if (!ow_status.ok()) {
448 return ow_status;
449 }
450
451 // TODO: Implement comprehensive tile statistics
452 // The AnalyzeTileUsage helper requires a specific tile_id,
453 // so we need a different approach to gather overall tile statistics.
454 // This could involve:
455 // 1. Iterating through all tiles in the overworld maps
456 // 2. Building a frequency map of tile usage
457 // 3. Computing statistics like unique tiles, most common tiles, etc.
458
459 formatter.BeginObject("Overworld Tile Statistics");
460 formatter.AddField("screen_filter", screen_id_str);
461 formatter.AddField("status", "partial_implementation");
462 formatter.AddField("message",
463 "Comprehensive tile statistics not yet implemented. "
464 "Use overworld-find-tile for specific tile analysis.");
465 formatter.AddField("total_tiles", 0);
466 formatter.AddField("unique_tiles", 0);
467
468 formatter.BeginArray("tile_counts");
469 formatter.EndArray();
470 formatter.EndObject();
471
472 return absl::OkStatus();
473}
474
475} // namespace handlers
476} // namespace cli
477} // 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:28
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
Utility for parsing common CLI argument patterns.
std::optional< std::string > GetString(const std::string &name) const
Parse a named argument (e.g., –format=json or –format json)
Utility for consistent output formatting across commands.
void BeginArray(const std::string &key)
Begin an array.
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.
void AddField(const std::string &key, const std::string &value)
Add a key-value pair.
Represents the full Overworld data, light and dark world.
Definition overworld.h:261
absl::Status Load(Rom *rom)
Load all overworld data from ROM.
Definition overworld.cc:36
auto overworld_maps() const
Definition overworld.h:527
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
absl::StatusOr< int > InferWorldFromMapId(int map_id)
absl::StatusOr< MapSummary > BuildMapSummary(zelda3::Overworld &overworld, int map_id)
absl::StatusOr< std::vector< WarpEntry > > CollectWarpEntries(const zelda3::Overworld &overworld, const WarpQuery &query)
absl::StatusOr< EntranceDetails > GetEntranceDetails(const zelda3::Overworld &overworld, uint8_t entrance_id)
absl::StatusOr< std::vector< OverworldSprite > > CollectOverworldSprites(const zelda3::Overworld &overworld, const SpriteQuery &query)
absl::StatusOr< std::vector< TileMatch > > FindTileMatches(zelda3::Overworld &overworld, uint16_t tile_id, const TileSearchOptions &options)
std::string WarpTypeName(WarpType type)
std::string WorldName(int world)
bool ParseHexString(absl::string_view str, int *out)
Definition hex_util.h:17
absl::StatusOr< std::vector< OverworldEntrance > > LoadEntrances(Rom *rom)
absl::StatusOr< std::vector< OverworldItem > > LoadItems(Rom *rom, std::vector< OverworldMap > &overworld_maps)
absl::StatusOr< std::vector< OverworldExit > > LoadExits(Rom *rom)
absl::StatusOr< std::vector< OverworldEntrance > > LoadHoles(Rom *rom)
static const std::vector< std::string > & GetItemNames()