3#include "absl/strings/str_format.h"
21 return absl::InvalidArgumentError(
22 absl::StrFormat(
"Map ID out of range: 0x%02X", map_id));
24 return absl::OkStatus();
33 }
else if (map_id < 0x80) {
40 return absl::OkStatus();
44 if (x < 0 || x >= 64 || y < 0 || y >= 64) {
45 return absl::InvalidArgumentError(
46 absl::StrFormat(
"Tile coordinates out of range: (%d,%d)", x, y));
48 return absl::OkStatus();
59 if (!ParseHexString(parser.
GetString(
"map").value(), &map_id) ||
60 !ParseHexString(parser.
GetString(
"x").value(), &x) ||
61 !ParseHexString(parser.
GetString(
"y").value(), &y)) {
62 return absl::InvalidArgumentError(
"Invalid numeric argument for map/x/y.");
71 const uint16_t tile = overworld.
GetTile(x, y);
78 return absl::OkStatus();
88 if (!ParseHexString(parser.
GetString(
"map").value(), &map_id) ||
89 !ParseHexString(parser.
GetString(
"x").value(), &x) ||
90 !ParseHexString(parser.
GetString(
"y").value(), &y) ||
91 !ParseHexString(parser.
GetString(
"tile").value(), &tile_value)) {
92 return absl::InvalidArgumentError(
93 "Invalid numeric argument for map/x/y/tile.");
97 if (tile_value < 0 || tile_value > 0xFFFF) {
98 return absl::InvalidArgumentError(
99 absl::StrFormat(
"Tile ID out of range: 0x%X", tile_value));
106 const uint16_t before = overworld.
GetTile(x, y);
107 overworld.
SetTile(x, y,
static_cast<uint16_t
>(tile_value));
108 const uint16_t after = overworld.
GetTile(x, y);
120 if (parser.
HasFlag(
"mock-rom")) {
121 formatter.
AddField(
"save_status",
"mock-rom-skipped");
124 save_settings.
backup =
true;
126 formatter.
AddField(
"save_status",
"saved");
129 return absl::OkStatus();
135 auto tile_id_str = parser.
GetString(
"tile").value();
138 if (!ParseHexString(tile_id_str, &tile_id)) {
139 return absl::InvalidArgumentError(
"Invalid tile ID format. Must be hex.");
144 auto ow_status = overworld.
Load(rom);
145 if (!ow_status.ok()) {
151 if (!matches_or.ok()) {
152 return matches_or.status();
154 const auto& matches = matches_or.value();
158 formatter.
AddField(
"tile_id", absl::StrFormat(
"0x%03X", tile_id));
159 formatter.
AddField(
"matches_found",
static_cast<int>(matches.size()));
162 for (
const auto& match : matches) {
164 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", match.map_id));
166 formatter.
AddField(
"local_x", match.local_x);
167 formatter.
AddField(
"local_y", match.local_y);
168 formatter.
AddField(
"global_x", match.global_x);
169 formatter.
AddField(
"global_y", match.global_y);
175 return absl::OkStatus();
181 auto screen_id_str = parser.
GetString(
"screen").value();
184 if (!ParseHexString(screen_id_str, &screen_id)) {
185 return absl::InvalidArgumentError(
"Invalid screen ID format. Must be hex.");
190 auto ow_status = overworld.
Load(rom);
191 if (!ow_status.ok()) {
197 if (!summary_or.ok()) {
198 return summary_or.status();
200 const auto& summary = summary_or.value();
203 formatter.
AddField(
"screen_id", absl::StrFormat(
"0x%02X", summary.map_id));
207 formatter.
AddField(
"x", summary.map_x);
208 formatter.
AddField(
"y", summary.map_y);
209 formatter.
AddField(
"local_index", summary.local_index);
213 formatter.
AddField(
"label", summary.area_size);
214 formatter.
AddField(
"is_large", summary.is_large_map);
215 formatter.
AddField(
"parent", absl::StrFormat(
"0x%02X", summary.parent_map));
216 formatter.
AddField(
"quadrant", summary.large_quadrant);
220 absl::StrFormat(
"0x%04X", summary.message_id));
222 absl::StrFormat(
"0x%02X", summary.area_graphics));
224 absl::StrFormat(
"0x%02X", summary.area_palette));
226 absl::StrFormat(
"0x%02X", summary.main_palette));
228 absl::StrFormat(
"0x%02X", summary.animated_gfx));
229 formatter.
AddField(
"subscreen_overlay",
230 absl::StrFormat(
"0x%04X", summary.subscreen_overlay));
231 formatter.
AddField(
"area_specific_bg_color",
232 absl::StrFormat(
"0x%04X", summary.area_specific_bg_color));
237 if (entrances_or.ok()) {
238 for (
const auto& entrance : entrances_or.value()) {
239 if (entrance.map_id_ == screen_id) {
241 formatter.
AddField(
"entrance_id", entrance.entrance_id_);
242 formatter.
AddField(
"x", entrance.x_);
243 formatter.
AddField(
"y", entrance.y_);
244 formatter.
AddField(
"is_hole",
false);
252 for (
const auto& hole : holes_or.value()) {
253 if (hole.map_id_ == screen_id) {
255 formatter.
AddField(
"entrance_id", hole.entrance_id_);
258 formatter.
AddField(
"is_hole",
true);
269 for (
const auto& exit : exits_or.value()) {
270 if (exit.map_id_ == screen_id) {
272 formatter.
AddField(
"room_id", exit.room_id_);
282 for (uint8_t gfx : summary.sprite_graphics) {
288 for (uint8_t pal : summary.sprite_palettes) {
294 for (uint8_t music : summary.area_music) {
295 formatter.
AddArrayItem(absl::StrFormat(
"0x%02X", music));
300 for (uint8_t sgfx : summary.static_graphics) {
301 formatter.
AddArrayItem(absl::StrFormat(
"0x%02X", sgfx));
306 formatter.
AddField(
"enabled", summary.has_overlay);
307 formatter.
AddField(
"id", absl::StrFormat(
"0x%04X", summary.overlay_id));
312 return absl::OkStatus();
318 auto screen_id_str = parser.
GetString(
"screen").value_or(
"all");
322 auto ow_status = overworld.
Load(rom);
323 if (!ow_status.ok()) {
329 if (screen_id_str !=
"all") {
331 if (!ParseHexString(screen_id_str, &map_id)) {
332 return absl::InvalidArgumentError(
333 "Invalid screen ID format. Must be hex.");
340 if (!warps_or.ok()) {
341 return warps_or.status();
343 const auto& warps = warps_or.value();
347 formatter.
AddField(
"screen_filter", screen_id_str);
348 formatter.
AddField(
"total_warps",
static_cast<int>(warps.size()));
351 for (
const auto& warp : warps) {
354 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", warp.map_id));
357 absl::StrFormat(
"(%d,%d)", warp.pixel_x, warp.pixel_y));
358 formatter.
AddField(
"map_pos", absl::StrFormat(
"0x%04X", warp.map_pos));
360 if (warp.entrance_id.has_value()) {
362 absl::StrFormat(
"0x%02X", warp.entrance_id.value()));
364 if (warp.entrance_name.has_value()) {
365 formatter.
AddField(
"entrance_name", warp.entrance_name.value());
367 if (warp.room_id.has_value()) {
369 absl::StrFormat(
"0x%04X", warp.room_id.value()));
372 formatter.
AddField(
"deleted", warp.deleted);
373 formatter.
AddField(
"is_hole", warp.is_hole);
379 return absl::OkStatus();
385 auto screen_id_str = parser.
GetString(
"screen").value_or(
"all");
389 auto ow_status = overworld.
Load(rom);
390 if (!ow_status.ok()) {
396 if (screen_id_str !=
"all") {
398 if (!ParseHexString(screen_id_str, &map_id)) {
399 return absl::InvalidArgumentError(
400 "Invalid screen ID format. Must be hex.");
407 if (!sprites_or.ok()) {
408 return sprites_or.status();
410 const auto& sprites = sprites_or.value();
414 formatter.
AddField(
"screen_filter", screen_id_str);
415 formatter.
AddField(
"total_sprites",
static_cast<int>(sprites.size()));
418 for (
const auto& sprite : sprites) {
421 absl::StrFormat(
"0x%02X", sprite.sprite_id));
422 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", sprite.map_id));
425 absl::StrFormat(
"(%d,%d)", sprite.x, sprite.y));
427 if (sprite.sprite_name.has_value()) {
428 formatter.
AddField(
"name", sprite.sprite_name.value());
436 return absl::OkStatus();
442 auto screen_id_str = parser.
GetString(
"screen").value_or(
"all");
446 auto ow_status = overworld.
Load(rom);
447 if (!ow_status.ok()) {
452 std::optional<int> map_filter;
453 if (screen_id_str !=
"all") {
455 if (!ParseHexString(screen_id_str, &map_id)) {
456 return absl::InvalidArgumentError(
457 "Invalid screen ID format. Must be hex.");
468 formatter.
AddField(
"screen_filter", screen_id_str);
472 for (
const auto& item : items) {
473 if (map_filter.has_value() &&
474 static_cast<int>(item.room_map_id_) != map_filter.value()) {
478 std::string world_name =
"Unknown";
485 formatter.
AddField(
"item_id", absl::StrFormat(
"0x%02X", item.id_));
487 if (item.id_ < item_names.size()) {
488 formatter.
AddField(
"item_name", item_names[item.id_]);
491 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", item.room_map_id_));
492 formatter.
AddField(
"world", world_name);
494 absl::StrFormat(
"(%d,%d)", item.game_x_, item.game_y_));
496 absl::StrFormat(
"(%d,%d)", item.x_, item.y_));
502 formatter.
AddField(
"total_items", total_items);
505 return absl::OkStatus();
511 auto entrance_id_str = parser.
GetString(
"entrance").value();
514 if (!ParseHexString(entrance_id_str, &entrance_id)) {
515 return absl::InvalidArgumentError(
516 "Invalid entrance ID format. Must be hex.");
521 auto ow_status = overworld.
Load(rom);
522 if (!ow_status.ok()) {
528 if (!details_or.ok()) {
529 return details_or.status();
531 const auto& details = details_or.value();
536 absl::StrFormat(
"0x%02X", details.entrance_id));
537 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", details.map_id));
540 absl::StrFormat(
"(%d,%d)", details.x, details.y));
541 formatter.
AddField(
"area_position", absl::StrFormat(
"(%d,%d)", details.area_x,
543 formatter.
AddField(
"map_pos", absl::StrFormat(
"0x%04X", details.map_pos));
544 formatter.
AddField(
"is_hole", details.is_hole);
546 if (details.entrance_name.has_value()) {
547 formatter.
AddField(
"name", details.entrance_name.value());
552 return absl::OkStatus();
558 auto screen_id_str = parser.
GetString(
"screen").value_or(
"all");
562 auto ow_status = overworld.
Load(rom);
563 if (!ow_status.ok()) {
575 formatter.
BeginObject(
"Overworld Tile Statistics");
576 formatter.
AddField(
"screen_filter", screen_id_str);
577 formatter.
AddField(
"status",
"partial_implementation");
579 "Comprehensive tile statistics not yet implemented. "
580 "Use overworld-find-tile for specific tile analysis.");
581 formatter.
AddField(
"total_tiles", 0);
582 formatter.
AddField(
"unique_tiles", 0);
588 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status SaveToFile(const SaveSettings &settings)
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.
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)
bool HasFlag(const std::string &name) const
Check if a flag is present.
Represents the full Overworld data, light and dark world.
void set_current_world(int world)
absl::Status Load(Rom *rom)
Load all overworld data from ROM.
absl::Status SaveMap32Tiles()
Save tile32 definitions to ROM.
absl::Status SaveMap16Tiles()
Save tile16 definitions to ROM.
const std::vector< OverworldMap > & overworld_maps() const
void set_current_map(int i)
uint16_t GetTile(int x, int y) const
absl::Status SaveOverworldMaps()
Save compressed map tile data to ROM.
void SetTile(int x, int y, uint16_t tile_id)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
absl::Status SetOverworldContextForMap(int map_id, zelda3::Overworld *overworld)
absl::Status ValidateTileCoordinates(int x, int y)
absl::Status ValidateMapId(int map_id)
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)
absl::StatusOr< std::vector< OverworldEntrance > > LoadEntrances(Rom *rom)
absl::StatusOr< std::vector< OverworldItem > > LoadItems(Rom *rom, std::vector< OverworldMap > &overworld_maps)
constexpr int kNumOverworldMaps
absl::StatusOr< std::vector< OverworldExit > > LoadExits(Rom *rom)
absl::StatusOr< std::vector< OverworldEntrance > > LoadHoles(Rom *rom)
#define RETURN_IF_ERROR(expr)
std::optional< int > map_id
std::optional< int > map_id
static const std::vector< std::string > & GetItemNames()